Jump to content

Recommended Posts

I'm trying to learn some basic facts about scripting.

I'm probably a bit older than most folks who play with the scripting end of the editor as I've never taken a class that even touched on computer coding...so please be kind.

 

I'd like to understand the difference in local vs global variables.

 

What determines whether a local or global variable is to be used?

Can they be used interchangeably within editor (like inside a units' init field or the on act field of a trigger)?

Or can I use global variables only inside things like triggers or local variables in called scripts?

Is the underscore (_) the local or global version?

 

Any info would be helpful. As I said; I'm pretty lame when it comes to things like this (but at least I'm trying to do something about it).

Thanks in advance.

Share this post


Link to post
Share on other sites

Local and global is best understood with the "scope" of a script or a piece of code. The scope defines, where the script and all of its local variables live. Scopes can be nested. An example:

 

//Script starts here
private _localVariableInScope1 = "blah";
{
	private _localVariableInScope2 = "blub";
}
[] spawn {private _localVariableInetAnotherScope = "gold";}

As you may have noticed, it is easy to spot a nested scope by curly brackets. Infact inside every if,else,switch,foreach,etc block, their is a new, nested scope, where local variables may be declared.

When you define a local variable, the variable can be access all inside the scope it was defined but not outside of it. BUT a local variable can be accessed inside a nested scope. In the example above: Inside the curly brackets _localVariableInScope1 and _localVariableInScope2 is defined, but after the brackets only _localVariableInScope1 is defined. We say _localVariableInScope1 is out of scope.

 

A global variable is a variable that is known to all scopes after it was declared. It never runs out of scope. But global variables can be hard to debug. With local variables, you can just look where the current scope ends. The variable can only be defined and used there. Not much chance to accidentally mess with an important variable. But a global variable can be manipulated from anywhere, even from third party addons/missions/some hacky player, etc.

 

Here is yet another, maybe more complete example:

//Script starts here

private _var1= "Blub1"; 	//Another local variable in the scope of the script
GlobalVariable = 123; 		//Global variable, known everywhere

if(_someParam) then {
	private _var2 = true;	//Local variable in the if scope. This variable will be undefined after the if() then {} block
	_var1 = "Blah!";	//We can change _var1 here, because this is a nested scope/block and the var still is defined
	GlobalVariable = 321;	//We can change GlobalVariable here, because this is global...
} else {
	private _var3 = false;	//Local variable in the else scope. This variable will be undefined after the else block
	_var1 = "Gold";
	GlobalVariable = 321;
};

[_var1] call {				//This is a piece of code that is called, means this is somewhat a complete new script. Our old scope from _var1 is not valid here.
	params["_var1"]; 	//This defines a local variable and binds a params from the call of this script. The value of _var1 will be exactly like _var1 in the call, but it is another local variable, with the same name, but in another scope.
	_var1 = "NotBlub"; 
	GlobalVariable = 666; //This works because GlobalVariable is global
}

systemchat str _var2; 			//This will produce an error, as _var2 is undefined here in the outer scope of our script
systemchat str _var3; 			//This will produce an error, as _var3 is undefined here in the outer scope of our script
systemchat str _var1; 			//Defined, will print without problems. Will be "Blah!" as the code in call could not change the value in _var1 in another scope.
systemchat str GlobalVariable;  // Will be 666 as the code behind call has changed our GlobalVariable. This can be handy but also very dangerous and error prone

 

When working with triggers and inits, you will most likely want to use globalvariables to set and get information elsewhere. But when you call a script/function from a trigger or init, you will most likely want to use local variables in it. Otherweise, when you have two vehicles witht he same init line, the called scripts may interfere with each other. Example:

 

You start this from a init of a car, to make it slowly get damaged with [this] execVM "damageScript.sqf":

params["_vehicle"];

c = 0; //Global countvariable -> very bad

for[{c = 0},{c<100},{c=c+1}] do {
	_vehicle setdamage c*0.01;
    sleep 1;
};

When you have only one vehicle with this script, the vehicle will get totally damaged over the time of 100 seconds. If you have two vehicles, both will start a single instance of that script and both scripts will add to the global variable c in their for-loop, resulting in both vehicles get totally damaged in only 50 seconds each.

This is by the way called "a sideeffect of the script damageScript.sqf". Normally you don't want sideeffects that are caused by using global variables everywhere. The correct script version would look like this

 

params["_vehicle"];

private _c = 0; //Local countvariable -> exists in every instance of this script seperately

for[{_c = 0},{_c<100},{_c=_c+1}] do {
	_vehicle setdamage _c*0.01;
    sleep 1;
};

Now both scripts have their own counting variable in their own scopes and when added to two or more vehicles, every vehicle will get destroyed in 100 seconds.

 

Hope this helps and is not too confusing.

  • Like 1

Share this post


Link to post
Share on other sites

THANK YOU so very much NeoArmageddon.

Would I be correct to say;

     The underscore prefix signifies a local variable.

     A local variable is one that only works within the piece of code it currently occupies...sort of like being the contents of a specific units backpack (inside the braces).

     And a global variable is one that works anywhere...throughout the mission file...such as the content of every backpack in a group.

And...

     While it's possible to change either of them from, for example, within a units init field, it's best (but not required) to use local variables there and work global variables outside of it due to the possibility of multiple init fields changing the same global variable anywhere throughout the mission. That is, I can put a scope into my snipers backpack (_scope1 (local))...but not in everyone else's pack (scope1 (global)).

 

Again, thanks for the quick and generous reply.

I have a bit of experimenting to do, but I think I understand it a bit better now.

Share this post


Link to post
Share on other sites

You are absolutly right, but to make it absolutly clear with the init field:

 

private _weapon = primaryweapon this;
this removeweapon _weapon;

This will query the primary weapon of a player, store it in a local variable _weapon and will remove it in the next line. After the init is run, the _weapon variable is kinda thrown away.

 

Same example:

unitsWeapon = primaryweapon this;
this removeweapon unitsWeapon;

This does the same, but this time unitsWeapon is global. If now several units run this piece of code at the same time it could happen that, for example, a sniper runs this code, and unitsWeapon is filled with the type of his rifle. Right after it, before the next line is reached, another unit runs its init and the unitsWeapon variable is globally filled with this units weapon. When the init of the sniper comes to his line, it will try to remove the weapontype of the second unit, maybe a machine gun, which will obviously fail. Now you sit there in your mission and wonder "When I only have one unit, everything works, but when I add another one, the first one keeps his rifle.... strange".

 

But enough bashing of global variables, here an example where you actually want to use global variables.

Sometimes, mostly in multiplayermissions you want to know the group the players are in. You can of course name the player units like "p1,p2,p3,p4,..." and everytime you need the group you can just type group p1; This is obviously a problem when p1 is not occupied by a player in MP. You could now check everytime which players are connected, but it quickly gets hard to read and confusing. Instead you can add this to every possible players init:

playerGroup = group this;

playerGroup is a global variable and when the init field of one of the players is run, their actual group is stored in the variable. When another player, maybe he connects later, runs the unit, it is executed again, but jusst filled with the same value.

I have to add that nowadays I assume setVariable (especially example 3 on that bikipage) should be used for setting global variables in multiplayer.

 

Oh and when you want to sync trigger, waypoints, etc, globalvariables are a good way (or even needed): Place two triggers, one is activated when the player is detected by the enemy and sets "enemyalert = true;". In the second trigger you replace the condition "this" with "enemyalert" and set maybe a timeout and an effect for an alarm. Now, when the player is detected, it will set the global variable true, the other trigger will fire, because his condition (the variable being true) is fullfilled, it will wait the timeout and sound an alarm. Another interesting idea here: Give a speaker pole a name, like "pole" and make the second triggers condition  "enemyalert && alive pole". Now the alarm will only sound, if the pole with the loudspeakers is still functional. You can than go ahead and add another global variable to the second trigger, like "alarmsounded = true" and make this a condition for some waiting enemy reinforcements waypoints. The result: When you are detected an alarm sounds and enemy troops are coming your way except you destroy the speaker pole before you get detected.

 

Share this post


Link to post
Share on other sites

Thanks m'frend.

This will help a lot.

I'm trying to learn things a little at a time so I don't get lost in a jungle of new info.

Now I have some new things to try out.

Thank you.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×