Lou Montana/Sandbox – User
m (Getting started; recommended skills to do the thing and some minor changes) |
Lou Montana (talk | contribs) m (Some page presentation / copy-paste introduction from Code Optimisation) |
||
Line 2: | Line 2: | ||
[[Category: Sandbox]] | [[Category: Sandbox]] | ||
{{Informative | Future [[Code Best Practices]] page}} | {{Informative | Future [[Code Best Practices]] page}} | ||
{{Important | | {{Important | This is at the moment '''only a list of topics''' and absolutely not the final render. Many single entries will result in a chapter.}} | ||
---- | ---- | ||
[[:Category: Scripting Topics]] | [[:Category: Scripting Topics]] | ||
==Getting | == Getting started == | ||
=== Prerequisites === | |||
* An Arma game | |||
* Passion | |||
* Some English knowledge, helped if needed by a translator tool like [https://www.google.com/translate Google Translate] or [https://www.deepl.com/ DeepL] | |||
* Access to this wiki is a plus (the Swiss flag is, too) | |||
* Tutorial/example article (YouTube tutorials, ) | |||
* Google-Fu (a.k.a search engine skills) | |||
* Advanced text editor (Such as Notepad++ or Visual Studio Code) | |||
== Rules == | |||
In the domain of development, any rule is a rule of thumb. If a rule states for example that it is ''better'' that a line of code doesn't go over 80 characters, it doesn't mean that any line '''''must not''''' go over 80 characters; sometimes, the situation needs it. If you have a good structure, '''do not''' change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement. | |||
With that being said, here are the three basic rules to get yourself in the clear: | |||
# [[#Make it work|Make it work]] | |||
# [[#Make it readable|Make it readable]] | |||
# [[#Optimise then|Optimise then]] | |||
=== Make it work === | |||
{{quote|Premature optimization is the root of all evil.|[https://en.wikipedia.org/wiki/Donald_Knuth Donald Knuth]}} | |||
Your first goal when coding is to make your code do what you want it does. A good way to reach this objective is to read and getting inspired by other people's code. If you understand it by reading it once, it is probably a good source of inspiration. | |||
* When starting from scratch if you know what you want but miss the specific steps to get to your point, it is a good practice to write down in your native language what you want to do. E.g ''Get [[allUnits|all the units]] [[distance|near]] [[locationPosition|the city]], and [[forEach|for each]] [[side|west]] soldier in them, [[setDamage|add 30% damage]]''. | |||
* Use [[Arma_3_Startup_Parameters#Developer_Options|-showScriptErrors]] startup parameter and '''make sure your code doesn't throw errors.''' Not only will your code run slower but it may also ''not work at all''. Be sure to read the error, isolate the issue and sort it out [[:Category:Scripting Commands|thanks to this Wiki]]. | |||
* Read your [[Crash_Files|Arma RPT]] (report) to read more details about the error that happened in your code. | |||
=== Make it readable === | |||
Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain: | |||
* While [[SQF syntax|SQF]] ''is'' (non-noticeably) impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like '''_u''' instead of '''_uniform''' should not be present. | |||
* ''One-lining'' (putting everything in one statement) memory improvement is most of the time not worth the headache it gives when trying to read it. Don't overuse it. | |||
* Indentation is important for the human mind, and space is too. Space is free, use it. | |||
* Same goes for line return; it helps to see a code block wrapping multiple common instructions instead of having to guess where it starts and stops. | |||
* Do you see the same code multiple times, only with different parameters? Now is the time to write a function! | |||
* If you have a lot of [[if]]..[[else]], you may want to look at a [[switch]] condition, or again break your code in smaller functions. | |||
* Is your function code far too long? Break it in understandable-sized bites for your own sanity. | |||
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names. | |||
{{Informative| '''_i''' is an accepted variable standard for a [[for]]..[[do]] iteration}} | |||
See the following code: | |||
_w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east}); | |||
The same example is far more readable with proper spacing, good variable names and intermediate results: | |||
_weaponNames = []; | |||
_allUnitsAliveAndDead = allUnits + allDeadMen; | |||
_allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east }; | |||
{ _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead; | |||
<!-- | |||
EDITOR'S NOTE: ^ code examples are not linking commands on purpose! This allows for a fair comparison of both syntaxes' readability. | |||
--> | |||
==== Constants ==== | |||
Using a hard-coded constant more than once? Use preprocessor directives rather than storing it in memory or cluttering your code with numbers. Such as: | |||
a = _x + 1.053; | |||
b = _y + 1.053; | |||
And | |||
_buffer = 1.053; | |||
a = _x + _buffer; | |||
b = _y + _buffer; | |||
Becomes | |||
<span style="color: purple; font-weight: bold;">#define BUFFER 1.053</span> {{codecomment|// note: no semicolon}} | |||
_a = _x + BUFFER; | |||
_b = _y + BUFFER; | |||
{{Informative | Using the '''#define''' macro only works within the current [[SQF]] ''file''. Such definition will not propagate anywhere else.}} | |||
'''Global''' "constants" can be defined ''via'' a [[Description.ext]] declaration, though, and accessed using [[getMissionConfigValue]] command: | |||
Declaration in [[Description.ext]]: | |||
<syntaxhighlight lang="cpp"> | |||
var1 = 123; | |||
var2 = "123"; | |||
var3[] = {1,2,3}; | |||
rand = __EVAL(random 999); | |||
</syntaxhighlight> | |||
Usage in code: | |||
[[hint]] [[str]] [[getMissionConfigValue]] "var1"; // 123 {{codecomment|// 0.0007 ms}} | |||
[[hint]] [[str]] [[getMissionConfigValue]] "var2"; // "123" {{codecomment|// 0.0008 ms}} | |||
[[hint]] [[str]] [[getMissionConfigValue]] "var3"; // [1,2,3] {{codecomment|// 0.0017 ms}} | |||
[[hint]] [[str]] [[getMissionConfigValue]] "rand"; // constant random, for example 935.038 {{codecomment|// 0.0007 ms}} | |||
The [[getMissionConfigValue]] command searching [[Description.ext]] from top to bottom, | |||
it is better for a matter of performance to put all your definitions at the top of the file. | |||
=== Optimise then === | |||
Once you know what is what, you can understand your code better. | |||
* Use private variables instead of global variables (preceded with an underscore) as much as possible | |||
* You were iterating multiple times on the same array? | |||
** You should be able to spot your issue now. | |||
* Are you using [[execVM]] on the same file, many times? | |||
** Store your function in memory to avoid file reading every call with {{Inline code|_myFunction {{=}} [[compile]] [[preprocessFileLineNumbers]] "myFile.sqf";}} | |||
* Is your variable name far too long? | |||
** Find a smaller name, according to the variable scope: | |||
e.g | |||
{ _opforUnitUniform {{=}} [[uniform]] [[_x]]; [[systemChat]] _opforUnitUniform; } [[forEach]] _allOpforUnits; | |||
becomes | |||
{ _uniform {{=}} [[uniform]] [[_x]]; [[systemChat]] _uniform; } [[forEach]] _allOpforUnits; | |||
{{Informative | See [[Code Optimisation]] for more information.}} | |||
== Best practices == | == Best practices == | ||
=== | === Code format === // move that in Make it Readable maybe - it's too late, brb later (: | ||
* Some general coding tips from [https://www.topcoder.com/blog/coding-best-practices/ here]: standards, line lengths, etc | * Some general coding tips from [https://www.topcoder.com/blog/coding-best-practices/ here]: standards, line lengths, etc | ||
** Variable names should indicate what they store / are used for. | ** Variable names should indicate what they store / are used for. | ||
* Format, indentation, no one-line, spacing, line returns | * Format, indentation, no one-line, spacing, line returns |
Revision as of 22:10, 8 September 2019
Getting started
Prerequisites
- An Arma game
- Passion
- Some English knowledge, helped if needed by a translator tool like Google Translate or DeepL
- Access to this wiki is a plus (the Swiss flag is, too)
- Tutorial/example article (YouTube tutorials, )
- Google-Fu (a.k.a search engine skills)
- Advanced text editor (Such as Notepad++ or Visual Studio Code)
Rules
In the domain of development, any rule is a rule of thumb. If a rule states for example that it is better that a line of code doesn't go over 80 characters, it doesn't mean that any line must not go over 80 characters; sometimes, the situation needs it. If you have a good structure, do not change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement.
With that being said, here are the three basic rules to get yourself in the clear:
Make it work
Template:quote Your first goal when coding is to make your code do what you want it does. A good way to reach this objective is to read and getting inspired by other people's code. If you understand it by reading it once, it is probably a good source of inspiration.
- When starting from scratch if you know what you want but miss the specific steps to get to your point, it is a good practice to write down in your native language what you want to do. E.g Get all the units near the city, and for each west soldier in them, add 30% damage.
- Use -showScriptErrors startup parameter and make sure your code doesn't throw errors. Not only will your code run slower but it may also not work at all. Be sure to read the error, isolate the issue and sort it out thanks to this Wiki.
- Read your Arma RPT (report) to read more details about the error that happened in your code.
Make it readable
Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:
- While SQF is (non-noticeably) impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like _u instead of _uniform should not be present.
- One-lining (putting everything in one statement) memory improvement is most of the time not worth the headache it gives when trying to read it. Don't overuse it.
- Indentation is important for the human mind, and space is too. Space is free, use it.
- Same goes for line return; it helps to see a code block wrapping multiple common instructions instead of having to guess where it starts and stops.
- Do you see the same code multiple times, only with different parameters? Now is the time to write a function!
- If you have a lot of if..else, you may want to look at a switch condition, or again break your code in smaller functions.
- Is your function code far too long? Break it in understandable-sized bites for your own sanity.
- Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
See the following code:
_w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east});
The same example is far more readable with proper spacing, good variable names and intermediate results:
_weaponNames = []; _allUnitsAliveAndDead = allUnits + allDeadMen; _allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east }; { _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;
Constants
Using a hard-coded constant more than once? Use preprocessor directives rather than storing it in memory or cluttering your code with numbers. Such as:
a = _x + 1.053; b = _y + 1.053;
And
_buffer = 1.053; a = _x + _buffer; b = _y + _buffer;
Becomes
#define BUFFER 1.053 // note: no semicolon _a = _x + BUFFER; _b = _y + BUFFER;
Global "constants" can be defined via a Description.ext declaration, though, and accessed using getMissionConfigValue command:
Declaration in Description.ext:
var1 = 123;
var2 = "123";
var3[] = {1,2,3};
rand = __EVAL(random 999);
Usage in code:
hint str getMissionConfigValue "var1"; // 123 // 0.0007 ms hint str getMissionConfigValue "var2"; // "123" // 0.0008 ms hint str getMissionConfigValue "var3"; // [1,2,3] // 0.0017 ms hint str getMissionConfigValue "rand"; // constant random, for example 935.038 // 0.0007 ms
The getMissionConfigValue command searching Description.ext from top to bottom, it is better for a matter of performance to put all your definitions at the top of the file.
Optimise then
Once you know what is what, you can understand your code better.
- Use private variables instead of global variables (preceded with an underscore) as much as possible
- You were iterating multiple times on the same array?
- You should be able to spot your issue now.
- Are you using execVM on the same file, many times?
- Store your function in memory to avoid file reading every call with
_myFunction = compile preprocessFileLineNumbers "myFile.sqf";
- Store your function in memory to avoid file reading every call with
- Is your variable name far too long?
- Find a smaller name, according to the variable scope:
e.g
{ _opforUnitUniform = uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits;
becomes
{ _uniform = uniform _x; systemChat _uniform; } forEach _allOpforUnits;
Best practices
=== Code format === // move that in Make it Readable maybe - it's too late, brb later (:
- Some general coding tips from here: standards, line lengths, etc
- Variable names should indicate what they store / are used for.
- Format, indentation, no one-line, spacing, line returns
- Be consistent (space/tab indentation, (camel)casing, K&R style / Allman style indenting)
- Use comments frequently to explain not what the code does, but why it is done this way.
Make reusable functions
- Don't copy and paste the same code, make functions
- Don't make macros to replace SQF code
Variables
- Prefix your public variables and setVariable with your tag
- PRIVATE (or params) your variables
- Use
#define SOME_CONST
for constant values instead of variables
Code location
- Nothing in init box but local commands for this specific unit - all the init boxes are run client-side on client connection
0 = myCommand
is "useful" only for editor fields that for no apparent reason refuse commands returning a value.
Final words
- Learn from others' scripts but don't steal code and pretend it's yours — be a decent human being.
- Don't try to obfuscate your code: it is considered rude, especially since you learnt from others.
- Have fun!