Tova 130 Posted April 1, 2019 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
beno_83au 1369 Posted April 1, 2019 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
Tova 130 Posted April 1, 2019 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
dreadedentity 278 Posted April 2, 2019 What happens if you just take setDir out? Share this post Link to post Share on other sites
beno_83au 1369 Posted April 2, 2019 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. 2 Share this post Link to post Share on other sites
Tova 130 Posted April 2, 2019 @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
beno_83au 1369 Posted April 2, 2019 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. 3 Share this post Link to post Share on other sites
pierremgi 4853 Posted April 2, 2019 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
johnnyboy 3792 Posted April 2, 2019 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. 5 Share this post Link to post Share on other sites
beno_83au 1369 Posted April 2, 2019 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
pierremgi 4853 Posted April 2, 2019 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. 2 Share this post Link to post Share on other sites
beno_83au 1369 Posted April 2, 2019 @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
johnnyboy 3792 Posted April 2, 2019 @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. 1 Share this post Link to post Share on other sites
Larrow 2820 Posted April 2, 2019 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; }; }; 4 Share this post Link to post Share on other sites
pierremgi 4853 Posted April 2, 2019 1^2 ? 😜 (joke). linearConversion is a very good idea. Share this post Link to post Share on other sites
Larrow 2820 Posted April 2, 2019 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 2 Share this post Link to post Share on other sites
johnnyboy 3792 Posted April 9, 2019 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. 3 Share this post Link to post Share on other sites
pierremgi 4853 Posted April 10, 2019 Did you try this bis_fnc_scriptedMove ? 2 Share this post Link to post Share on other sites
johnnyboy 3792 Posted April 10, 2019 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! 2 Share this post Link to post Share on other sites
johnnyboy 3792 Posted April 10, 2019 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. 3 Share this post Link to post Share on other sites
Tova 130 Posted November 23, 2019 Well, months later, I didn't do much progress on the subject... I ended up using the "smooth rotation" script from BIS_fnc_scriptedMove, but while it looks decent for units rotating while stopped, it still looks cranky when a unit is rotating while moving (cf video) : Here's the "smooth rotation" code : //procedure for smooth turning _turningProc = { _this spawn { _guy = _this select 0; _wp = _this select 1; _dir = [_guy, _wp] call BIS_fnc_dirTo; if (_dir < 0) then {_dir = 360 + _dir}; _degs = _dir - direction _guy; if (abs _degs > 180) then {_degs = _degs + (360 * (_degs / abs _degs) * (-1))}; _step = _degs / 20; while {(abs _degs > abs _step) && BIS_scriptedMoveEnabled} do { _guy setDir (direction _guy + _step); _degs = _dir - direction _guy; sleep 0.025 }; _guy setDir ([_guy, _wp] call BIS_fnc_dirTo) } }; I also tried your suggestions with OnEachFrame/stacked eventHandler but without any improvement and with the additional drawback that the unit also rotate when you are in the pause menu 😛 Maybe the new Arma updates introduced a way to solve this issue ? Also I know @johnnyboythat you have been working on your own version of the script, maybe were you able to make progress on the matter ? 1 Share this post Link to post Share on other sites
johnnyboy 3792 Posted November 23, 2019 16 hours ago, Tova said: Also I know @johnnyboythat you have been working on your own version of the script, maybe were you able to make progress on the matter ? Hey Tova, the units moving in that video look good to me. What I like better about yours is they turn while moving. In my case, units stop at each point and turn while stopped until pointed at next point. I use onEachFrame for turning. Turn script: Spoiler // turnToPos.sqf // Example Call: [dude1, _nextPos] call JBOY_turnToPos; params["_unit","_targetPos"]; _unit setVariable ["busyTurning", true,true]; _unit setVelocity [0,0,0]; _unit forceSpeed 0; //_unit setBehaviour "CARELESS"; // ***************************************************************** // Disable AI that interferes with forced animation moves. // ***************************************************************** { _unit disableAI _x; } forEach [ //"PATH", //"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. ]; // ***************************************************************** // // ***************************************************************** _dirTo = [_unit, _targetPos] call BIS_fnc_dirTo; _degreesToRotate = _unit getRelDir _targetPos; _rotateDegrees = 4.4; if (_degreesToRotate > 180) then { //_degreesToRotate = (360 - _degreesToRotate ) * -1; _rotateDegrees = _rotateDegrees * -1; }; diag_log ["turnToPos start turning",_unit,_targetPos, getpos _unit, _unit distance _targetPos]; // ***************************************************************** // // ***************************************************************** _EH_ID = format ["%1_EH",_unit getVariable "JBOY_memberID"]; [_EH_ID, "onEachFrame", { params["_unit","_dirTo", "_rotateDegrees", "_targetPos","_EH_ID" ]; if ([ position _unit, getDir _unit, 10, _targetPos ] call BIS_fnc_inAngleSector and (_unit getVariable "busyTurning")) then { //_unit enableAI "ANIM"; { _unit enableAI _x; } forEach [ //"PATH", //"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 setFormDir getDir _unit; _unit setVariable ["busyTurning", false,true]; diag_log ["turnToPos end turning",_unit]; //if (_unit == dude1) then { hintc str format["_unit=%1, EH=%2",_unit,_EH_ID];}; [_EH_ID, "onEachFrame"] call BIS_fnc_removeStackedEventHandler; } else { _unit setdir ((getdir _unit) + _rotateDegrees); }; }, [_unit,_dirTo, _rotateDegrees, _targetPos, _EH_ID]] call BIS_fnc_addStackedEventHandler; 1 Share this post Link to post Share on other sites