Jump to content
Tova

AI movement on scripted path / smooth rotations

Recommended Posts

Hello !

 

To give a bit of context, I'd like to emulate the SetDriveOnPath command for infantry units in order to have them navigate inside buildings lacking path/building positions.

The idea is to give an array of positions (e.g  [[4680.4,6175.85,0.00143909],[4680.3,6170.7,0.00143909]]) and have the AI move in a straight line in between thanks to a switchMove.

 

As of now, this is what I made :

 

infantryDriveOnPath={
	params ["_unit","_pathArray"];
	_unit disableAI "ANIM";
	_unit switchMove "Acts_SupportTeam_Front_Move";
	{
		_direction= _unit getdir _x;
		while {sleep 0.5; ( _unit distance _x > 1)} do {
			_unit setDir _direction;
		}
	}forEach _pathArray;
	_unit enableAI "ALL";
	_unit switchMove "";
};

 

The resulting behaviour is satisfying, however the setDir command causes instant rotations/twitching that are very ugly.

 

Any ideas on how I could make smooth direction corrections ?

 

Thanks 🙂

Share this post


Link to post
Share on other sites

Well, setDir is just a command to set the direction an object is facing. The command in this that you're after would be setFormDir. Not sure just how well that'll translate into what you're trying to do, so it might be worth using a setDir after the AI finish adjusting their direction from setFormDir.

Share this post


Link to post
Share on other sites

Thanks for your help, however that doesn't really solve my issue 🙂

 

In fact, I cannot use setFormDir since I used disableAI "ANIM"; that command "freezes" all AI movement/behavour, so setFormDir won't have any effect.

Moreover, even if used a workaround to be able to use setFormDir, the unit will stop turning as soon as unit direction is +/- 30 degrees of the formation direction, which will force me to use a setDir... Hence causing an "instant turn".

Share this post


Link to post
Share on other sites
21 hours ago, Tova said:

Moreover, even if used a workaround to be able to use setFormDir, the unit will stop turning as soon as unit direction is +/- 30 degrees of the formation direction, which will force me to use a setDir... Hence causing an "instant turn".

 

Yeah ok, thought that might've been the case but maybe not to that extreme. 

 

What about using onEachFrame to incrementally setDir to try and reduce the obvious snapping? 

 

OR......

Have the unit play one of the turning animations until it's facing the right direction.

  • Like 2

Share this post


Link to post
Share on other sites

@dreadedentity : Well, if I remove the setDir, the unit just keep moving forward ! Without changing direction I mean 😛

 

@beno_83au : I like the ideas !

Quote

Have the unit play one of the turning animations until it's facing the right direction.

I'll have to find a good looking turning animation 🙂

Checking if the direction is good could then be done with a :

waitUntil { ((direction _unit)<_direction+2) AND ((direction _unit)>_direction-2) }

But I have to say I like this approach better :

Quote

What about using onEachFrame to incrementally setDir to try and reduce the obvious snapping? 

How would you choose the incrementation rate ?

I believe we can't do for instance 1° per frame, because the turning speeding would then be inconsistent ?

 

Share this post


Link to post
Share on other sites

For example:

onEachFrame {

_dir = getDir _unit;

_unit setDir (_dir + 0.1);

if ((getDir _unit) == _wpDir) exitWith {};

};

 

Sorry about the formatting. It's 3.20 am and I'm on my phone. But, something like that should get you going. Keep in mind you would need to use global variables for _unit and _wpDir (I don't think you can parse local variables into onEachFrame?). Then use something like you posted above to check that the unit's direction is within a certain range for the exitWith. You could then adjust the 0.1 up or down for better results. There's definitely turning anims though, so give those a go too. 

  • Like 3

Share this post


Link to post
Share on other sites
41 minutes ago, beno_83au said:

For example:

onEachFrame {

_dir = getDir _unit;

_unit setDir (_dir + 0.1);

if ((getDir _unit) == _wpDir) exitWith {};

};

 

exiting an onEachFrame?

Share this post


Link to post
Share on other sites
On 4/1/2019 at 3:44 AM, Tova said:

In fact, I cannot use setFormDir since I used disableAI "ANIM"; that command "freezes" all AI movement/behavour, so setFormDir won't have any effect.

Moreover, even if used a workaround to be able to use setFormDir, the unit will stop turning as soon as unit direction is +/- 30 degrees of the formation direction, which will force me to use a setDir... Hence causing an "instant turn".

onEachFrame is going to work for you, I am confident of that.  You can even do it with a loop.  In my Last Tango in Tanoa Episode 2 mission, I rotated an AI (El Cojon) via a loop so he could track and shoot flying birds.  It worked pretty smooth as you can see in vid below.  This code could be easily tweaked to work for you I think.  You may want a smaller dir increment than .25, as I have cojon turning fast to track a bird in flight.

Spoiler

_targeted = false; 
cojon switchmove "amovpercmstpsraswrfldnon";
while {not _targeted } do
{
	if ((abs(getdir cojon - ([cojon, _critter modelToWorld [0,3,0]] call BIS_fnc_dirTo))) > 2) then
	{
		if (_relPos select 0 > 0) then
		{
			_dir = (getdir cojon) + .25;
		} else
		{
			_dir = (getdir cojon) - .25;
		};
		cojon setdir _dir;
	}
	else
	{
		_targeted = true; 
	};
};

 

Skip to 1.25 in video for watching cojon (non-player ai on right) for first good view of him swiveling left to shoot a bird.

 

  • Like 5

Share this post


Link to post
Share on other sites
28 minutes ago, pierremgi said:

exiting an onEachFrame?

 

Yeah, example 3 on https://community.bistudio.com/wiki/exitWith

 

I actually used it for the first time ever yesterday. Before then I've always used the functions as recommended in the wiki entry for onEachFrame.

Share this post


Link to post
Share on other sites
6 minutes ago, beno_83au said:

 

Yeah, example 3 on https://community.bistudio.com/wiki/exitWith

 

I actually used it for the first time ever yesterday. Before then I've always used the functions as recommended in the wiki entry for onEachFrame.

 

Ah OK. The example 3:  exitWith {onEachFrame{}}  is a mean to "kill" the onEachFrame with and empty onEachFrame code. On my mind it's outdated, from a time when stackable EH didn't exist.

Now, I'm always using Bis_fnc_addStackedEventHandler (example 2 with parameters). You can easily stop it with the remove function on _thisEventHandler special variable, but there is no reason to do that here.

  • Like 2

Share this post


Link to post
Share on other sites

@pierremgi Yeah, i use the stackable event handlers too. As i said, first time I'd use onEachFrame like that yesterday and i did it to be quick and lazy for some testing i was doing. But i also forgot the onEachFrame {} in exitWith so good pick up there 👌

Share this post


Link to post
Share on other sites

@Tova, btw, I am very interested in your final script.  Please post it when finished.  It will be awesome for building clearing cutscenes and many other uses.

  • Thanks 1

Share this post


Link to post
Share on other sites
22 hours ago, Tova said:

How would you choose the incrementation rate ?

Could always use linearConversion to retrieve a direction based on time or distance.

 

Code just as an example to get idea across. Would need playing with...

_targetPosition = /*some postion*/
_targetDistance = _unit distanceSqr _targetPosition;
_minArrivalDistance = 1;

_rotateDistance = 5;

//While the unit is more than minArrivalDistance away from target
while { _unit distanceSqr _targetPosition >= ( _minArrivalDistance ^ 2 ) } do {

	//If the relative dir to the target is <> 5 degree from facing
	//Just to try and stop to many distance calcualtions
	if !( _unit getRelDir _targetPosition < 5 || { _unit getRelDir _targetPosition > 355 } ) then {
		
		//Get a direction to face,  based off of...
		_dir = linearConversion[
			//From starting distance
			_targetDistance,
			//To starting distance minus the rotating distance
			(( _targetDistance - ( _rotateDistance ^ 2 )) max ( _minArrivalDistance ^ 2 )),
			//Based off of current distance from unit to target
			_unit distanceSqr _targetPosition, 
			//Turn from current facing direction
			getDir _unit,	//May need to be starting direction
			//To the direction to the target
			_unit getDir _targetPosition,
			//Clamp the value
			true 
		];
		
		//Set units direction
		_unit setDir _dir;
	};
};

 

  • Like 4

Share this post


Link to post
Share on other sites

1^2 ? 😜 (joke). linearConversion is a very good idea.

Share this post


Link to post
Share on other sites
19 hours ago, pierremgi said:

1^2 ? 😜

Aye should really read...

_rotateDistance = 5;
_minArrivalDistance = 1;

//While the unit is more than min arrival distance away from target
while { _unit distanceSqr _targetPosition >= ( _minArrivalDistance ^ 2 ) } do {

Is there just to make sure what ever min distance is, is squared, I realise 1^2 == 1

  • Like 2

Share this post


Link to post
Share on other sites

If you're still looking for some code to spin a unit toward another unit, here's a test I built.  It uses a stacked eventHandler.  To test it in editor, place a player unit and a unit named "thug".  Run the mission and paste this code in the debug console and execute it.  Thug unit will spin towards player until pointing at player.  Will spin left or right depending which is shortest degrees to spin.  Walk around thug and execute it again.

Spoiler

_n= [thug, player] spawn
{
params["_unit","_target"];
	//_unit setCombatMode "CARELESS";
	{
		_unit disableAI _x;
	} forEach 
	[
		"TARGET",// - stop the unit to watch the assigned target / group commander may not assign targets
		"AUTOTARGET",// - prevent the unit from assigning a target independently and watching unknown objects / no automatic target selection
		"MOVE",// - disable the AI's movement / do not move
		//"ANIM",// - disable ability of AI to change animation. Available only since ArmA: Cold War Assault (OFP 1.99).
		//"TEAMSWITCH",// - AI disabled because of Team Switch
		"FSM",// - disable the execution of AI behavior scripts. Available only since Operation Arrowhead v1.60.
		"WEAPONAIM",// - no weapon aiming
		"AIMINGERROR",// - prevents AI's aiming from being distracted by its shooting, moving, turning, reloading, hit, injury, fatigue, suppression or concealed/lost target Available only since Arma 3 v1.42.
		"SUPPRESSION",// - prevents AI from being suppressed Available only since Arma 3 v1.42.
		//"CHECKVISIBLE",// - disables visibility raycasts Available only since Arma 3 v1.54.
		"COVER",// - disables usage of cover positions by the AI Available only since Arma 3 v1.56.
		"AUTOCOMBAT",// - disables autonomous switching to COMBAT when in danger Available only since Arma 3 v1.56.
		"MINEDETECTION"// - disable Ai mine detection.
	];
//_unit playmove "amovpercmstpsraswrfldnon";
_targetObj = _target;
_dirTo = [_unit, _target] call BIS_fnc_dirTo;
_degreesToRotate = _unit getRelDir _target;
_rotateDegrees = 1;
if (_degreesToRotate > 180) then
{
	_degreesToRotate = (360 - _degreesToRotate ) * -1;
	_rotateDegrees = _rotateDegrees * -1;
};

["dudeEH1", "onEachFrame", 
{	
	params["_dude","_dirTo", "_rotateDegrees", "_targetObj" ];
	_slug = attachedTo _dude;
	if ([ position _dude, getDir _dude, 20, position _targetObj ] call BIS_fnc_inAngleSector) then
	{
		_dude enableAI "ANIM";
		{
			_dude enableAI _x;
		} forEach 
		[
			"TARGET",// - stop the unit to watch the assigned target / group commander may not assign targets
			"AUTOTARGET",// - prevent the unit from assigning a target independently and watching unknown objects / no automatic target selection
			"MOVE",// - disable the AI's movement / do not move
			"ANIM",// - disable ability of AI to change animation. Available only since ArmA: Cold War Assault (OFP 1.99).
			//"TEAMSWITCH",// - AI disabled because of Team Switch
			"FSM",// - disable the execution of AI behavior scripts. Available only since Operation Arrowhead v1.60.
			"WEAPONAIM",// - no weapon aiming
			"AIMINGERROR",// - prevents AI's aiming from being distracted by its shooting, moving, turning, reloading, hit, injury, fatigue, suppression or concealed/lost target Available only since Arma 3 v1.42.
			"SUPPRESSION",// - prevents AI from being suppressed Available only since Arma 3 v1.42.
			//"CHECKVISIBLE",// - disables visibility raycasts Available only since Arma 3 v1.54.
			"COVER",// - disables usage of cover positions by the AI Available only since Arma 3 v1.56.
			"AUTOCOMBAT",// - disables autonomous switching to COMBAT when in danger Available only since Arma 3 v1.56.
			"MINEDETECTION"// - disable Ai mine detection.
		];
		_dude setFormDir getDir _dude;
		["dudeEH1", "onEachFrame"] call BIS_fnc_removeStackedEventHandler;
	} else
	{
		_dude setdir ((getdir _dude) + _rotateDegrees);
	};
}, [_unit,_dirTo, _rotateDegrees, _targetObj, "dudeEH1"]] call BIS_fnc_addStackedEventHandler;
};

 

Notes:

  • To increase speed of rotation, increase this variable:    _rotateDegrees = 1;
  • I disable various AI features on spinning unit, so he doesn't fight the spin and get all twitchy and weird. 
  • AI is reenabled after spin complete so he can engage targets and be his normal retarded AI self, etc.

 

  • Like 3

Share this post


Link to post
Share on other sites
3 hours ago, pierremgi said:

Did you try this bis_fnc_scriptedMove ?

Goddammit, didn't know about this function.  I just spent hours writing my own version of this!  Thank mgi!

  • Like 2

Share this post


Link to post
Share on other sites
17 hours ago, pierremgi said:

Did you try this bis_fnc_scriptedMove ?

I looked a this bis function and I didn't waste my time after all.  Their function is hardcoded to a walk animation only with no weapon.  My script will support any animation, and provide option to force firing while moving.  I may adopt their turning code though...not sure.

  • Like 3

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

×