Jump to content
Sign in to follow this  
Mike_Kilo

Path following algorithm

Recommended Posts

Hello all,

For an academic project I am working on, I use ARMA OA as a scenario simulator.

The project incorporates a UGV (Unmanned Ground Vehicle) designed to carry the infantry equipment.

The UGV follows the exact path of the operator it is assigned to, keeping a set distance from the operator, unless it is in remote control mode.

I have tried several methods in order to simulate this behavior:

  1. Creating a vehicle (ATV), constantly reading the relative position of the operator and creating a velocity vector to it - does not work well enough and the vehicle is not really present in the simulation.
  2. Creating a vehicle and using the "attachTo" command - yielded very poor results.
  3. Creating a vehicle and assigning "InvisibleManW_EP1" as pilot, while shutting off the AI - this method seems the most promising, but the AI is still not completely neutralized, making this method ineffective.

Is there any other way you think I can Implement the algorithm?

Is there a way to shut the AI completely, as the option disableAI "PATHPLAN" in VBS? that sounds like the ultimate tool for what I need.

Thanks for the help in advance,

MK

Share this post


Link to post
Share on other sites

disableAI.

This will probably help you out.

I don't see why you would shut off the AI though since if you have an invisible pilot that can perfectly well follow the Operator around (well, the AI has it's limits but yeah) with doFollow it seems quite unnecessary to shut off the AI and manually configure everything, or am I misunderstanding this?

Share this post


Link to post
Share on other sites

Hello Tryteyker,

As I said in the original post - I need the UGV to follow the exact path of the operator it follows.

The AI takes the liberty of planning its path, if it detects a possible obstacle for example, it will try to avoid it, and if the unit it follows stops, it will sometimes drive aside.

I need the AI to drive in straight lines between the coordinates transmitted at predetermined intervals from the operator.

All the disableAI commands did not remove the basic behavior of the unit - it still runs some kind of FSM which I cannot shut, even with disableAI "FSM".

I hope I managed to clarify the problem a bit more.

Best regards,

MK

Share this post


Link to post
Share on other sites

You might find this useful, I discovered this little gem in the game files, it is hardly documented anywhere, but BIS used it animate targets and such. (This was written by a BIS dev as far as I know)

I made a quick demo a long time ago as a proof of concept, please ignore the sound and this was just a test.

Here is the function:

functions_animations.sqf

BIS_animateMoveSmooth =
{
   private["_sleep","_object","_posStart","_posEnd","_deltax","_deltay","_deltaz","_distance","_tmax","_a","_vmax","_t","_ssaved","_animList","_animPhase","_progress","_posx","_posy","_posz","_anim","_taccel","_saccel","_sconst","_tconst"];
   private["_wps","_positions","_distancesStart","_distancesPrev","_refWP","_ghostBall","_time","_hoverHeight","_dir"];

   _sleep = 0.01;                    //delay between each anim frame, lower values = smoother animation


   _object = _this select 0;
   _wps = _this select 1;            //array of all animation WPs


   //hover height
   if (count _this > 4) then {
       _hoverHeight = _this select 4;
   };


   //----------------------------------------------------------------------------------------------
   //positioning & animating along waypoints ------------------------------------------------------

   _positions = [];        //array of all WP positions
   _distancesStart = [];    //array of WPs distances from the beginning of the animation
   _distancesPrev = [];    //array of WPs distances from the previous WP
   _refWP = _wps select 0;


   private["_d","_dTotal","_tmpPos"];
   _d = 0;
   _dTotal = 0;

   {
       //storing position of each WP
       //it's WP object
       if (typeName(_x) == "OBJECT") then {
           _tmpPos = getPosASL _x;
           if (_x == player) then {
               _tmpPos = [_tmpPos select 0, _tmpPos select 1, (_tmpPos select 2) + 0.8]; 
           };
       //it's position            
       } else {
           _tmpPos = _x;
       };
       _positions = _positions + [_tmpPos];




       //total from the start
       _d = _x distance _refWP;
       _dTotal = _dTotal + _d;
       _distancesStart = _distancesStart + [_dTotal];



       //from previous WP
       _distancesPrev = _distancesPrev + [_x distance _refWP];
       _refWP = _x;        

   } forEach _wps;

   //current WPs
   _wpStart = 0;
   _wpEnd = _wpStart + 1;


   //count positions
   _posStart = _positions select _wpStart;
   _posEnd = _positions select _wpEnd;    

   //count delta x,y,z
   _deltax = (_posEnd select 0) - (_posStart select 0);
   _deltay = (_posEnd select 1) - (_posStart select 1);
   _deltaz = (_posEnd select 2) - (_posStart select 2);    


   _dir =  [_posStart,_posEnd] call BIS_fnc_dirTo;
   _object setDir (_dir-180);    


   //display object at start position
   if isNil("_hoverHeight") then {
       _object setPosASL (_positions select 0);
   } else {
       _object setPos [(_positions select 0) select 0, (_positions select 0) select 1,_hoverHeight];
   };
   //----------------------------------------------------------------------------------------------
   //----------------------------------------------------------------------------------------------




   //_vmax
   if (count(_this) > 2) then {
       _vmax = _this select 2;
   } else {
       _vmax = 15;
   };


   //_a
   if (count(_this) > 3) then {
       _a = _this select 3;
   } else {
       _a = 5;
   };


   //acceleration path
   _taccel = _vmax/_a;
   _saccel = 1/2 * _a * _taccel * _taccel;


   //distance (of whole animation)
   _distance = _distancesStart select (count(_distancesStart) - 1);
   //["_distance",_distance] call BIS_debugLog;

   //distance too short
   if (2*_saccel > _distance) then {
       //["DISTANCE IS TOO SHORT"] call BIS_debugLog;
       //["_saccel (raw)",_saccel] call BIS_debugLog;
       //["_taccel (raw)",_taccel] call BIS_debugLog;        

       _saccel = _distance/2;
       _taccel = sqrt(_distance/_a);


       //["_saccel (fixed)",_saccel] call BIS_debugLog;
       //["_taccel (fixed)",_taccel] call BIS_debugLog;        


       //distance is too short -> object wont reach max speed
       _vmax = _a * _taccel;

       //there is no constant speed part in middle
       _sconst = 0;
       _tconst = 0;

   //distance is fine
   } else {
       //["DISTANCE IS FINE"] call BIS_debugLog;

       _sconst = _distance - 2*_saccel;
       _tconst = _sconst/_vmax;
   };


   //total time
   _ttotal = 2*_taccel + _tconst;

   _t = 0;
   _tmax = _taccel;
   _ssaved = 0;


   _animList = [];
   _animPhase = 0;
   _animList = _animList + [{1/2 * _a * _t * _t}];
   _animList = _animList + [{_vmax * _t}];
   _animList = _animList + [{_vmax * _t - 1/2 * _a * _t * _t}];    


   _time = time;

   private["_sleepTime","_tover"];

   _tover = 0;


   while {_animPhase < count(_animList)} do {
       _t = _t + _sleep;

       _sleepTime = time;


       sleep _sleep;


       //real sleep time calculations
       _sleepTime = time - _sleepTime;
       _t = _t - _sleep + _sleepTime;
       if (_t > _tmax) then {
           _tover = _t - _tmax;
           _t = _tmax;
       };

       _anim = _animList select _animPhase;
       _s = call _anim;


       //------------------------------------------------------------------------------------------
       //positioning & animating along waypoints --------------------------------------------------
       _movedFromStart = _ssaved + _s;

       //searching for acctual _wpStart & _wpEnd
       while {_movedFromStart > _distancesStart select _wpEnd} do {
           _wpStart = _wpStart + 1;
           _wpEnd = _wpStart + 1;

           //["Current waypoints:",format["%1 -> %2",_wpStart,_wpEnd]] call BIS_debugLog;


           //count positions
           _posStart = _positions select _wpStart;
           _posEnd = _positions select _wpEnd;    

           //count delta x,y,z
           _deltax = (_posEnd select 0) - (_posStart select 0);
           _deltay = (_posEnd select 1) - (_posStart select 1);
           _deltaz = (_posEnd select 2) - (_posStart select 2);

           //set direction
           _dir =  [_posStart,_posEnd] call BIS_fnc_dirTo;
           _object setDir (_dir-180);            
       };

       //progress part -> object positioning
       _movedBetweenWPs = _movedFromStart - (_distancesStart select _wpStart);
       _progress = _movedBetweenWPs / (_distancesPrev select _wpEnd);
       if (_progress > 1) then {
           _progress = 1;
       };

       _posx = (_posStart select 0) + _deltax * _progress;
       _posy = (_posStart select 1) + _deltay * _progress;
       _posz = (_posStart select 2) + _deltaz * _progress;

       if isNil("_hoverHeight") then {
           _object setPosASL[_posx,_posy,_posz];
       } else {
           _object setPos[_posx,_posy,_hoverHeight];
       };


       //["_movedFromStart",_movedFromStart] call BIS_debugLog;
       //["_movedBetweenWPs",_movedBetweenWPs] call BIS_debugLog;
       //["_progress",_progress] call BIS_debugLog;
       //["_posx",_posx] call BIS_debugLog;

       //create a ghost-ball
       /*
       _ghostBall = "sign_sphere25cm_ep1" createVehicle [0,0,0];
       _ghostBall setObjectTexture [0,"#(argb,8,8,3)color(0.8,0.5,0.5,0.5,ca)"];
       _ghostBall setPosASL[_posx,_posy,_posz];
       */


       //------------------------------------------------------------------------------------------


       if (_t == _tmax) then {
           _animPhase = _animPhase + 1;


           //[format["Animation ID%1 finished at distance %2 (took %3 secs)!",_animPhase-1,_ssaved + _s,_tmax]] call BIS_debugLog;

           //skip the middle part (animationPhase 1) if its time == 0
           if (_animPhase == 1 && _tconst == 0) then {
               _animPhase = _animPhase + 1;
               //["Animation ID1 skipped!"] call BIS_debugLog;
           };            

           if (_animPhase == 1) then {
               _tmax = _tconst;
           } else {
               _tmax = _taccel;
           };

           //_t = 0;
           _t = _tover;
           _ssaved = _ssaved + _s;
       };    
   };

   private["_pos"];

   _pos = _positions select (count(_positions) - 1);

   if isNil("_hoverHeight") then {
       _object setPosASL _pos;
   } else {
       _object setPos [_pos select 0, _pos select 1, _hoverHeight];
   };    
};




//_sqf = this spawn {_i=0;while {true} do {_i=_i+2;if(_i>360)then{_i=_i-360;};_this setDir _i;sleep 0.01;};};
BIS_animateRotate =
{
   private["_object","_dir","_i"];

   _object = _this select 0;
   _dir = getDir(_object);

   _i = _dir;

   while {true} do {
       _i = _i + 2;
       if (_i > 360) then {_i = _i-360;};
       _object setDir _i;
       sleep 0.02;
   };
};




BIS_animateUpDown  =
{
   private["_object","_s","_t","_v","_a","_pos","_sleep","_smax","_tmax","_vmax","_startPos"];

   _sleep = 0.02;    //delay between each anim frame, lower values = smoother animation

   _object = _this select 0;

   _smax = _this select 1;            //animation length [m]
   _tmax = _this select 2;            //animation time [sec]

   _a = 2*_smax/(_tmax*_tmax);        //_acceleration
   _vmax = _a * _tmax;                //animation speed [m/sec]


   _t = 0;

   _pos = getPosASL(_object);
   _startPos = _pos;

   _animList = [];
   _animPhase = 0;


   //move-up and decelerate
   _animList = _animList + [{+1 * (_vmax * _t - 1/2 * _a * _t * _t)}];


   //move-down and accelerate
   _animList = _animList + [{-1 * (1/2 * _a * _t * _t)}];


   //move-down and decelerate
   _animList = _animList + [{-1 * (_vmax * _t - 1/2 * _a * _t * _t)}];    

   //move-up and accelerate
   _animList = _animList + [{+1 * (1/2 * _a * _t * _t)}];

   while {true} do {
       _t = _t + _sleep;

       if (_t > _tmax) then {
           _t = _tmax;
       };
       sleep _sleep;


       _anim = _animList select _animPhase;
       _s = call _anim;


       _object setPosASL[_pos select 0,_pos select 1,(_pos select 2) + _s];

       if (_t == _tmax) then {
           _animPhase = _animPhase + 1;
           _t = 0;


           if (_animPhase == count(_animList)) then {
               _animPhase = 0;
           };


           _pos = [_pos select 0,_pos select 1,(_pos select 2) + _s];
       };    
   };


   //reposition bact to start pos
   _object setPosASL _startPos;
};

Compile it like this:

BIS_animateMoveSmooth = compile preprocessFile "functions_animations.sqf";

You call it by passing the object to be moved or animated and then providing a list of postions or objects that you want it to path over.

Example:

_nul = [_objectthatsmoving, [[x,y,z], [x,y,z] , [x,y,z], [x,y,z]]] spawn BIS_animateMoveSmooth;

Share this post


Link to post
Share on other sites

Thanks for the suggestion, Riouken.

I created the "functions_animations.sqf" file and posted the "compile preprocess" in the INIT line of the player.

I then created a new SQF with the following content:

_target = _this select 0;
while {alive _target} do 
{
_nul = [_atv1,getpos _target] spawn BIS_animateMoveSmooth;
sleep 5;
};

where "target" is the unit I want to follow and "atv1" is the object I want to move.

when I ran the simulation, nothing happened- i.e. the ATV remained in its spawn location.

any ideas of what am I doing wrong?

MK

Share this post


Link to post
Share on other sites

You have to provide at-least 2 positions in an array.

I would try creating a function that tracks the players pos every second for say 5 -10 seconds and stores it in an array, then execute that function. rinse and repeat, in theory it should follow you around like a puppy dog. Let me see if I can write something up real quick.

---------- Post added at 07:20 PM ---------- Previous post was at 07:01 PM ----------

This is not tested but I hope this helps.

_target = _this select 0;
_path = [];




while {alive _target} do 
{
_seedtime = time + 10;


While {time < _seedtime} do
	{
		_newpos = getposATL _target;
		_path set [count _path,_newpos];	
		sleep 1;


	};

_nul = [_atv1,_path] spawn BIS_animateMoveSmooth;


sleep .01;
_path = [];
};

Share this post


Link to post
Share on other sites

Still no joy.

Let's see that I didn't mess some of the technicalities:

1. I created a game logic with "atv1 = "ATV_US_EP1" createVehicle getpos this;" in the INIT line, in order to create the ATV.

2. In the player INIT line, I placed the "BIS_animateMoveSmooth = compile preprocessFile "functions_animations.sqf";" line.

3. I then placed a trigger which calls for an SQF with the code you just wrote.

4. nothing happens, ATV remains still

Can you see something that I'm doing wrong?

Thanks for the help again, anyhow!

MK

Share this post


Link to post
Share on other sites
Any insights on the problem?

Hi, Mile, I couldn't get the first set of scripts to work but that's no surprise as they're functions and I never can get them to work.

the last code contains a bug , it uses _atv1 it should be atv1 for the name of the vehicle, it also has a 10 second delay make it smaller.

Although it works it only it's only moving the vehicle instantly to the new position, I don't see any smooth moving.

Share this post


Link to post
Share on other sites

Try this sample mission based off of Riouken's use of Bis_animateMoveSmooth

BIS_animateMoveSmooth

I changed a couple of settings in the function, because it would move your vehicle backwards.

Activate by radio trigger Alpha

Remember, this function was not meant for vehicles, as there is no movement animations(tires, etc). Also, if you don't mess around with the height, I have found they usually move undergound.

Aircraft will explode if to close to the ground.

array used for demo:

[object, [start pos, 1st marker,second marker],speed,acceleration,height] spawn BIS_animateMoveSmooth;

Edited by panther42

Share this post


Link to post
Share on other sites

Yes that worked panther42, but as I more or less new it only moves in straight lines, mike is trying to make it look like a small vehicle (robot) ect is following you.

AI can't be disabled enough as they avoid stuff and keep making unnatural movements.

I did doctor up play/record to input live data with a short delay, it does work with vehicle to vehicle up to a point but with record/play it stutters. I was hoping to make better convoys.

It fails completely when tracking man as a man can move side ways and so does the vehicle.

Edited by F2k Sel

Share this post


Link to post
Share on other sites

Ah yes, better convoys. I've always had good luck with DTM's convoy, or ruebe's.

Getting an unmanned vehicle to follow you around, like the OP wanted may be tricky. Unless you could use something like HOZ do follow, or Mando move with an invisible man.

Share this post


Link to post
Share on other sites

Nice to see you here too, F2K Sel!

Panther42 - can you elaborate more about HOZ or the mando techniques?

Do you think there is a chance one of the developers can give me a tip about shutting the AI completely off, or the chances are that they see this thread are very low?

Best regards and thanks for your suggestions and support, all!

MK

Share this post


Link to post
Share on other sites

Any other / new ideas?

Right now I keep on working with the velocity vectors method, but the results are far from satisfactory - the graphics are jerky and the model does not perform path following, rather a "dog pursuit" algorithm, which is not what I am looking for.

Best Regards,

MK

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  

×