Jump to content
sarogahtyp

[SOLVED] Predict Target Position - Need some help with code optimization

Recommended Posts

Hey guys,

with ur help in this Topic I managed to write a script which can predict a targets Position in the moment when ur current magazines ammo is reaching it.
I ll post an example Mission within the next days.

And here it is:

/*
    Author: Sarogahtyp
    File: pre_tar_pos.sqf

    Description:
    Calculates the postion of a moving target at the time the bullet of shooters current magazine is arriving at target.
    Works with most self propelled weapons, too. Works with vehicles. Tested without MODs only - vanilla style.

    Version 0.0 Pre-Alpha

    Known issues:
    Not working with RPG but will be fixed soon.

    Parameter(s):
    0: target - the object the shooter wants to shoot at - default is the nearest target to shooters crosshair
    1: shooter - the one who wants to shoot at target - default is the player

    Returns:
    position - position of the target when the current bullet will reach it
*/

params [["_target", cursorTarget], ["_shooter", player],
["_speed_coef_muzzle", 1], ["_flight_dist", 0], "_weapon", "_magazine", "_init_speed_gun", "_init_speed_mag", 
"_ammo_class", "_air_friction", "_items", "_init_time", "_max_speed", "_thrust", "_thrust_time", "_rel_speed", 
["_air_fric_fact", -0.002], ["_burned_out", false], ["_prop_started", false], ["_burn_time", 0], ["_last_dist", 0], 
["_last_time", 0], ["_time_diff", 0], "_distance", "_dist_step", "_last_speed", "_last_accel", "_new_dist", "_term", 
"_new_time", "_new_speed", "_new_accel"]; 

_shooter = vehicle _shooter;

_weapon = currentWeapon _shooter;
_magazine = currentMagazine _shooter;

_init_speed_gun = getNumber(configfile >> "CfgWeapons" >> _weapon >> "initSpeed");
_init_speed_mag = getNumber(configfile >> "CfgMagazines" >> _magazine >> "initSpeed");
_ammo_class = getText(configFile >> "CfgMagazines" >> _magazine >> "ammo");

_air_friction = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "airFriction");

_items = _shooter weaponAccessories _weapon;

_init_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "initTime");
_max_speed = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "maxSpeed");
_thrust = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrust");
_thrust_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrustTime");;

_rel_speed = ((velocity _target) vectorDiff (velocity _shooter));

if((_init_speed_gun) < 0) then
{
 _init_speed_gun = -1 * init_speed_gun * _init_speed_mag;
};

if((_init_speed_gun) == 0) then
{
 _init_speed_gun = _init_speed_mag;
};

{
 if((_x find "muzzle") > -1) then
 {
  _temp_coef = getNumber(configfile >> "CfgWeapons" >> _x >> "ItemInfo" >> "MagazineCoef" >> "initSpeed");
  _speed_coef_muzzle = if (_temp_coef !=0) then {_temp_coef}else{1};
 }; 
} forEach _items;

_init_speed_gun = _init_speed_gun * _speed_coef_muzzle;

if((vectorMagnitude _rel_speed) > 0) exitWith
{
 _distance = _target distance _shooter;

 if(_air_friction > 0) then {_air_friction = _air_friction * _air_fric_fact;};

 _dist_step = _distance / (10 * diag_fps);

_last_speed = _init_speed_gun;
_last_accel = _air_friction * _init_speed_gun ^ 2;

while {_last_dist < _distance} do
{
 _new_dist = _last_dist + _dist_step;

 //calculate time from last position to actual position (_time_diff)
 if (_last_accel < 0) then // standard case - bullet is decelerated by airflow
 {
  _term = _last_speed / _last_accel;
  _time_diff = -1 * sqrt (_term ^ 2 + 2 * _dist_step / _last_accel) - _term;
 }
 else
 {
  if(_last_accel == 0) then  //maybe no value for air friction stored in config then we have just t=s/v
  {
   _time_diff = _dist_step / _last_speed;
  }
  else  // positive acceleration should happen if a propulsion is burning only
  {
   _term = _last_speed / _last_accel;
   _time_diff = sqrt (_term ^ 2 + 2 * _dist_step / _last_accel) - _term;
  };
 }; 

 _new_time = _time_diff + _last_time;
 _new_speed = _last_speed + _last_accel * _time_diff;

 if(_new_speed <= 0) exitWith {0};

 _new_accel = _air_friction * _new_speed ^ 2;  //calc air frictions influence to acceleration

//check if bullet is a rocket and if propulsion should accelerate
 if((_max_speed > 30) and (!_burned_out) and (_last_time > _init_time)) then  
 {
  if(!_prop_started) then
  {
   _prop_started = true;
   _burn_time = diag_tickTime + _thrust_time;
  }
  else
  {
   if(diag_tickTime > _burn_time) then
   {
    _burned_out = true;
   };
  };
  _new_accel = _new_accel + _thrust;
 };

 _last_dist = _new_dist;
 _last_time = _new_time;
 _last_speed = _new_speed;
 _last_accel = _new_accel;
};

((_target modelToWorld [0,0,1.2]) vectorAdd (_rel_speed vectorMultiply _last_time))
};

(_target modelToWorld [0,0,1.2])

Now I d like to optimize that script some more.

My first question:
Is the way I use params a good one?
I just put every local variable in it to ensure it is private.
The Problem I see is that someone could pass more than the 2 thought Parameters to the script. That would destroy the calculations. How can I avoid this?

Beside that question tell me ur thoughts about the script please and also if u think ist worth a release if its more optimized.
 
EDIT: this script is thought to be executed once each frame

EDIT: moving this to first post ...

there is an ammosets.txt file inside. u can use it to copy paste the content and "local exec" it in editor to test different infantry weapons.
when u start the mission u sit in a tigris and on ur left side there is a helicopter where u can aim at.
its a 2d editor mission.
i stripped the mission down a bit but it should work. if not just tell me plz.

https://www.dropbox.com/s/sxij9oeqf9f60lp/IR_laser_ranging.Altis.rar?dl=0

Edited by sarogahtyp
  • Like 2

Share this post


Link to post
Share on other sites

Well, IMHO, you should use params for the variables that are parameters and private for those that you just want private, from what I understand with params (could be wrong), is it paramtizes variables coming in AND then also privatizes them at the same time. It's safer for the details you already stated about possibly more input from the user.

But overall I'm not seeing anything too outstandish when in comes to non-optimal, if I'm making the correct assumption about config file references, most of this script is running in constant time (all the calculations), with some linear time (forEach loop and config file). Making this script at worst O(n), which isn't too bad considering what your're trying to accomplish, unless I'm missed something (3am and I did read quickly :p), so hopefully someone with some sleep and keen eyes, and maybe a bit further understanding of the backend of some of these functions can provide a more detailed answer.

EDIT: Yup, completely looked past the while loop, which atm, my brain cannot analyze the time complexity of that, especially via my phone...so...yea :). (I think it's still O(n) based on the condition...but idk anymore)

  • Like 2

Share this post


Link to post
Share on other sites

Thank u for ur statement idk if I got everything you said because Im not a native english Speaker (surly u already know :) but what I forgot to say is that this script is thought to be executed once each frame.

 

I thinhk I ll do those local variable Thing with private then if there is no way to forbid to pass more than the number of wanted Parameters with params.

 

Edit:

Could u please explain what u mean with this?

 

I think it's still O(n) based on the condition

 

 

thats what I did not get :-)

Edited by sarogahtyp

Share this post


Link to post
Share on other sites

Looks very clean written to me. However, I'd also suggest to clean up the params array at the beginning, only use params for target and shooter and set the rest to private. Additionally you could test if replacing forEach with count and bool is faster in the following part

if((_x find "muzzle") > -1) then
{
_temp_coef = getNumber(configfile >> "CfgWeapons" >> _x >> "ItemInfo" >> "MagazineCoef" >> "initSpeed");
_speed_coef_muzzle = if (_temp_coef !=0) then {_temp_coef}else{1};
// add bool here
}; 
} forEach _items;// replace with count
  • Like 2

Share this post


Link to post
Share on other sites

Sorry, computer science coursework coming out, O(n) basically is a nomenclature to specify how "fast" (efficient) a piece of code or functions runs based on all it's parts loops, calculations, etc.

The 'n' in this case is the number of variable "steps" it takes to get from _lastTime < _distance to _lastTime > _distance, so O(n) means that this function will run at a linear pace, dependent on 'n', the number of steps it takes to clear the while loop.

As far as on each frame, you'll honestly just have to see if it causes a heavy hit or not, it probably will.

  • Like 2

Share this post


Link to post
Share on other sites

As far as on each frame, you'll honestly just have to see if it causes a heavy hit or not, it probably will.

is there a good way to detect such heavy hit? actually the only thing I do is looking at the fps value at top left of my screen and I cant notice that its decreasing.

surely i can measure the time it runs on each frame but that time is specific for my own PC only and i think nobody can tell me if that time value is good, good enough or terrible bad...

so how can i find out if that piece of code is to heavy to run on each frame?

Share this post


Link to post
Share on other sites

FPS is probably one of the best identifiers of it, best way to absolutely ensure that it doesn't cause too much of a hit would be to play a mission/game with that running in the background, not just in the editor "testing" it, cause when it's just you and your script, most of the time the weight is transparent, but once you throw a mission and other scripts on top of that it may show to be more of a hit, and like you said it's dependent on the client's computer, as it is a local script.

 

What is the practical use for this script? I may be able to answer a bit clearer if I had some context as to why and what this is used for. I understand what it does, just need to know why you need it.

 

May as well throw the whole thing through code performance and see how fast it runs.

Share this post


Link to post
Share on other sites

thats easy to tell.

i draw a crosshair on the screen at the position where u have to shoot at to hit the desired moving target.

u can activate it by pressing L key and a crosshair is drawn at the predicted position. if u press T then u can lock that cursorTarget and the crosshair is drawn for that locked target as long as its alive or u unlock it.

I ll give u a dropbox link in just a moment.

Share this post


Link to post
Share on other sites

there is an ammosets.txt file inside. u can use it to copy paste the content and "local exec" it in editor to test different infantry weapons.

when u start the mission u sit in a tigris and on ur left side there is a helicopter where u can aim at.

its a 2d editor mission.

i stripped the mission down a bit but it should work. if not just tell me plz.

https://www.dropbox.com/s/sxij9oeqf9f60lp/IR_laser_ranging.Altis.rar?dl=0

Share this post


Link to post
Share on other sites

Ok, yea, then since it's more of an "on/off" type thing it really shouldn't be too bad of a hit, people may experience a bit of an FPS drop while in use, but they can simple deactivate it after a while, I thought you would be constantly tracking the cursor target of the player for the duration of the mission (or whatever).

 

I mean, look at ACE3 (if you've messed with it), they have a speed delimited (similar to a cruise control), anytime I activate that in my vehicle my FPS immediately takes a dump as I'm sure the function behind that is forcing the vehicle to maintain below a certain speed on each frame while the delimited is enabled, so hopefully your function doesn't have that much of an effect, but people do deal with the speed delimited FPS drop, therefore I think they can live if yours has a bit of a drop (if any :)).

 

Your description at the top of the function didn't give me that impression (and I've read zero of the other thread you linked), so I was probably missing a bit of context

Share this post


Link to post
Share on other sites

 

 

I mean, look at ACE3 (if you've messed with it), they have a speed delimited (similar to a cruise control), anytime I activate that in my vehicle my FPS immediately takes a dump as I'm sure the function behind that is forcing the vehicle to maintain below a certain speed on each frame while the delimited is enabled, so hopefully your function doesn't have that much of an effect, but people do deal with the speed delimited FPS drop, therefore I think they can live if yours has a bit of a drop (if any :)).

 

 

setVelocity in a loop works nice in the singleplayer VR dungeon they code in, in MP reality sets in and the setVelocity loop feels horrible

 

 

to OP

 

lots of use of getNumber/getText/getX from configfile. this is very heavy process.

 

maybe you can store this data to a namespace variable for more performance friendly runtime execution.

  • Like 2

Share this post


Link to post
Share on other sites

to OP

 

lots of use of getNumber/getText/getX from configfile. this is very heavy process.

 

maybe you can store this data to a namespace variable for more performance friendly runtime execution.

 

thank you, thats a thing I thought about and I ll do that.

Share this post


Link to post
Share on other sites

Okay guys I did as u said now. I did the private thing and I m running the config lookups only if the weapon is changed now.

 

The whole thing isnt too haevy I think.

My laptop has the minimal performance to run arma 3 (Intel I3 Core with Intel onboard graphics) and that script is running on 7 ms with non propelled ammo and 8 ms with self propelled ammo.

 

@revo I did not implement ur count advice because thats only running one time if the weapon was changed.

 

I split that in 2 scripts now and here they are:

 

pred_tar_pos.sqf

/*
    Author: Sarogahtyp
    File: pre_tar_pos.sqf

    Description:
    Calculates the postion of a moving target at the time the bullet of shooters current magazine is arriving at target.
    Works with most self propelled weapons, too. Works with vehicles. Tested without MODs only - vanilla style.

    Known issues:
    Not working with RPG but will be fixed soon.

    Parameter(s):
    0: target - the object the shooter wants to shoot at - default is the player
    1: shooter - the one who wants to shoot at target - default is the nearest target to shooters crosshair

    Returns:
    position - position of the target when the current bullet will reach it
*/

params [["_target", cursorTarget], ["_shooter", player]];

if(!(alive _shooter) or (isNull _target) or (isNull _shooter)) exitWith {(_target modelToWorld [0,0,1.2])};

private _flight_dist = 0; 
private _burned_out = false;
private _prop_started = false;
private _burn_time = 0; 
private _time_diff = 0;
private _last_time = 0;
private _last_dist = 0;
private ["_weapon", "_magazine", "_init_speed_gun", "_init_speed_mag", "_air_friction", "_init_time", "_max_speed", 

"_thrust", "_thrust_time", "_rel_speed", "_distance", "_dist_step", "_last_speed", "_last_accel", "_new_dist", "_term", 

"_new_time", "_new_speed", "_new_accel"]; 

_shooter = vehicle _shooter;

_weapon = currentWeapon _shooter;
_magazine = currentMagazine _shooter;
 
if ( (count IR_gun_mag_ammo_cfg) > 1 ) then
{
 if ((_weapon != IR_gun_mag_ammo_cfg select 0) or (_magazine != IR_gun_mag_ammo_cfg select 1)) then
 { [_shooter] call fnc_handle_configs; };
}
else
{ [_shooter] call fnc_handle_configs; }; 

_init_speed_gun = IR_gun_mag_ammo_cfg select 2;
_air_friction = IR_gun_mag_ammo_cfg select 3;
_init_time = IR_gun_mag_ammo_cfg select 4;
_max_speed = IR_gun_mag_ammo_cfg select 5;
_thrust = IR_gun_mag_ammo_cfg select 6;
_thrust_time = IR_gun_mag_ammo_cfg select 7;

_rel_speed = ((velocity _target) vectorDiff (velocity _shooter));

if((vectorMagnitude _rel_speed) > 0) exitWith
{

 _distance = _target distance _shooter;

 _dist_step = _distance / (10 * diag_fps);

 _last_speed = _init_speed_gun;
 _last_accel = _air_friction * _init_speed_gun ^ 2;

 while {_last_dist < _distance} do
 {
  _new_dist = _last_dist + _dist_step;

  //calculate time from last position to actual position (_time_diff)
  if (_last_accel < 0) then // standard case - bullet is decelerated by airflow
  {
   _term = _last_speed / _last_accel;
   _time_diff = -1 * sqrt (_term ^ 2 + 2 * _dist_step / _last_accel) - _term;
  }
  else
  {
   if(_last_accel == 0) then  //maybe no value for air friction stored in config then we have just t=s/v
   {
    _time_diff = _dist_step / _last_speed;
   }
   else  // positive acceleration should happen if a propulsion is burning only
   {
    _term = _last_speed / _last_accel;
    _time_diff = sqrt (_term ^ 2 + 2 * _dist_step / _last_accel) - _term;
   };
  }; 

  _new_time = _time_diff + _last_time;
  _new_speed = _last_speed + _last_accel * _time_diff;

  if(_new_speed <= 0) exitWith {0};

  _new_accel = _air_friction * _new_speed ^ 2;  //calc air frictions influence to acceleration

//check if bullet is a rocket and if propulsion should accelerate
  if((_max_speed > 30) and (!_burned_out) and (_last_time > _init_time)) then  
  {
   if(!_prop_started) then
   {
    _prop_started = true;
    _burn_time = diag_tickTime + _thrust_time;
   }
   else
   {
    if(diag_tickTime > _burn_time) then
    {
     _burned_out = true;
    };
   };
   _new_accel = _new_accel + _thrust;
  };

  _last_dist = _new_dist;
  _last_time = _new_time;
  _last_speed = _new_speed;
  _last_accel = _new_accel;
 };

((_target modelToWorld [0,0,1.2]) vectorAdd (_rel_speed vectorMultiply _last_time))
};

(_target modelToWorld [0,0,1.2])

handle_configs.sqf

params ["_shooter"];

private _speed_coef_muzzle = 1;
private _air_fric_fact = -0.002;
private ["_weapon", "_magazine", "_init_speed_gun", "_init_speed_mag", "_ammo_class", "_air_friction", "_items", 

"_init_time", "_max_speed", "_thrust", "_thrust_time"]; 

_shooter = vehicle _shooter;

_weapon = currentWeapon _shooter;
_magazine = currentMagazine _shooter;

_init_speed_gun = getNumber(configfile >> "CfgWeapons" >> _weapon >> "initSpeed");
_init_speed_mag = getNumber(configfile >> "CfgMagazines" >> _magazine >> "initSpeed");
_ammo_class = getText(configFile >> "CfgMagazines" >> _magazine >> "ammo");

_air_friction = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "airFriction");

_items = _shooter weaponAccessories _weapon;

_init_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "initTime");
_max_speed = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "maxSpeed");
_thrust = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrust");
_thrust_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrustTime");

if((_init_speed_gun) < 0) then
{
 _init_speed_gun = -1 * init_speed_gun * _init_speed_mag;
};

if((_init_speed_gun) == 0) then
{
 _init_speed_gun = _init_speed_mag;
};

{
 if((_x find "muzzle") > -1) then
 {
  _temp_coef = getNumber(configfile >> "CfgWeapons" >> _x >> "ItemInfo" >> "MagazineCoef" >> "initSpeed");
  _speed_coef_muzzle = if (_temp_coef !=0) then {_temp_coef}else{1};
 }; 
} forEach _items;

_init_speed_gun = _init_speed_gun * _speed_coef_muzzle;

if(_air_friction > 0) then {_air_friction = _air_friction * _air_fric_fact;};

IR_gun_mag_ammo_cfg = [];
IR_gun_mag_ammo_cfg pushBack _weapon;
IR_gun_mag_ammo_cfg pushBack _magazine;
IR_gun_mag_ammo_cfg pushBack _init_speed_gun;
IR_gun_mag_ammo_cfg pushBack _air_friction;
IR_gun_mag_ammo_cfg pushBack _init_time;
IR_gun_mag_ammo_cfg pushBack _max_speed;
IR_gun_mag_ammo_cfg pushBack _thrust;
IR_gun_mag_ammo_cfg pushBack _thrust_time;

Thanks alot for ur advises, that thing is done now :-)

Share this post


Link to post
Share on other sites

Hey there sarogahtyp. First of all, nice script and thanks for sharing.

 

I know it's been quite some time since the time you posted here but I wanted to add a couple of minor improvements. I am not sure as to how much they would improve the performance (probably not a noticeable difference as they are really small details).

 

First is that when you have multiple checks you could use { }  to enclose all apart the first check. Example:

if(!alive ZaellixA && {alive sarogahtyp} && {local sarogahtyp}) {
	hint "Oooopppsss, you got me...!!!";
};

This way if the first condition (that is !alive ZaellixA) is false the rest of the conditions won't be checked.

 

Second, as fn_Quiksilver mentioned searching in the configFile is quite expensive. And although you save the data in a parameter you do search in it quite some times. You could possibly limit the search somewhat if you were to save part of the path in a variable. Example (borrowing part of your code):

private _configMags = configFile >> "CfgMagazines" >> magazine; // GET THIS PART OF THE "PATH" IN A VARIABLE

_init_speed_gun = getNumber(configfile >> "CfgWeapons" >> _weapon >> "initSpeed");

// NOW REPLACE THE NEXT TWO (COMMENTED BY ME) LINES WITH THE ONES I PLACED UNDERNEATH THEM
//_init_speed_mag = getNumber(configfile >> "CfgMagazines" >> _magazine >> "initSpeed"); -> ORIGINAL IMPLEMENTATION
//_ammo_class = getText(configFile >> "CfgMagazines" >> _magazine >> "ammo"); -> ORIGINAL IMPLEMENTATION
_init_speed_mag = getNumber(_configMags >> "initSpeed"); // ADDED CODE
_ammo_class = getText(_configMags >> "ammo"); // ADDED CODE

// NOW DO THE SAME FOR THE AMMO
_configAmmo = configFile >> "CfgAmmo" >> _ammo_class;

// REPLACE ALL INSTANCES OF config >> "CfgAmmo" >> _ammo_class WITH THE ABOVE VARIABLE

// _air_friction = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "airFriction"); -> ORIGINAL IMPLEMENTATION
_air_friction = getNumber(_configAmmo >> "airFriction"); // ADDED CODE

_items = _shooter weaponAccessories _weapon;

//_init_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "initTime"); -> ORIGINAL IMPLEMENTATION
//_max_speed = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "maxSpeed"); -> ORIGINAL IMPLEMENTATION
//_thrust = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrust"); -> ORIGINAL IMPLEMENTATION
//_thrust_time = getNumber(configFile >> "CfgAmmo" >> _ammo_class >> "thrustTime"); -> ORIGINAL IMPLEMENTATION

_init_time = getNumber(_configAmmo >> "initTime"); // ADDED CODE
_max_speed = getNumber(_configAmmo >> "maxSpeed"); // ADDED CODE
_thrust = getNumber(_configAmmo >> "thrust"); // ADDED CODE
_thrust_time = getNumber(_configAmmo >> "thrustTime"); // ADDED CODE

That's it... As I said, I am not sure how much of an impact these changes may have on the performance, but if you do bother trying them out and feel like it, let us know.

 

Cheers, and thanks again for sharing.

  • Like 2

Share this post


Link to post
Share on other sites

Hey @ZaellixA now I ll do a late reply 🙂

 

your first suggestion about lazy evaluation is a double-edged sword. The problem is that IF the part in curly brackets gets evaluated then it is slower than it would be without lazy evaluation.

Therefore one has to think about how often that could happen and if there is a real performance gain if one uses LE or if the whole thing gets slower with LE because the curly brackets are evaluated too often...

 

detailed comparison;

 

true || {false} || {false}	0.0009 ms
false || {true} || {false}	0.0014 ms
false || {false} || {true}	0.0017 ms

true || false || false		0.0012 ms
false || true || false		0.0012 ms
false || false || true		0.0012 ms

false && {true} && {true}	0.001 ms
true && {false} && {true}	0.0014 ms
true && {true} && {false}	0.0018 ms

what u can see is that without LE the execution time is always same.

With LE you have only one case where it is faster as without LE.

Therefore you have to care that your script triggers the best case mostly. or you just do it without LE...

  • Like 1

Share this post


Link to post
Share on other sites
On 4/7/2016 at 7:05 AM, sarogahtyp said:

i draw a crosshair on the screen at the position where u have to shoot at to hit the desired moving target.

u can activate it by pressing L key and a crosshair is drawn at the predicted position. if u press T then u can lock that cursorTarget and the crosshair is drawn for that locked target as long as its alive or u unlock it.

I ll give u a dropbox link in just a moment.

anyone have the script for this? please msg me or reply here

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

×