|
|
Line 1: |
Line 1: |
| [[Category: Sandbox]] | | [[Category: Sandbox]] |
|
| |
| {{Informative | Future [[Code Best Practices]] page}}
| |
| {{wip}}
| |
|
| |
| ----
| |
|
| |
| {{SideTOC}}
| |
| {{Stub}}
| |
|
| |
| [[:Category: Scripting Topics]]
| |
|
| |
| == Getting started ==
| |
|
| |
| 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, let's go!
| |
|
| |
| === Prerequisites ===
| |
|
| |
| * An advanced text editor such as Visual Studio Code or Notepad++ (see [[Debugging Techniques#Code Edition|Debugging Techniques' Code Edition chapter]] for useful plugins)
| |
| * Some English knowledge, helped if needed by a translator tool such as [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)
| |
| * Tutorials/examples (YouTube tutorials, community tutorials, [[:Category:Example_Code|Example Code]])
| |
| * Google-Fu (a.k.a search engine skills)
| |
| * {{arma3}} Discord channel: [https://discord.gg/arma ARMA], channel <tt>#scripting</tt>
| |
|
| |
|
| |
| == Best practices ==
| |
|
| |
| === Code format ===
| |
|
| |
| * Whatever you do about your code's format, '''be consistent'''.
| |
| * Choose an indentation format '''and stick to it'''. There is not especially one indent better than another (''but there sure are terrible ones''), again the important point here being ''consistency''.
| |
| ** Common indentation styles are:
| |
| *** [https://en.wikipedia.org/wiki/Indentation_style#K&R_style K&R style] indenting
| |
| *** [https://en.wikipedia.org/wiki/Indentation_style#Allman_style Allman style] indenting
| |
| ** Use empty space. Line return, spaces before and after brackets, if this improves readability, use it: space is free.
| |
| ** indent with two/four spaces '''or''' one tab. Do not mix these.
| |
| ** ''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.
| |
| * {{Inline code|<span style{{=}}"background-color: #FCC">'''0 {{=}}''' </span>''myCommand''}} is "useful" only for editor fields that for no apparent reason refuse commands returning a value. You do '''not''' need it in script files.
| |
|
| |
| === Variable format ===
| |
|
| |
| * Name your variables and functions properly:<!--
| |
| --> 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.
| |
| ** Your variable names must have a meaning: variables like '''_u''' instead of '''_uniform''' should not exist. '''_i''' is an accepted iteration variable name (e.g in [[for]] loops).
| |
| ** It is recommended to use [https://en.wikipedia.org/wiki/Camel_case camel-case] your variables;<!--
| |
| --> camel-casing (namingLikeThis) your variables and functions make the code naturally more readable, especially for long names.
| |
| * Prefix your public variables and [[setVariable]] with your tag in order to avoid any conflict about other mods, scripts or mission variables.
| |
| * Make your variables '''private''' thanks to the [[private]] or [[params]] keyword in order to avoid accidental upper-scope overwriting of variables of the same name.
| |
| * Defined constants must be UPPERCASE_WITH_UNDERSCORES (e.g {{Inline code|#define SOME_CONST}}).
| |
|
| |
|
| |
| === Code structuration ===
| |
|
| |
| * '''DRY:''' '''D'''on't '''R'''epeat '''Y'''ourself. If you write the same code block or the same logic many times, export this code as a function and use parameters with it.
| |
| ** If your code has too many levels, it is time to split and rethink it<!--
| |
| --> (e.g {{Inline code|[[if]] (a) [[then]] { [[if]] (b) [[then]] { [[if]] (c) [[then]] { {{codecomment|/* etc */ }} }; }; };}}...)
| |
| ** Do NOT use [[PreProcessor Commands#Macros|macros]] as functions - these hinder readability. Use functions instead.
| |
| * {{codecomment|Comments}} in your code must not explain ''what'' the code does, but ''why'' it is done this way (if needed).<!--
| |
| --> Your code organisation combined to your variable names must be enough to be read by a human.
| |
|
| |
| === Code/Files organisation ===
| |
|
| |
| * Use [[Arma 3 Functions Library#Adding a Function|CfgFunctions]] to declare the functions that will be called frequently.
| |
| ** One Functions directory, with sub-directories if needed.
| |
| * Use [[Event Scripts]] as needed.
| |
| * Don't put '''any code''' in a unit's init box ''but eventually'' [[local]] commands for this specific unit - '''all''' unit's init boxes are run client-side on client connection.
| |
|
| |
|
| |
| == Examples ==
| |
| <!--
| |
|
| |
| EDITOR'S NOTE: Formatting Code examples are not linking commands on purpose! This allows for a fair comparison of both syntaxes' readability.
| |
|
| |
| -->
| |
| {| class="bikitable"
| |
| ! <big>Bad Example</big>
| |
| ! <big>Good Example</big>
| |
| |-
| |
| ! colspan="2" |
| |
| <small>
| |
| === Good Practice examples ===
| |
| </small>
| |
| |-
| |
| | <code>_unit = player;</code>
| |
| | <code>'''private''' _unit = player;</code>
| |
| |-
| |
| | <code>private _uB = allUnits select { side _x == blufor };</code>
| |
| | <code>private '''_bluforUnits''' = allUnits select { side _x == blufor };</code>
| |
| |-
| |
| | <code>private _plead = leader player;</code>
| |
| | <code>private '''_playersLeader''' = leader player;</code>
| |
| |-
| |
| | <code>finalAssault = true; publicVariable "finalAssault";</code>
| |
| | <code>'''PREFIX_'''finalAssault = true; publicVariable "'''PREFIX_'''finalAssault";</code>
| |
| |-
| |
| | <code>player setVariable ["MoneyInPocket", 250, true];</code>
| |
| | <code>player setVariable ["'''PREFIX_'''MoneyInPocket", 250, true];</code>
| |
| |-
| |
| |
| |
| <code>#define KILL(UNIT) UNIT setDamage 1<br>
| |
| (...)<br>
| |
| {
| |
| KILL(_x);
| |
| } forEach (units group player - [player]);</code>
| |
| |
| |
| <code>private _killFnc = { _this setDamage 1; };<br>
| |
| (...)<br>
| |
| {
| |
| _x call _killFnc;
| |
| } forEach (units group player - [player]);</code>
| |
| |- style="vertical-align: top"
| |
| |
| |
| <code>{{cc|if player has less than 3/4 health}}
| |
| if (damage player > 0.25) then
| |
| {
| |
| {{cc|if the player has no first aid kit}}
| |
| if (not ("FirstAidKit" in items player))
| |
| {
| |
| {{cc|if player has room for first aid kit}}
| |
| if (player canAdd "FirstAidKit") then
| |
| {
| |
| {{cc|add first aid kit to the player}}
| |
| player addItem "FirstAidKit";
| |
| }
| |
| else
| |
| {
| |
| {{cc|set player's damage to 1/4}}
| |
| player setDamage 0.25;
| |
| };
| |
| };
| |
| };</code>
| |
| |
| |
| <code>{{cc|player will need health at this stage of the mission}}
| |
| if (
| |
| damage player > 0.25 &&
| |
| not ("FirstAidKit" in items player)) then
| |
| {
| |
| if (player canAdd "FirstAidKit") then
| |
| {
| |
| player addItem "FirstAidKit";
| |
| }
| |
| else {{cc|let's help him anyway}}
| |
| {
| |
| player setDamage 0.25;
| |
| };
| |
| };</code>
| |
| |-
| |
| ! colspan="2" |
| |
| <small>
| |
| === Flow Logic examples ===
| |
| </small>
| |
| |- style="vertical-align: top"
| |
| |
| |
| <code>if (alive player && damage player >= 0.9) then {
| |
| hint "very damaged";
| |
| };
| |
| if (alive player && damage player >= 0.5 && damage player < 0.9) then {
| |
| hint "quite damaged";
| |
| };
| |
| if (alive player && damage player > 0 && damage player < 0.5) then {
| |
| hint "slightly damaged";
| |
| };
| |
| if (alive player && damage player == 0) then {
| |
| hint "pristine state";
| |
| };
| |
| if (not alive player) then {
| |
| hint "dead";
| |
| };</code>
| |
| |
| |
| <code>if (not alive player) exitWith { hint "dead"; };<br>
| |
| private _playerDamage = damage player;
| |
| switch (true) do {
| |
| case (_playerDamage >= 0.9): { hint "very damaged"; };
| |
| case (_playerDamage >= 0.5): { hint "quite damaged"; };
| |
| case (_playerDamage > 0) : { hint "slightly damaged"; };
| |
| default { hint "pristine"; };
| |
| };</code>
| |
| |-
| |
| ! colspan="2" |
| |
| <small>
| |
| === Format examples ===
| |
| </small>
| |
| |- style="vertical-align: top"
| |
| |
| |
| <code>if (not alive player) exitWith
| |
| { hint "dead"; };<br>
| |
| private _playerDamage = damage player;
| |
| switch (true) do {
| |
| case (_playerDamage >= 0.9): {hint "very damaged";};
| |
| case (_playerDamage >= 0.5): {
| |
| hint "quite damaged";};<br>
| |
| case (_playerDamage > 0): { hint "slightly damaged"; };
| |
| <nowiki> </nowiki>default
| |
| {<br>
| |
| <nowiki> </nowiki>hint "pristine";
| |
| <nowiki> </nowiki>};
| |
| };</code>
| |
| |
| |
| <code>if (not alive player) exitWith { hint "dead"; };<br>
| |
| private _playerDamage = damage player;
| |
| switch (true) do {
| |
| case (_playerDamage >= 0.9): { hint "very damaged"; };
| |
| case (_playerDamage >= 0.5): { hint "quite damaged"; };
| |
| case (_playerDamage > 0) : { hint "slightly damaged"; };
| |
| default { hint "pristine"; };
| |
| };</code>
| |
| |}
| |
|
| |
|
| |
| == Final words ==
| |
|
| |
| * Remember: '''consistency''' is the most important thing!
| |
| * The {{arma3}} Discord channel and its community can help: [https://discord.gg/arma ARMA]
| |
| * Learn from others' scripts but don't steal code and pretend it's yours — be a decent human being. Stealing code and its consequences:
| |
| ** It soils your reputation and devaluates your actions once it is found out — and it ''always'' get found out. DMCA's are filled on Steam every day.
| |
| ** It makes people in the community get less helpful and more reluctant in giving advices. It can also prevent them to ''release'' an interesting feature!
| |
| * Don't try to obfuscate your code: it is considered rude, especially since you learnt from others.
| |
| ** Obfuscated code only makes it ''harder'' to get, but does not make it ''copy-protected''. If the engine can read it, it can be obtained.
| |
| ** Obfuscated code is also slower on compilation (and, depending on the quality of code and obfuscation, on execution too).
| |
| * Most of all, have fun!
| |