Variables: Difference between revisions
Lou Montana (talk | contribs) m (Add green cut due to blue variable) |
Lou Montana (talk | contribs) (Add privateAll link) |
||
(24 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{ | {{TOC|side}} | ||
A [[Variables|variable]] is a "storage container" or "named placeholder" for data. You can read and modify the data once this container is created.<br> | A [[Variables|variable]] is a "storage container" or "named placeholder" for data. You can read and modify the data once this container is created.<br> | ||
Its "name" is referenced as [[Identifier]]. | Its "name" is referenced as [[Identifier]]. | ||
Line 12: | Line 12: | ||
== | == Initialisation == | ||
The first thing to do to create a variable is to find its name, also called [[Identifier|identifier]]; this name must be speaking to the reader - keep in mind that code is meant to be read by ''human beings'' (see [[Code Best Practices#Variable format|Code Best Practices - Variable format]]). | The first thing to do to create a variable is to find its name, also called [[Identifier|identifier]]; this name must be speaking to the reader - keep in mind that code is meant to be read by ''human beings'' (see [[Code Best Practices#Variable format|Code Best Practices - Variable format]]). | ||
Line 18: | Line 18: | ||
Once a proper name is found, it can be used to '''declare''' (or '''initialise''') the variable: | Once a proper name is found, it can be used to '''declare''' (or '''initialise''') the variable: | ||
<sqf>myVariable = 0;</sqf> | |||
Before | Before {{GameCategory|arma2|link= y}}, querying undefined (or uninitialised) variables returns [[nil]] (undefined value); from {{GameCategory|arma2|link= y}} and later, it returns an "Error Undefined variable in expression" error. | ||
{{ | {{Feature|informative| | ||
Unless trying to emulate [[isNil]], '''always''' declare your variable before trying to access it.}} | An undefined ([[nil]]) variable converted to [[String]] with [[str]] will return [[scalar bool array string 0xe0ffffef]] (in [[:Category:ArmA: Armed Assault|{{arma1}}]]) and [[scalar bool array string 0xfcffffef]] (in [[:Category:Operation Flashpoint|{{ofp}}]]).<br> | ||
Unless trying to emulate [[isNil]], '''always''' declare your variable before trying to access it. | |||
}} | |||
Line 31: | Line 33: | ||
Variable deletion is done by setting its value to [[nil]]: | Variable deletion is done by setting its value to [[nil]]: | ||
<sqf>HugeVariable = nil;</sqf> | |||
{{ | {{Feature|informative|Local variables are automatically freed (deleted from memory) when their scope is exited, avoiding the need to manually deallocate them.}} | ||
== Scopes == | == Scopes == | ||
Variables are only visible in certain scopes of the game. This prevents name conflicts between different variables in different [[Script | Variables are only visible in certain scopes of the game. This prevents name conflicts between different variables in different [[Script File|scripts]]. | ||
There are two main scopes: | There are two main scopes: | ||
Line 47: | Line 49: | ||
Names given to units in the [[Mission Editor]] are also global variables pointing to those units, which may not be redefined or modified. | Names given to units in the [[Mission Editor]] are also global variables pointing to those units, which may not be redefined or modified. | ||
<!-- TODO: mention missionNamespace? --> | <!-- TODO: mention missionNamespace? --> | ||
{{ | {{Feature | important | a global variable can be different from one machine to another!<!-- | ||
--> To ensure the global variable's proper broadcast, see [[publicVariable]], [[publicVariableServer]] and [[publicVariableClient]]. | --> To ensure the global variable's proper broadcast, see [[publicVariable]], [[publicVariableServer]] and [[publicVariableClient]]. | ||
If the value changes, the broadcast must be reapplied. e.g: | If the value changes, the broadcast must be reapplied. e.g: | ||
< | <sqf> | ||
GlobalVariable = 33; | |||
publicVariable "GlobalVariable"; | |||
GlobalVariable = 42; // GlobalVariable is now 42 on the current machine but still 33 on the others | |||
publicVariable "GlobalVariable"; // updates the value to 42 for everyone | |||
</sqf> | |||
}} | }} | ||
=== Local Scope === | === Local Scope === | ||
A local variable is only visible in the [[Script | A local variable is only visible in the [[Script File|Script]], [[Function]] or [[Control Structures|Control Structure]] in which it was defined. | ||
{{Feature|informative|A local variable cannot be broadcast. In order to broadcast a local variable's ''value'', it must be assigned to a global variable first: | |||
<sqf> | |||
private _myLocalVariable = 33; | |||
publicVariable "_myLocalVariable"; // incorrect | |||
GlobalVariable = _myLocalVariable; | |||
publicVariable "GlobalVariable"; // correct | |||
</sqf> | |||
}} | |||
=== Local Variables Scope === | === Local Variables Scope === | ||
{{ | {{Feature|warning| | ||
Local variables in [[call]]able code (e.g [[Arma 3 Functions Library|Functions]]) should be scoped using the | Local variables in [[call]]able code (e.g [[Arma 3: Functions Library|Functions]]) should be scoped using the [[private]]/[[privateAll]] commands, | ||
otherwise you may modify local variables of the [[call]]ing script that are visible in the function.}} | otherwise you may modify local variables of the [[call]]ing script that are visible in the function.}} | ||
<sqf> | |||
// Since Arma 3 v1.54 | |||
private _myLocalVariable = 0; | |||
// From Arma 2 until Arma 3 v1.54 | |||
local _myLocalVariable = 0; | |||
// Before Arma 2 | |||
private "_myLocalVariable"; | |||
_myLocalVariable = 0; | |||
// Alternative method to private multiple local variables at the same time | |||
private ["_myLocalVariable1", "_myLocalVariable2"]; | |||
_myLocalVariable1 = 1; | |||
_myLocalVariable2 = 2; | |||
</sqf> | |||
If a [[private]] variable is initialised within a [[Control Structures|Control Structure]] (i.e. [[if]], [[for]], [[switch]], [[while]]), | If a [[private]] variable is initialised within a [[Control Structures|Control Structure]] (i.e. [[if]], [[for]], [[switch]], [[while]]), | ||
Line 94: | Line 104: | ||
don't judge me | don't judge me | ||
--> | --> | ||
<table style="font-family: monospace; font-size: 1.1em; line-height: 1.15em; margin: auto; tab-size: 4; -moz-tab-size: 4; white-space: pre"> | <table style="border-collapse: collapse; font-family: monospace; font-size: 1.1em; line-height: 1.15em; margin: auto; tab-size: 4; -moz-tab-size: 4; white-space: pre"> | ||
<tr> | <tr> | ||
<th colspan=" | <th colspan="9">Variables<br>lifespan</th> | ||
<th style="font-size: 1.25em">Code</th> | <th style="font-size: 1.25em">Code</th> | ||
</tr> | |||
<tr style="border-bottom: 0.05em solid grey"> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td style="line-height: 2em">'''{{Color|grey|BEGINNING OF FILE}}''' - e.g <span style="font-size: small"><sqf inline>execVM "script.sqf";</sqf></span></td> | |||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td rowspan="28" style="background-color: orange; border-radius: 10em; width: .40em"></td> | <td rowspan="28"></td> | ||
<td rowspan="29" style="background-color: orange; border-radius: 10em 10em 0 0; width: .40em"></td> | |||
<td rowspan="28" style="width: .10em"></td> | |||
<td style="width: .40em"></td> | <td style="width: .40em"></td> | ||
<td rowspan="28" style="width: .10em"></td> | |||
<td style="width: .40em"></td> | <td style="width: .40em"></td> | ||
<td rowspan="28" style="width: .10em"></td> | |||
<td style="width: .40em"></td> | <td style="width: .40em"></td> | ||
<td rowspan="28"></td> | <td rowspan="28" style="width: 1em"></td> | ||
<td>'''{{Color|darkorange|TAG_GlobalVariable}}''' = "Global variable"; {{cc|Global variable is accessible from any scope}}</td> | <td>'''{{Color|darkorange|TAG_GlobalVariable}}''' = "Global variable"; {{cc|Global variable is accessible from any scope}}</td> | ||
</tr> | </tr> | ||
Line 114: | Line 140: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td rowspan="4" style="background-color: darkgreen; border-radius: 10em"></td> | <td rowspan="4" style="background-color: darkgreen; border-radius: 10em; margin: .1em"></td> | ||
<td><wbr /></td> | <td><wbr /></td> | ||
<td><wbr /></td> | <td><wbr /></td> | ||
Line 174: | Line 200: | ||
<td></td> | <td></td> | ||
<td></td> | <td></td> | ||
<td>[[hint]] [[str]] '''{{Color|darkgreen|_myVariable}}'''; {{cc|if '''{{Color|grey|_condition}}''' is [[true]], _myVariable's value is '''{{Color|darkgreen|1}}''';}}</td> | <td>[[hint]] [[str]] '''{{Color|darkgreen|_myVariable}}'''; {{cc|if '''{{Color|grey|_condition}}''' is [[true]], '''{{Color|darkgreen|_myVariable}}''''s value is '''{{Color|darkgreen|1}}''';}}</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td></td> | <td></td> | ||
<td></td> | <td></td> | ||
<td> {{cc|if '''{{Color|grey|_condition}}''' is [[false]], _myVariable's value is '''{{Color|darkgreen|4}}'''.}}</td> | <td> {{cc|if '''{{Color|grey|_condition}}''' is [[false]], '''{{Color|darkgreen|_myVariable}}''''s value is '''{{Color|darkgreen|4}}'''.}}</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
Line 249: | Line 275: | ||
<td></td> | <td></td> | ||
<td>{{cc|'''{{Color|purple|_anotherVariable}}''' being only scoped in the [[else]] block.}}</td> | <td>{{cc|'''{{Color|purple|_anotherVariable}}''' being only scoped in the [[else]] block.}}</td> | ||
</tr> | |||
<tr style="border-top: 0.05em solid grey"> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td style="line-height: 2em">'''{{Color|grey|END OF FILE}}''' - '''{{Color|darkorange|TAG_GlobalVariable}}''' keeps on living in [[missionNamespace]]</td> | |||
</tr> | </tr> | ||
</table> | </table> | ||
Line 257: | Line 294: | ||
! style="width: 50%" | Incorrect | ! style="width: 50%" | Incorrect | ||
|- | |- | ||
|< | | <sqf> | ||
private _living = false; | |||
if (alive player) then | |||
{ | |||
_living = true; | |||
}; | |||
| < | |||
hint format ["%1", _living]; // returns true | |||
</sqf> | |||
| <sqf> | |||
// - | |||
if (alive player) then | |||
{ | |||
private _living = true; | |||
}; | |||
// throws an error since the private variable was | |||
// not initialised outside of the if control structure. | |||
hint format ["%1", _living]; | |||
</sqf> | |||
|} | |} | ||
Line 275: | Line 319: | ||
== Data Types == | == Data Types == | ||
Variables may store values of a certain [[Data Types|Data | Variables may store values of a certain [[:Category:Data Types|Data Types]] ([[String]], [[Number]], etc). The kind of the value specifies the ''type'' of the variable. | ||
Different [[Operators|operators]] and [[:Category:Scripting Commands|commands]] require variables to be of different types. | Different [[Operators|operators]] and [[:Category:Scripting Commands|commands]] require variables to be of different types. | ||
A variable is not strongly typed and changes its type according to the new data: | A variable is not strongly typed and changes its type according to the new data: | ||
<sqf> | |||
private "_myVariable"; // nil | |||
_myVariable = 1; // 1 (Number) | |||
_myVariable = "test"; // "test" (String) | |||
</sqf> | |||
Line 289: | Line 335: | ||
The biggest security risk would be to see it being overridden by a malicious usage of [[publicVariable]], setting potentially dangerous code in it. | The biggest security risk would be to see it being overridden by a malicious usage of [[publicVariable]], setting potentially dangerous code in it. | ||
The best option is to declare your function in [[Arma 3 Functions Library|CfgFunctions]] so the engine secures it for you. | The best option is to declare your function in [[Arma 3: Functions Library|CfgFunctions]] so the engine secures it for you. | ||
If you want to manually create a global function anyway, the best practice is the following: | If you want to manually create a global function anyway, the best practice is the following: | ||
<sqf>TAG_MyGlobalVariableFunction = compileFinal preprocessFileLineNumbers "functionFile.sqf";</sqf> | |||
{{ | {{Feature|informative| | ||
You should ideally run this code locally on every machine. | |||
Using [[publicVariable]] on a "final" global function (created with [[compileFinal]]) will indeed broadcast the variable '''and''' make it final on other clients as well, but the network can quickly become saturated from sending big pieces of code. | |||
}} | |||
Line 301: | Line 350: | ||
* [[Introduction to Arma Scripting]] | * [[Introduction to Arma Scripting]] | ||
* [[Identifier]] | * [[Identifier]] | ||
* [[Data Types]] | * [[:Category: Data Types| Data Types]] | ||
* [[Control Structures]] | * [[Control Structures]] | ||
* [[Magic Variables]] | * [[Magic Variables]] | ||
* [[private]] ({{arma3}}) | * [[private]]/[[privateAll]] ({{arma3}}) | ||
* [[local]] ({{arma2}}) | * [[local]] ({{arma2}}) | ||
[[Category: Scripting Topics]] | [[Category: Scripting Topics]] |
Latest revision as of 15:13, 11 July 2024
A variable is a "storage container" or "named placeholder" for data. You can read and modify the data once this container is created.
Its "name" is referenced as Identifier.
Requirements
The following links guide to the basics to understand this article:
Initialisation
The first thing to do to create a variable is to find its name, also called identifier; this name must be speaking to the reader - keep in mind that code is meant to be read by human beings (see Code Best Practices - Variable format).
Once a proper name is found, it can be used to declare (or initialise) the variable:
Before Arma 2, querying undefined (or uninitialised) variables returns nil (undefined value); from Arma 2 and later, it returns an "Error Undefined variable in expression" error.
Deletion
Once created, variables take up space in the computer's memory.
This is not drastic for small variables, but if a big number of very large variables is used, it is recommended to undefine the unneeded ones in order to free up memory.
Variable deletion is done by setting its value to nil:
Scopes
Variables are only visible in certain scopes of the game. This prevents name conflicts between different variables in different scripts.
There are two main scopes:
Global Scope
A global variable is visible from all scripts on the computer on which it was defined. Names given to units in the Mission Editor are also global variables pointing to those units, which may not be redefined or modified.
Local Scope
A local variable is only visible in the Script, Function or Control Structure in which it was defined.
Local Variables Scope
If a private variable is initialised within a Control Structure (i.e. if, for, switch, while), its existence will be limited to it and its sub-structures - the variable does not exist outside of the structure and will be seen as undefined.
Variables lifespan |
Code | ||||||||
---|---|---|---|---|---|---|---|---|---|
BEGINNING OF FILE - e.g execVM "script.sqf"; | |||||||||
TAG_GlobalVariable = "Global variable"; // Global variable is accessible from any scope | |||||||||
private _myVariable = 0; | |||||||||
if (_condition) then | |||||||||
{ | |||||||||
_myVariable = 1; | |||||||||
private _myVariable = 2; | |||||||||
_myVariable = 3; | |||||||||
} else { | |||||||||
_myVariable = 4; | |||||||||
private _anotherVariable = 10; | |||||||||
}; | |||||||||
hint str _myVariable; // if _condition is true, _myVariable's value is 1; | |||||||||
// if _condition is false, _myVariable's value is 4. | |||||||||
[] call { | |||||||||
hint str _myVariable; // works, as called code runs in the same scope | |||||||||
}; | |||||||||
[_myVariable] spawn { | |||||||||
hint TAG_GlobalVariable; // works | |||||||||
hint str _myVariable; // throws an "undefined variable" error, | |||||||||
// as _myVariable does not exist in this new script | |||||||||
}; | |||||||||
// trying to use _anotherVariable here would result in an "undefined variable" error, | |||||||||
// _anotherVariable being only scoped in the else block. | |||||||||
END OF FILE - TAG_GlobalVariable keeps on living in missionNamespace |
Correct | Incorrect |
---|---|
Data Types
Variables may store values of a certain Data Types (String, Number, etc). The kind of the value specifies the type of the variable. Different operators and commands require variables to be of different types.
A variable is not strongly typed and changes its type according to the new data:
Multiplayer Considerations
Storing functions (or any callable code) into global variables without securing them with compileFinal (since Arma 3) is a very bad practice in multiplayer. The biggest security risk would be to see it being overridden by a malicious usage of publicVariable, setting potentially dangerous code in it.
The best option is to declare your function in CfgFunctions so the engine secures it for you.
If you want to manually create a global function anyway, the best practice is the following: