Forum Jump :

Author Message


Posts: 1
Rank:


Level: Member

Country: hr
Location:
Occupation:
Age:
In-game name:

 
#1 Posted at 2019-10-19 13:02        
     
Hello, Armaholic.

I've been working on an ambitious and scope-y mission project for about a year now and I've encountered a very specific bug several times, causing me to lose some hours attempting to fix the issue (because I forget it exists every time).

It concerns saving a modified marker or unit position into an array. I'll explain it with an example, all the variables are declared as private "xyz" at the top of the file before they're used and outside any if's, while's and for's, but I'm taking them out for readability:

// Position is actually a getMarkerPos passed into the function
// Range is e.g. [100, 100], same format as getMarkerSize
_Position = _this select 0;
_Range = _this select 1;

_AllPositions = [];
_OffsetRotation = 0;

_RangeX = _Range select 0;
_RangeY = _Range select 1;

for[{_i = 0}, {_i < 72}, {_i = _i + 1}] do {
    _WaypointPos = _Position;
	
    _WaypointPos set[0, ((_WaypointPos select 0) + _RangeX * (sin _OffsetRotation))];
    _WaypointPos set[1, ((_WaypointPos select 1) + _RangeY * (cos _OffsetRotation))];

    _AllPositions = _AllPositions + [_WaypointPos];
    _OffsetRotation = _OffsetRotation + 5;
};
So, what this code does is basically go around at the edge of a circle/ellipse by 5 degrees of rotation and saves each position into an array. This is done 71 times, so it rotates a full 360 - or rather 355, because 0=360 and it's already in there.

There are two problems with this. If I do _WaypointPos = _Position; and then use set on the waypoint position, the value outside the scope of this script file that is passed as parameter (_Position) will change when the execution exits this particular script and continues in the one that called it. This is regardless of the fact that _Position is declared as a private variable inside this script file.

The second problem is that after 71 executions of this loop, the _AllPositions array will contain only the latest value (the one where _OffsetRotation was set to 355), repeated 71 times. This would mean that, under the hood, _WaypointPos is not a position or an array, but instead a pointer - which I assume was done to save memory or something.

The workaround for this problem is the following:
// Position is actually a getMarkerPos passed into the function
// Range is e.g. [100, 100], same format as getMarkerSize
_Position = _this select 0;
_Range = _this select 1;

_WaypointPos = [0, 0, 0];
_AllPositions = [];
_OffsetRotation = 0;

_RangeX = _Range select 0;
_RangeY = _Range select 1;

for[{_i = 0}, {_i < 72}, {_i = _i + 1}] do {
	_WaypointPos set[0, (_Position select 0)];
	_WaypointPos set[1, (_Position select 1)];
	_WaypointPos set[2, (_Position select 2)];
	
	_WaypointPos set[0, ((_WaypointPos select 0) + _RangeX * (sin _OffsetRotation))];
	_WaypointPos set[1, ((_WaypointPos select 1) + _RangeY * (cos _OffsetRotation))];

	_AllPositions = _AllPositions + [[_WaypointPos select 0, _WaypointPos select 1, _WaypointPos select 2]];
	_OffsetRotation = _OffsetRotation + 5;
};

The following code (I'm assuming, but I haven't tested it) would not have this problem:
{
    _AllPositions = _AllPositions + [getPos _x];
} foreach (units group player);
because I'm assuming the getPos function cannot return a pointer to an array which would mess with the unit's position, and instead just spits out the actual array.

Now, what I'm looking for is not a solution to this problem (because I kinda have it here), but rather an explanation of why this would happen and if there's a more elegant way of doing this type of algorithm or any other kind of position-into-array code, so I can make my codebase less messy. It also wouldn't hurt if this info was added to the Wiki, if it's not already there and I just glanced over it. Or maybe I'm just missing something here?

Cheers.

This post was edited by Tinkerton (2019-10-19 13:24, 501 days ago)