Scripting: Best Practices – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Fix category)
m (Text replacement - "<syntaxhighlight lang="C#">" to "<enforce>")
Line 52: Line 52:
! Good
! Good
|- style="vertical-align: top"
|- style="vertical-align: top"
| <syntaxhighlight lang="C#">
| <enforce>
auto number = 42;
auto number = 42;
Animal cutePet = new Dog();
Animal cutePet = new Dog();
</syntaxhighlight>
</syntaxhighlight>
| <syntaxhighlight lang="C#">
| <enforce>
int number = 42;
int number = 42;
Dog cutePet = new Dog();
Dog cutePet = new Dog();
</syntaxhighlight>
</syntaxhighlight>
|- style="vertical-align: top"
|- style="vertical-align: top"
| <syntaxhighlight lang="C#">
| <enforce>
for (int i = 0; i < list.Count(); i++)
for (int i = 0; i < list.Count(); i++)
{
{
}
}
</syntaxhighlight>
</syntaxhighlight>
| <syntaxhighlight lang="C#">
| <enforce>
for (int i = 0, count = list.Count(); i < count; i++) // only one list.Count() call
for (int i = 0, count = list.Count(); i < count; i++) // only one list.Count() call
{
{
Line 72: Line 72:
</syntaxhighlight>
</syntaxhighlight>
|- style="vertical-align: top"
|- style="vertical-align: top"
| <syntaxhighlight lang="C#">
| <enforce>
// declaring 'text' string in every for loop creates a pointer attribution for each loop
// declaring 'text' string in every for loop creates a pointer attribution for each loop
for (int i = 0, count = list.Count(); i < count; i++)
for (int i = 0, count = list.Count(); i < count; i++)
Line 80: Line 80:
}
}
</syntaxhighlight>
</syntaxhighlight>
| <syntaxhighlight lang="C#">
| <enforce>
string text; // external declaration = one pointer attribution
string text; // external declaration = one pointer attribution
for (int i = 0, count = list.Count(); i < count; i++)
for (int i = 0, count = list.Count(); i < count; i++)
Line 89: Line 89:
</syntaxhighlight>
</syntaxhighlight>
which can also be simplified to
which can also be simplified to
<syntaxhighlight lang="C#">
<enforce>
foreach (int i, element : list)
foreach (int i, element : list)
{
{
Line 96: Line 96:
</syntaxhighlight>
</syntaxhighlight>
|- style="vertical-align: top"
|- style="vertical-align: top"
| <syntaxhighlight lang="C#">
| <enforce>
if (a)
if (a)
{
{
Line 112: Line 112:
}
}
</syntaxhighlight>
</syntaxhighlight>
| <syntaxhighlight lang="C#">
| <enforce>
if (a && b)
if (a && b)
{
{
Line 120: Line 120:
|- style="vertical-align: top"
|- style="vertical-align: top"
| Boilerplate code:
| Boilerplate code:
<syntaxhighlight lang="C#">
<enforce>
int i = 0;
int i = 0;
if (a)
if (a)
Line 136: Line 136:
</syntaxhighlight>
</syntaxhighlight>
| Simplified:
| Simplified:
<syntaxhighlight lang="C#">
<enforce>
int i = 0;
int i = 0;
array<bool> conditions = { a, b, c };
array<bool> conditions = { a, b, c };
Line 148: Line 148:
</syntaxhighlight>
</syntaxhighlight>
or (if some other code is involved)
or (if some other code is involved)
<syntaxhighlight lang="C#">
<enforce>
int i = 0;
int i = 0;
IncrementIfTrue(a, i);
IncrementIfTrue(a, i);
Line 163: Line 163:
</syntaxhighlight>
</syntaxhighlight>
|- style="vertical-align: top"
|- style="vertical-align: top"
| <syntaxhighlight lang="C#">
| <enforce>
Initialise(player1, 1);
Initialise(player1, 1);
Initialise(player2, 2);
Initialise(player2, 2);
Line 171: Line 171:
Initialise(player6, 6);
Initialise(player6, 6);
</syntaxhighlight>
</syntaxhighlight>
| <syntaxhighlight lang="C#">
| <enforce>
array<IEntity> list = { player1, player2, player3, player4, player5, player6 };
array<IEntity> list = { player1, player2, player3, player4, player5, player6 };
foreach (int i, item : list)
foreach (int i, item : list)

Revision as of 20:17, 30 July 2022

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 the code has a good structure, do not change it to enforce a single arbitrary rule. If many of them are not implemented/not respected, changes should be applied; again, this is according to one's judgement.

With that being said, let's go!


Best practices

Code format

  • Reminder: chosen indentation for Enfusion is Allman style
  • Reminder: indentation is done with tabulations
  • Use empty space. Line return, spaces before and after brackets, if this improves readability, use it: space is free
  • 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
    • it also hinders debugger's usage, e.g in the event of an inlined if

Variable format

  • Name variables and functions properly: code must be readable by a human being, e.g variables like u instead of uniform should not exist.
    • i is an accepted iteration variable name (e.g in for loops).
  • Prefix any public content (classes, global methods, global variables) with a Creator Tag in order to prevent conflicts with other mods.
  • Use the closest value type whenever possible; using auto for a known variable type makes code less clear.

Code Structuration

SOLID

A series of development principles to follow in order to ensure an easy code maintenance and lifetime.

See SOLID.

DRY

Don't Repeat Yourself. If within the same class, the same code or the same pattern is written in various places, write a protected method and use appropriate parameters.

Logical Simplifications

If the code has too many repetitions, make a common method as stated above.

If the code has too many levels, it is time to split it and rethink it.

Examples

Improvable Good
<enforce>

auto number = 42; Animal cutePet = new Dog(); </syntaxhighlight>

<enforce>

int number = 42; Dog cutePet = new Dog(); </syntaxhighlight>

<enforce>

for (int i = 0; i < list.Count(); i++) { } </syntaxhighlight>

<enforce>

for (int i = 0, count = list.Count(); i < count; i++) // only one list.Count() call { } </syntaxhighlight>

<enforce>

// declaring 'text' string in every for loop creates a pointer attribution for each loop for (int i = 0, count = list.Count(); i < count; i++) { string text = "Value " + i + " is " + list[i]; Print(text); } </syntaxhighlight>

<enforce>

string text; // external declaration = one pointer attribution for (int i = 0, count = list.Count(); i < count; i++) { text = "Value " + i + " is " + list[i]; Print(text); } </syntaxhighlight> which can also be simplified to <enforce> foreach (int i, element : list) { PrintFormat("Value %1 is %2", i, element); } </syntaxhighlight>

<enforce>

if (a) { if (b) { if (c) { Method(true); } else { Method(false); } } } </syntaxhighlight>

<enforce>

if (a && b) { Method(c); } </syntaxhighlight>

Boilerplate code:

<enforce> int i = 0; if (a) { i++; } if (b) { i++; } if (c) { i++; } </syntaxhighlight>

Simplified:

<enforce> int i = 0; array<bool> conditions = { a, b, c }; foreach (bool condition : conditions) { if (condition) { i++; } } </syntaxhighlight> or (if some other code is involved) <enforce> int i = 0; IncrementIfTrue(a, i); IncrementIfTrue(b, i); IncrementIfTrue(c, i);

void IncrementIfTrue(bool condition, out int value) { if (condition) { value++; } } </syntaxhighlight>

<enforce>

Initialise(player1, 1); Initialise(player2, 2); Initialise(player3, 3); Initialise(player4, 4); Initialise(player5, 5); Initialise(player6, 6); </syntaxhighlight>

<enforce>

array<IEntity> list = { player1, player2, player3, player4, player5, player6 }; foreach (int i, item : list) { Initialise(item, i + 1); } </syntaxhighlight>

Code Comments

Code comments are surprisingly not a must-have; code organisation combined to variable names should be enough to be read by a human, then comment can be used:

    • a comment should explain why the code is written this way
    • a comment should not tell what the code does; code should be self-explanatory
    • as a last resort in the event of a complex piece of code, a comment can be used to describe what the code actually does - or at least its intention
  • On the other hand, Documentation is more than welcome as it provides information from the outside without having to read the code. Enfusion uses Doxygen format.

Files organisation

See Directory Structure to know how/where to organise script files (Scripts\GameCode).
  • Have one class/enum per file
    • Small classes/enums can always be grouped together in the same file, provided they are part of the same system or only used there
  • Use (sub-)directories to group related classes