Jump to content
cklymowsky

How can I improve efficiency of "onEachFrame" with BIS_fnc_addStackedEventHandler ?

Recommended Posts

With additional units on the field the FPS starts to drop.

 

I would like to have the script only calculate the _distance every second instead of on every frame to see how much it improves FBS.

 

Any ideas?

 

I have tried the following with no luck

private ["_randomSectorDestination","_revealTime","_enemyArray","_addNew"];
_enemyArray = [];  
_distance = 0;
_displayName = "";
_picture = "";
_icon = "";
_revealTime = 30;
_run = missionNamespace setVariable ["Intel",0];
_timer = missionNamespace setVariable ["Timer",time];

fn_Draw3DNEW = {
	_enemyArray = [];  
	_distance = 0;
	_displayName = "";
	_picture = "";
	_icon = "";
	_run = missionNamespace getVariable ["Intel",0];
	_timer = missionNamespace getVariable ["Timer",0];

	{if  (((side _x) == AIside)  && (isFormationLeader _x)) then {_enemyArray pushBack _x}} forEach allUnits+vehicles+allUnitsUAV;		
	
		{
				_text = "";			
			if ( floor(time-_timer) == _run ) then {
			
		
				_run = missionNamespace setVariable ["Intel",_run + 5];			


				private ["_private", "_displayName", "_picture", "_icon", "_text", "_distance", "_leaderDestination","_enemyArray"];
				if (vehicle _x != _x) then {_x = vehicle _x};
				_distance = round (_x distance player);
				_displayName = getText (configfile >> "CfgVehicles" >> typeOf _x >> "displayName");
			};		
				
				_text = str(parseText format["%1 men %2m",count units group _x, (str _distance)]);

				_icon = "";
				if ( (_x isKindOf "LandVehicle") or (_x isKindOf "Air") or (_x isKindOf "Ship") ) then {
					_picture = getText (configfile >> "CfgVehicles" >> typeOf _x  >> "picture");
				} else {
					_icon =  getText (configFile >> "CfgVehicles" >> typeOf _x >> "Icon");
					_picture = getText (configFile >> "CfgVehicleIcons" >> _icon);  
				};
			

			
			drawIcon3D [
				_picture,
				[1,0,0,1],
				[(visiblePosition _x) select 0,(visiblePosition _x) select 1,((visiblePosition _x) select 2) + 10],
				0.8,
				0.5,
				0,
				_text,
				0,
				0.03,
				"PuristaMedium"
			];
			player reveal [_x, 4];
			{[_x, 1] showWaypoint "ALWAYS" } forEach allGroups;
		} forEach _enemyArray; 
	
	hint format ["_run: %1 \n floor(time-_timer): %2 \n floor(time-_timer) == _run: %3", _run, floor(time-_timer), floor(time-_timer) == _run];
};

_addNew = ["BIS_idNEW", "onEachFrame", "fn_Draw3DNEW"] call BIS_fnc_addStackedEventHandler;
sleep (_revealTime);
["BIS_idNEW", "onEachFrame", "fn_Draw3DNEW"] call BIS_fnc_removeStackedEventHandler;
_run = missionNamespace setVariable ["Intel",nil];
_timer = missionNamespace setVariable ["Intel",nil];

thanks in advance...

Share this post


Link to post
Share on other sites

Well if you did not copy this from somewhere else then you know how to script and should be able to turn this into a per second cycle easily. Few notes though:

  • Script could do with some optimizing. Note that 'reveal' on each frame and even every second is quite expensive.
  • No sure why you are declaring variables at the beginning of the script as they are not used elsewhere
  • What is 'AIside'? Not declared anywhere unless it is a new BIS function I am un aware off 

Share this post


Link to post
Share on other sites

My practice when working with onEachFrame and similar is to move the data crunching into a separate scripted loop (like a while do for instance) and I let the onEachFrame code read from a common data set all required parameters. It sacrifices a bit of memory but doesn't clog up the unscheduled code with expensive operations. It also gives easy control over the update rate through sleep and waitUntil etc. 

  • Like 1

Share this post


Link to post
Share on other sites

Since you are calling drawIcon3D the code should go to "Draw3D" event handler instead.

 

_drawEvent = addMissionEventHandler ["Draw3D", 
{ 
 // drawIcon3D code here 
} ];

sleep (_revealTime);

removeMissionEventHandler ["Draw3D", _drawEvent];

 

But I'm not sure can you use BIS_fnc_addStackedEventHandler to make new Draw3D events. The above code should however be all you need.

 

As for performance you should create separate loop that handles the units. putting it all in eachframe loop will indeed take a lot from the CPU .

 

Edited by gc8
Added some more code

Share this post


Link to post
Share on other sites

Hi All,

 

I went with the following changes and it does improve FPS. Let me know if you see any other improvements.

 

Thanks in advance.

 

Quote

private ["_revealTime","_addNew","_enemyArray","_displayName", "_picture", "_icon", "_text", "_distance"];
_revealTime = 60;
_enemyArray = [];  
_distance = 0;
_displayName = "";
_picture = "";
_icon = "";
_timer = time; 
_text = "";	
enemyArray = [];

while {floor(time -_timer) <= _revealTime} do {
	_enemyArray = [];  
	{if  ((((side _x) == AIside)  && (isFormationLeader _x)) or (((side _x) == AIside)  && (vehicle _x != _x))) then {_enemyArray pushBackUnique (vehicle _x)}} forEach allUnits;		
	enemyArray = [];
	{	
			//if (vehicle _x != _x) then {_x = vehicle _x};
			_distance = round (_x distance player);
			_displayName = getText (configfile >> "CfgVehicles" >> typeOf _x >> "displayName");
			_text = str(parseText format["%1 men %2m",count units group _x, (str _distance)]);
			if ( (_x isKindOf "LandVehicle") or (_x isKindOf "Air") or (_x isKindOf "Ship") ) then {
				_picture = getText (configfile >> "CfgVehicles" >> typeOf _x  >> "picture");
				_text = str(parseText format["%1m",(str _distance)]);
			} else {
				_icon =  getText (configFile >> "CfgVehicles" >> typeOf _x >> "Icon");
				_picture = getText (configFile >> "CfgVehicleIcons" >> _icon);  
			};
		player reveal [_x, 4];
		enemyArray pushBack [_x,_picture,_text];
		sleep 0.05;
	} forEach _enemyArray; 
	sleep 0.5;

	_addNew = ["BIS_idNEW", "onEachFrame",
		{
			_unit = objNull;
			_picture = "";
			_text = "";
			_enemyArray = _this select 0;
			{
				_unit = _x select 0;
				_picture = _x select 1;
				_text = _x select 2;
				drawIcon3D [
					_picture,
					[1,0,0,1],
					[(visiblePosition _unit) select 0,(visiblePosition _unit) select 1,((visiblePosition _unit) select 2) + 10],
					0.8,
					0.5,
					0,
					_text,
					0,
					0.03,
					"PuristaMedium"
				];
			} forEach (_enemyArray); 
		},
	[enemyArray]] call BIS_fnc_addStackedEventHandler;
};

["BIS_idNEW", "onEachFrame"] call BIS_fnc_removeStackedEventHandler;

 

 

Share this post


Link to post
Share on other sites

Try changing:

[(visiblePosition _unit) select 0,(visiblePosition _unit) select 1,((visiblePosition _unit) select 2) + 10]

to

(visiblePosition _unit) vectorAdd [0,0,10]

 

Then,

_enemyArray = [];  
{if  ((((side _x) == AIside)  && (isFormationLeader _x)) or (((side _x) == AIside)  && (vehicle _x != _x))) then {_enemyArray pushBackUnique (vehicle _x)}} forEach allUnits;	

to (using the select command)

_enemyArray = allUnits select {((side _x isEqualTo AIside) && (isFormationLeader _x)) or ((side _x isEqualTo AIside) && (vehicle _x != _x))} apply {vehicle _x};


Additionally you are using str when format already returns a string. parseText parses structured text which the strings used do not contain (therefore it's not required):

_text = str(parseText format["%1m",(str _distance)]);

to

_text = format ["%1m", _distance];

 

Additionally, you are using pushBack when you could use the apply command:

 

Spoiler

From:


	enemyArray = [];
	{	
			//if (vehicle _x != _x) then {_x = vehicle _x};
			_distance = round (_x distance player);
			_displayName = getText (configfile >> "CfgVehicles" >> typeOf _x >> "displayName");
			_text = str(parseText format["%1 men %2m",count units group _x, (str _distance)]);
			if ( (_x isKindOf "LandVehicle") or (_x isKindOf "Air") or (_x isKindOf "Ship") ) then {
				_picture = getText (configfile >> "CfgVehicles" >> typeOf _x  >> "picture");
				_text = str(parseText format["%1m",(str _distance)]);
			} else {
				_icon =  getText (configFile >> "CfgVehicles" >> typeOf _x >> "Icon");
				_picture = getText (configFile >> "CfgVehicleIcons" >> _icon);  
			};
		player reveal [_x, 4];
		enemyArray pushBack [_x,_picture,_text];
		sleep 0.05;
	} forEach _enemyArray;

 

to:


	enemyArray = _enemyArray apply {
		_distance = round (_x distance player);
		_displayName = getText (configFile >> "CfgVehicles" >> typeOf _x >> "displayName");
		_text = format ["%1 men %2m", count units group _x, _distance];
		
		if ((_x isKindOf "LandVehicle") or (_x isKindOf "Air") or (_x isKindOf "Ship")) then {
			_picture = getText (configFile >> "CfgVehicles" >> typeOf _x  >> "picture");
			_text = format ["%1m", _distance];
		} else {
			_icon =  getText (configFile >> "CfgVehicles" >> typeOf _x >> "Icon");
			_picture = getText (configFile >> "CfgVehicleIcons" >> _icon);  
		};
		
		player reveal [_x, 4];
		sleep 0.05;
		[_x,_picture,_text]
	};

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

https://community.bistudio.com/wiki/Code_Optimisation

 

Here is a wiki page with lots of common optimizations.

But remember to always think about what you are doing. shortening your variable names from _veryDescriptiveVariableName to _v1 might give you a 0.0001ms performance improvement. But readability is more important if you ever wanna touch your code again.

Share this post


Link to post
Share on other sites
On 3/30/2018 at 8:32 AM, whiztler said:

Well if you did not copy this from somewhere else then you know how to script and should be able to turn this into a per second cycle easily. Few notes though:

  • Script could do with some optimizing. Note that 'reveal' on each frame and even every second is quite expensive.
  • No sure why you are declaring variables at the beginning of the script as they are not used elsewhere
  • What is 'AIside'? Not declared anywhere unless it is a new BIS function I am un aware off 

 

Any numbers on your reveal assumption? Revealing 100 units to the player takes around 0.0823ms, since one frame takes 16.6667ms to run at 60fps there's plenty of overhead.

Same goes for "player distance [0,0,0]" which only takes 0.0008ms.

 

@cklymowsky:

Optimization is only needed if you notice an fps drop.

Try running the mission without the script, if the fps still drops it's most likely not the script.

To get more performance out of your script it's good practice to look at each single command used and find possible faster alternatives.

 

You are already using sleep 0.05 in the forEach loop, which equals 50ms my guess is that the draw event is slowing things down.

From the looks of it you always add a stacked eventhandler every 0.5s for every unit inside the enemyArray, since both _enemyArray and enemyArray reset to [] at beginning of the while loop. You need to check if the units are already being handled by the onEachFrame either with making sure the enemyArray doesn't reset every iteration or by using setVariable/getVariable.

 

Cheers

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

×