Jump to content
Sign in to follow this  
Tankbuster

getVariable and setVariable. What are they for?

Recommended Posts

As an old school programmer, I struggled a little with OOL but have made some progress, but now I see that using getVariable and setVariable, we can store variables on objects.

So, my question is, and please don't link me to the biki page which I've read, what is object variable space and why would one use it over global or public variables?

While we're on the subject of scope, I assume a client can use getVariable to retrieve the value of a variable from an object, work with it, and put it back using setVariable, then another client (or even the server) can come along and see the new value? Is this what these functions are all about? Getting around the tricky bits of locality?

Is there even more to it than this? What else can they do?

Share this post


Link to post
Share on other sites

You use object variables to store values pertaining the object.

Say you create a weapon overheating system. Why would you create many global variables for each unit,

with probably overhead of determining dynamic variable names and what not, while you can just store the overheating value on the units themselves?

You could also create a hash (most basic representation: 2 arrays, one with keys, and one with values), but that's all overhead and are rather a 'workaround' for when more advanced coding is unavailable, like in OFP :D

Object Variables allow you to more logically store the information state for various objects, and also use a namespace that gets deleted when the object in question gets deleted.

Just like global variables, it's up to you to make them public, thats what the third value is for: _object setVariable ["someVar", _someValue, true];

If you need to make it public, depends on your requirements / intentions.

Edited by Sickboy
  • Thanks 1

Share this post


Link to post
Share on other sites

You pretty much nailed it.

Why is it better than a public or a global variable? Well a global variable takes up a name in the namespace. Also you are bound to this global variable for multiple scripts or they have to have their own variable which is messy.

By storing the information on the object your logic becomes much simpler. Say you want to store how much money an object has. To store this in a global variable would require a global variable per object. Instead you know can store the amount of money "on" the object using setVariable and later retrieve it using getVariable.

If you set the third optional parameter, "public", to true then the new value of this "key" on the object would be sent to others so if they getVariable they will see the same value. Guess you could say this help deal with the locality issue, saving you from using publicVariable and a global variable.

Just be aware that when the object is destroyed/dead then the variable space is cleared shortly after. Meaning don't store information that you want to access after the unit is dead.

Share this post


Link to post
Share on other sites

Storing in an object also clears the variables when the object is gone, thus saving space, no?

And of course using getvariable/setvariable is so much easier than any other alternative.

Share this post


Link to post
Share on other sites

That's excellent, guys. Thank you all.

So it makes sense to use these when I'm adding an identical feature to many units, which is exactly what I want to do.

Sicky's example was particularly helpful.

Share this post


Link to post
Share on other sites

All variables "live" in a variable space and are represented by a string identifier/name, type and value in Arma.

Where a variable is stored depends on just where you need it (missionNamespace, aka global variables, local variables, uiNamespace, profileNamespace (ToH), object variable space).

Adding it to an object gives you a little bit OO feeling and makes it easier to access a variable without extra array lookup workarounds. In the end the engine is still using a mechanism to find the variable string/name somwhere in memory to access its value and type.

Xeno

Share this post


Link to post
Share on other sites
Where a variable is stored depends on just where you need it (missionNamespace, aka global variables, local variables, uiNamespace, profileNamespace (ToH), object variable space).

That makes it clearer - knowing not just the how, but also the why. Many thanks.

Share this post


Link to post
Share on other sites

This isn't working as expected...

I'm putting a variable and a value onto an object, but can't get it back out again.

Apologies in advance for the masses of remarked out code - this is a test mission and when it works, it'll be plugged back into a larger mission.

sleep 2;
hint "init.sqf2327";
fnc_bigbombtrigger = compile preProcessFileLineNumbers "fnc_bigbombtrigger.sqf";
fnc_smallbombtrigger = compile preProcessFileLineNumbers "fnc_smallbombtrigger.sqf";
sleep 1;
_t_smalliedcount = 1;

ied1pos = getMarkerPos "marker1";
_clutterclass = "Barrel1";
_clutterobj = createVehicle [_clutterclass, ied1pos, [], 0,"NONE"];
//_clutterobj enableSimulation false;
_realpos = getPos _clutterobj; // createvehicle sometimes moves it's object away from other opbjects to prevent clipping. get new position
_t_xpos = _realpos select 0;
_t_ypos = _realpos select 1;
//iedobj = createVehicle ["BAF_ied_v3", [_t_xpos, _t_ypos], [], 0, "CAN_COLLIDE"];
_clutterobj setVariable ["iedactive", 1, true];// 1 = active| 0 = inactive 
diag_log format ["small ied create index %1, xpos %2, y pos %3", _t_smalliedcount, _t_xpos, _t_ypos];
_t_iedtrigger = createTrigger ["EmptyDetector", [_t_xpos, _t_ypos]];
_t_iedtrigger setTriggerArea[3, 3, 0, false];
_t_iedtrigger setTriggerActivation["ANY", "PRESENT", false];
_t_iedtrigger setTriggerText "_t_iedtrigger";
_t_strinf = format ["_tankysmallied%1 = createVehicle [""Goat02_EP1"", [%2,%3], [], 0, ""NONE""];",_t_smalliedcount,_t_xpos,_t_ypos];
//hint format ["clutterobject ied active variable = %1", _clutterobj getVariable "iedactive"];
_iedactive = _clutterobj getVariable "iedactive";
//hint  format ["_iedactive = %1, and typename = %2", _iedactive, typeName _iedactive];
diag_log format [" typename getvar  = %1", typeName (_clutterobject getVariable 'iedactive')];
diag_log format [" tempvariable typename = %1", typeName  _iedactive];
_t_iedtrigger setTriggerStatements ["vehicle player in thisList && player selectionPosition 'Neck' select 2 > 0.5 && ( _iedactive == '1') && !(vehicle player isKindOf 'air')", _t_strinf, ""];
//_t_iedtrigger setTriggerStatements ["vehicle player in thisList && player selectionPosition 'Neck' select 2 > 0.5 && !(vehicle player isKindOf 'air')", _t_strinf, ""];
_t_nul = _clutterobj addEventHandler ["hit", "if (_this select 2 > 50) then { hint 'pow!' }"];
//_t_nul = _clutterobj addEventHandler ["hit", "if (_this select 2 > 50) then {nul = execVM 'deltrig.sqf'}"];
//_t_smalliedcount = _t_smalliedcount -1;
//iedarray set [_iedarraycounter, _t_iedtrigger];// add the trigger to an array for later removal
//_iedarraycounter = _iedarraycounter + 1;

The diag_log seen is

"small ied create index 1, xpos 3440.91, y pos 3622.05"
" typename getvar  = "
" tempvariable typename = SCALAR"

so the setvariable is to store if the IED is active. The plan being if it's gone off, or been defused or disrupted, that flag will be zero, otherwise, it's one.

I've tried many different variants, but the 3rd condition in the setTriggerStatements never seems to be satisfied, even when I've setvar'd it to 1. I've tried testing it as 1 and '1'.

getVariable may be retrieving it as a string, rather than a number and I've also tried doing the getVariable before the settriggerstatements and putting it into a temp variable, but that didn't work either.

Help would be really appreciated.

Share this post


Link to post
Share on other sites

Your hint doesn't work because you wrote: _clutterobject

You trigger doesn't work because you entered a local variable _iedactive. Think of it from the trigger's perspective. The code the trigger uses "kind works like execVM/spawn" meaning it starts in a separate completely fresh new scope. It has no idea what _iedactive is. You need to "inject" that like you do with _tankysmallied%1,

Share this post


Link to post
Share on other sites
You trigger doesn't work because you entered a local variable _iedactive. ,

_iedactive is a temporary variable created by

_iedactive = _clutterobj getVariable "iedactive";

this was done for testing purposes, but as you can see when I hint it's typeName further down, it's reported as SCALAR.

But you're saying I can't use an object name with an underscore for the clutterobject?

Am I not allowed to do this in general?

Does the underscore on _clutterobject make it local in some way, in the same way it does for variables?

Share this post


Link to post
Share on other sites

One of the things I am saying it that _clutterobject is the wrong name since you use _clutterobj everywhere else.

But you're saying I can't use an object name with an underscore for the clutterobject?

Am I not allowed to do this in general?

Does the underscore on _clutterobject make it local in some way, in the same way it does for variables?

Not sure what you mean since 'clutterobj(ect)' is a variable just like '_iedactive' so it is local just like every other variable starting with an underscore.

However, you can't use local variables for triggers, because they were created in your script and is local there and not in the trigger. If you to want dynamically created triggers your best bet is to create a unique global name

for your clutterobjects and IED's if they are to be used in a trigger condition.

Only thing i'm not sure of is why you get the SCALAR result in the hint. I have to theories:

I tried to setVariable on an object created by createVehicle where I should have used createUnit. Result was that setVariable didn't work.

I've found that the baf-ied objects in some ways are different from ordinary vehicles. Can't remember the exact issue though.

Share this post


Link to post
Share on other sites
One of the things I am saying it that _clutterobject is the wrong name since you use _clutterobj everywhere else.

.

Do me a favour mate. Come to my house and uninstall all my file editing applications. It'll save you a lot of time and me a lot of embarrassment ! :D

---------- Post added at 11:52 AM ---------- Previous post was at 11:46 AM ----------

However, you can't use local variables for triggers, because they were created in your script and is local there and not in the trigger. If you to want dynamically created triggers your best bet is to create a unique global name

for your clutterobjects and IED's if they are to be used in a trigger condition.

.

Yeah, this is the problem. This IED is created more than once on the server so the name needs to be unique. THAT was why I was hoping to get away with local names, so they didn't interfere with each other.

At the simplest level, it works fine when it was just creating an object and a trigger. As the project matures, my simplistic approach is proving to be too inflexible. As you say, I can use the same method where I did (_tankysmallied%1) and then make it global.

Share this post


Link to post
Share on other sites

I use some functions to help deal with such situations:

#include "\x\cba\addons\main\script_macros_mission.hpp"

#define PREFIX MF
#define COMPONENT functions

/*
   ["VARNAME",VALUE] - compile format tucked away into this function.
*/
FUNCMAIN(AssignToVarName) = {
   //(_this select 1) call (compile format ["%1 = _this", _this select 0]);
   missionNamespace setVariable [_this select 0,_this select 1];
};

if (isNil QGVAR(UniqNum)) then {
   GVAR(UniqNum) = 0;
};

FUNCMAIN(GetUniqNumber) = {
   INC(GVAR(UniqNum));
   GVAR(UniqNum)
};

FUNCMAIN(GetUniqueName) = {  
   DEFAULT_PARAM(0,_prefix,"uniq_a");
   TRACE_1("GetUniqueName",_prefix);
   private ["_result"];
   _result = format ["%1_%2", _prefix, call FUNCMAIN(GetUniqNumber)];
   _result
};

FUNCMAIN(GetUniqueVarName) = {
   DEFAULT_PARAM(0,_prefix,"uniq_b");
   TRACE_1("GetUniqueVarName",_prefix);
   private ["_name"];
   _name = format ["%1_%2", _prefix, floor random 10000];
   while {!isNil {call compile _name}} do {
       _name = format ["%1_%2", _prefix, floor random 10000];
   };
   _name
};

So you could do stuff like:

_iedObject = SOMETHING;
_iedVarName = ["ied"] call MF_fnc_GetUniqueVarName;
//Set this _iedVarName to _iedObject
[_iedVarName, _iedObject] call MF_fnc_AssignToVarName;
//Put into some trigger:
_myNewTrigger setTriggerStatements ["this", format ["deleteVehicle %1;", _iedVarName], ""];

Share this post


Link to post
Share on other sites

I don't use CBA, but that's interesting to see. It gives me another option, thanks.

Share this post


Link to post
Share on other sites
I don't use CBA, but that's interesting to see. It gives me another option, thanks.

CBA requirement can easily be removed.

/*
   ["VARNAME",VALUE] - compile format tucked away into this function.
*/
AssignToVarName = {
   //(_this select 1) call (compile format ["%1 = _this", _this select 0]);
   missionNamespace setVariable [_this select 0,_this select 1];
};

if (isNil "UniqNum") then {
   UniqNum = 0;
};

GetUniqNumber = {
UniqNum = UniqNum + 1;
UniqNum
};

GetUniqueName = {  
   private ["_prefix","_result"];
_prefix = if (count _this > 0) then {_this select 0} else {"uniq_a"};
   _result = format ["%1_%2", _prefix, [] call GetUniqNumber];
   _result
};

GetUniqueVarName = {
private ["_name","_prefix"];
_prefix = if (count _this > 0) then {_this select 0} else {"uniq_b"};
   _name = format ["%1_%2", _prefix, floor random 10000];
   while {!isNil {call compile _name}} do {
       _name = format ["%1_%2", _prefix, floor random 10000];
   };
   _name
}; 

Share this post


Link to post
Share on other sites

Going back a few steps to see why I can't getVariable reliably.

I've removed the underscore from clutterobj and fixed the bad variable name.

tempvariable = clutterobj getVariable "iedactive";
diag_log format [" tempvariable raw = %1", tempvariable];
diag_log format [" typename getvar  = %1", typeName (clutterobj getVariable 'iedactive')];
diag_log format [" str tempvariable = %1" , str tempvariable];
diag_log format [" tempvariable typename = %1", typeName  tempvariable];

diag_log output,

" tempvariable raw = 1"
" typename getvar  = SCALAR"
" str tempvariable = 1"
" tempvariable typename = SCALAR"

the str of the tempvariable is 1, yet it's typename is scalar?

What does SCALAR mean in this context?

Edited by Tankbuster

Share this post


Link to post
Share on other sites

SCALAR means that it is a number.

You can also get these types in other situations without typename. An example would be where it cannot find the correct value:

hint format ["D: %1", undefined_var]; //Hints D: any
hint format ["D: %1", toArray undefined_var]; //Hints D: array
hint format ["D: %1", undefined_var + 1]; //Hints D: scalar

Basically it tries to infer what the result would have been although it couldn't give you the result.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites

Why use "unique" variable names? Why not instead use an array and use unique indexes? Should be easier to keep track of and more efficient...

Share this post


Link to post
Share on other sites
Why use "unique" variable names? Why not instead use an array and use unique indexes? Should be easier to keep track of and more efficient...

What I posted was something I wrote a while ago where I needed unique names and then only a few (array indexing was not very feasibly at all). You are right he could store it all in an array instead and is probably the way to go.

However, don't buy the efficiency argument. Storage-wise we cannot be sure what uses most memory, a global variable or extra item in the array. The length of the variable might use more space than just the number - however we can't be sure unless someone who knows how the script engine is implemented enlighten us. What about speed? Well again we do not know the implementation but you would need to retrieve the item from the array - meaning accessing the array variable and selecting the value versus just accessing a variable. With proper tagging collisions is really not an issue.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites

Storage is harder to judge, but I would bet processing indexing in an array is faster than string manipulation required to determine the variable name. It's still just speculation, though, but I think it's a reasonable one.

Anyway, the main reason is easiness of use, not to mention easiness of debug.

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
Sign in to follow this  

×