Nlaw - Update to use Missile Guidance Framework (#10034)

This commit is contained in:
Bailey Danyluk 2024-08-23 08:46:01 -06:00 committed by GitHub
parent 2c4c7c2fdf
commit a1cafef5ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 207 additions and 21 deletions

View File

@ -12,3 +12,9 @@ class EGVAR(missileguidance,SeekerTypes) {
functionName = QFUNC(seeker); functionName = QFUNC(seeker);
}; };
}; };
class EGVAR(missileguidance,NavigationTypes) {
class GVAR(PLOS) {
functionName = QFUNC(navigation);
onFired = QFUNC(navigation_onFired);
};
};

View File

@ -6,9 +6,8 @@ class CfgAmmo {
class ace_missileguidance { class ace_missileguidance {
enabled = 1; enabled = 1;
minDeflection = 0.0005; // Minium flap deflection for guidance pitchRate = 5; // Minium flap deflection for guidance
maxDeflection = 0.01; // Maximum flap deflection for guidance yawRate = 10; // Maximum flap deflection for guidance
incDeflection = 0.0005; // The incrmeent in which deflection adjusts.
canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode
@ -19,6 +18,9 @@ class CfgAmmo {
defaultSeekerLockMode = "LOBL"; defaultSeekerLockMode = "LOBL";
seekerLockModes[] = {"LOBL"}; seekerLockModes[] = {"LOBL"};
defaultNavigationType = QGVAR(PLOS);
navigationTypes[] = { QGVAR(PLOS) };
seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos] seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos]
seekerAngle = 45; // Angle in front of the missile which can be searched seekerAngle = 45; // Angle in front of the missile which can be searched
seekerAccuracy = 1; // seeker accuracy multiplier seekerAccuracy = 1; // seeker accuracy multiplier

View File

@ -4,3 +4,5 @@ PREP(attackProfile);
PREP(keyDown); PREP(keyDown);
PREP(onFired); PREP(onFired);
PREP(seeker); PREP(seeker);
PREP(navigation);
PREP(navigation_onFired);

View File

@ -22,7 +22,11 @@ GVAR(pitchChange) = 0;
// Visual debuging, idealy used with a moving vehicle called "testTarget" // Visual debuging, idealy used with a moving vehicle called "testTarget"
#ifdef DRAW_NLAW_INFO #ifdef DRAW_NLAW_INFO
GVAR(debug_firedPrediction) = [];
addMissionEventHandler ["Draw3d", { addMissionEventHandler ["Draw3d", {
// BLACK - On fired path prediction
{ drawIcon3D _x; } forEach GVAR(debug_firedPrediction);
// GREEN - Draw an object called "testTarget"'s aim pos and 1 sec aimpos predicted by velocity // GREEN - Draw an object called "testTarget"'s aim pos and 1 sec aimpos predicted by velocity
if ((!isNil "testTarget") && {!isNull testTarget}) then { if ((!isNil "testTarget") && {!isNull testTarget}) then {
{ {

View File

@ -18,14 +18,13 @@
*/ */
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"]; params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
#ifdef DRAW_NLAW_INFO
_args params ["_firedEH", "_launchParams"]; _args params ["_firedEH", "_launchParams"];
_launchParams params ["","_targetLaunchParams", "", "_attackProfile"]; _launchParams params ["","_targetLaunchParams", "", "_attackProfile"];
_targetLaunchParams params ["", "", "_launchPos"]; _targetLaunchParams params ["", "", "_launchPos"];
_firedEH params ["","","","","","","_projectile"]; _firedEH params ["","","","","","","_projectile"];
// Use seeker (if terminal)
if (_seekerTargetPos isNotEqualTo [0,0,0]) exitWith {_seekerTargetPos};
_attackProfileStateParams params ["_startTime", "_startLOS", "_yawChange", "_pitchChange"]; _attackProfileStateParams params ["_startTime", "_startLOS", "_yawChange", "_pitchChange"];
(_startLOS call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"]; (_startLOS call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
@ -36,14 +35,6 @@ private _flightTime = CBA_missionTime - _startTime;
private _realYaw = _yaw + _yawChange * _flightTime; private _realYaw = _yaw + _yawChange * _flightTime;
private _realPitch = _pitch + _pitchChange * _flightTime; private _realPitch = _pitch + _pitchChange * _flightTime;
private _returnTargetPos = _launchPos vectorAdd ([_distanceFromLaunch, _realYaw, _realPitch] call CBA_fnc_polar2vect);
if (_attackProfile == QGVAR(overflyTopAttack)) then { // Add 2m height in OTA attack mode
_returnTargetPos = _returnTargetPos vectorAdd [0,0,2];
};
#ifdef DRAW_NLAW_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], ASLtoAGL _launchPos, 0.75, 0.75, 0, "LAUNCH", 1, 0.025, "TahomaB"]; drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], ASLtoAGL _launchPos, 0.75, 0.75, 0, "LAUNCH", 1, 0.025, "TahomaB"];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,1,1], ASLtoAGL (_launchPos vectorAdd (_startLOS vectorMultiply (_distanceFromLaunch + 50))), 0.75, 0.75, 0, "Original LOS", 1, 0.025, "TahomaB"]; drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,1,1], ASLtoAGL (_launchPos vectorAdd (_startLOS vectorMultiply (_distanceFromLaunch + 50))), 0.75, 0.75, 0, "Original LOS", 1, 0.025, "TahomaB"];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,1,0,1], ASLtoAGL (_launchPos vectorAdd ([_distanceFromLaunch + 50, _realYaw, _realPitch] call CBA_fnc_polar2vect)), 0.75, 0.75, 0, format ["Predicted @%1sec",(floor(_flightTime * 10)/10)], 1, 0.025, "TahomaB"]; drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,1,0,1], ASLtoAGL (_launchPos vectorAdd ([_distanceFromLaunch + 50, _realYaw, _realPitch] call CBA_fnc_polar2vect)), 0.75, 0.75, 0, format ["Predicted @%1sec",(floor(_flightTime * 10)/10)], 1, 0.025, "TahomaB"];
@ -56,5 +47,4 @@ if ((count _test) > 0) then {
}; };
#endif #endif
// TRACE_1("Adjusted target position",_returnTargetPos); [0, 0, 1]
_returnTargetPos;

View File

@ -74,7 +74,7 @@ playSound "ACE_Sound_Click";
_args set [1, _yaw]; _args set [1, _yaw];
_args set [2, _pitch]; _args set [2, _pitch];
#ifdef DEBUG_MODE_FULL #ifdef DRAW_NLAW_INFO
hintSilent format ["Instantaneous\nYaw: %1\n Pitch: %2\nGVAR\nYaw: %3\nPitch: %4", _yawChange, _pitchChange, GVAR(yawChange), GVAR(pitchChange)]; hintSilent format ["Instantaneous\nYaw: %1\n Pitch: %2\nGVAR\nYaw: %3\nPitch: %4", _yawChange, _pitchChange, GVAR(yawChange), GVAR(pitchChange)];
#endif #endif
}, .25, [CBA_missionTime, _yaw, _pitch, true]] call CBA_fnc_addPerFrameHandler; }, .25, [CBA_missionTime, _yaw, _pitch, true]] call CBA_fnc_addPerFrameHandler;

View File

@ -0,0 +1,78 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Attempts to hold angle as fed to by seeker. Does so with a simple proportional controller
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_line
*
* Public: No
*/
// arbitrary constant
#define PROPORTIONALITY_CONSTANT 20
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos", "_targetData", "_navigationParams"];
_args params ["_firedEH"];
_firedEH params ["","","","","","","_projectile"];
_navigationParams params ["_yawChange", "_pitchChange", "_lastPitch", "_lastYaw", "_initialPitch", "_pitchUp", "_lastYawRateDifference"];
// for some reason we need to multiply this. I don't know why, but it just works
_pitchChange = _pitchChange * 1.5;
_yawChange = _yawChange * 1.5;
((velocity _projectile) call CBA_fnc_vect2polar) params ["", "_currentYaw", "_currentPitch"];
private _pitchRate = if (_timestep == 0) then {
0
} else {
(_currentPitch - _lastPitch) / _timestep
};
_navigationParams set [2, _currentPitch];
private _pitchModifier = if (_pitchChange == 0) then {
1
} else {
abs (_pitchRate / _pitchChange)
};
private _desiredPitchChange = (_pitchChange - _pitchRate) * PROPORTIONALITY_CONSTANT * _pitchModifier;
_desiredPitchChange = _desiredPitchChange + _pitchUp * (_initialPitch - _currentPitch) * PROPORTIONALITY_CONSTANT * _pitchModifier;
private _yawRate = if (_timestep == 0) then {
0
} else {
(_currentYaw - _lastYaw) / _timestep
};
_navigationParams set [3, _currentYaw];
private _yawModifier = if (_yawChange == 0) then {
1
} else {
abs (_yawRate / _yawChange)
};
private _yawRateDifference = _yawChange - _yawRate;
private _yawChangeDerivative = if (_timestep == 0) then {
0
} else {
(_yawRateDifference - _lastYawRateDifference) / _timestep
};
_navigationParams set [6, _yawRateDifference];
private _desiredYawChange = _yawRateDifference * PROPORTIONALITY_CONSTANT + _yawRateDifference * 2;
#ifdef DRAW_NLAW_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], ASLtoAGL getPosASLVisual _projectile, 0.75, 0.75, 0, format ["dP [%1] dY: [%2]", _desiredPitchChange, _desiredYawChange], 1, 0.025, "TahomaB"];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], [0, 0, 1] vectorAdd ASLtoAGL getPosASLVisual _projectile, 0.75, 0.75, 0, format ["pitch proportional [%1] yaw proportional [%2]", _pitchModifier, _yawModifier], 1, 0.025, "TahomaB"];
#endif
TRACE_4("nlaw pitch/yaw info",_currentPitch,_lastPitch,_currentYaw,_lastYaw);
TRACE_6("nlaw navigation",_yawChange,_desiredYawChange,_pitchChange,_desiredPitchChange,_yawRate,_pitchRate);
_projectile vectorModelToWorldVisual [_yawChange + _desiredYawChange, 0, _pitchChange + _desiredPitchChange]

View File

@ -0,0 +1,70 @@
#include "..\script_component.hpp"
/*
* Author: PabstMirror
* Sets up missile guidance state arrays (called from missileGuidance's onFired).
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Navigation Parameters <ARRAY>
*
* Example:
* [] call ace_nlaw_fnc_onFired
*
* Public: No
*/
params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
_firedEH params ["_shooter","","","","","","_projectile"];
_launchParams params ["","_targetLaunchParams","","_attackProfile"];
_targetLaunchParams params ["_target"];
_stateParams params ["", "", "", "", "_navigationParams"];
// Reset _launchPos origin as projectile's height instead of player's foot
_targetLaunchParams set [2, getPosASL _projectile];
// Get state params:
TRACE_3("start of attack profile",_attackProfile,_shooter,vectorDir _projectile);
private _firedLOS = _shooter weaponDirection (currentWeapon _shooter);
private _yawChange = 0;
private _pitchChange = 0;
if (_shooter == ACE_player) then {
TRACE_2("isPlayer",GVAR(yawChange),GVAR(pitchChange));
_yawChange = GVAR(yawChange);
_pitchChange = GVAR(pitchChange);
TRACE_1("los check",_firedLOS call CBA_fnc_vect2Polar);
} else {
if ((!isNil "_target") && {!isNull _target}) then {
_firedLOS = (getPosASL _projectile) vectorFromTo (aimPos _target);
(((eyePos _shooter) vectorFromTo (aimPos _target)) call CBA_fnc_vect2Polar) params ["", "_startYaw", "_startPitch"];
// Add some random error to AI's velocity prediction:
private _random = random [(_shooter skillFinal "aimingAccuracy") min 0.9, 1, 2-((_shooter skillFinal "aimingAccuracy") min 0.9)];
(((eyePos _shooter) vectorFromTo ((aimPos _target) vectorAdd ((velocity _target) vectorMultiply (_random)))) call CBA_fnc_vect2Polar) params ["", "_predictedYaw", "_predictedPitch"];
_yawChange = ([_predictedYaw - _startYaw] call CBA_fnc_simplifyAngle180);
_pitchChange = ([_predictedPitch - _startPitch] call CBA_fnc_simplifyAngle180);
TRACE_1("AI",_target);
} else {
TRACE_1("AI - no target",_target);
};
};
// Limit Max Deflection
//_yawChange = -10 max _yawChange min 10;
//_pitchChange = -10 max _pitchChange min 10;
((velocity _projectile) call CBA_fnc_vect2polar) params ["", "_currentYaw", "_currentPitch"];
((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
TRACE_5("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange,_currentPitch,_currentYaw);
_navigationParams set [0, _yawChange];
_navigationParams set [1, _pitchChange];
_navigationParams set [2, _currentPitch]; // last pitch
_navigationParams set [3, _currentYaw]; // last yaw
_navigationParams set [4, _pitch]; // initial pitch
_navigationParams set [5, 0]; // whether or not to zero out the pitch
_navigationParams set [6, 0];
_navigationParams

View File

@ -19,7 +19,7 @@ params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_statePa
_firedEH params ["_shooter","","","","","","_projectile"]; _firedEH params ["_shooter","","","","","","_projectile"];
_launchParams params ["","_targetLaunchParams","","_attackProfile"]; _launchParams params ["","_targetLaunchParams","","_attackProfile"];
_targetLaunchParams params ["_target"]; _targetLaunchParams params ["_target"];
_stateParams params ["", "", "_attackProfileStateParams"]; _stateParams params ["", "_seekerStateParams", "_attackProfileStateParams"];
// Reset _launchPos origin as projectile's height instead of player's foot // Reset _launchPos origin as projectile's height instead of player's foot
_targetLaunchParams set [2, getPosASL _projectile]; _targetLaunchParams set [2, getPosASL _projectile];
@ -27,7 +27,7 @@ _targetLaunchParams set [2, getPosASL _projectile];
// Get state params: // Get state params:
TRACE_3("start of attack profile",_attackProfile,_shooter,vectorDir _projectile); TRACE_3("start of attack profile",_attackProfile,_shooter,vectorDir _projectile);
private _firedLOS = _shooter weaponDirection (currentWeapon _shooter); private _firedLOS = vectorDir _projectile;
private _yawChange = 0; private _yawChange = 0;
private _pitchChange = 0; private _pitchChange = 0;
@ -36,6 +36,23 @@ if (_shooter == ACE_player) then {
_yawChange = GVAR(yawChange); _yawChange = GVAR(yawChange);
_pitchChange = GVAR(pitchChange); _pitchChange = GVAR(pitchChange);
TRACE_1("los check",_firedLOS call CBA_fnc_vect2Polar); TRACE_1("los check",_firedLOS call CBA_fnc_vect2Polar);
#ifdef DRAW_NLAW_INFO
systemChat format ["YAW [%1]", _yawChange];
systemChat format ["PITCH [%1]", _pitchChange];
GVAR(debug_firedPrediction) = [];
private _debugPos = getPosASL _projectile;
((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_debugYaw", "_debugPitch"];
private _distance = 0;
for "_x" from 0 to 6 step 0.1 do {
private _debugAproxVel = linearConversion [0, 1, 5, 40, 170, true];
_distance = _distance + _debugAproxVel * 0.1;
private _debugYaw = _debugYaw + _yawChange * _x;
private _debugPitch = _debugPitch + _pitchChange * _x;
private _debugPos = _debugPos vectorAdd ([_distance, _debugYaw, _debugPitch] call CBA_fnc_polar2vect);
GVAR(debug_firedPrediction) pushBack ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,0,0,1], ASLtoAGL _debugPos, 0.5, 0.5, 0, format ["%1", _x], 1, 0.025, "TahomaB"];
};
#endif
} else { } else {
if ((!isNil "_target") && {!isNull _target}) then { if ((!isNil "_target") && {!isNull _target}) then {
_firedLOS = (getPosASL _projectile) vectorFromTo (aimPos _target); _firedLOS = (getPosASL _projectile) vectorFromTo (aimPos _target);
@ -55,6 +72,10 @@ if (_shooter == ACE_player) then {
_yawChange = -10 max _yawChange min 10; _yawChange = -10 max _yawChange min 10;
_pitchChange = -10 max _pitchChange min 10; _pitchChange = -10 max _pitchChange min 10;
_seekerStateParams set [2, _yawChange];
_seekerStateParams set [3, _pitchChange];
_seekerStateParams set [4, CBA_missionTime];
TRACE_3("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange); TRACE_3("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange);
_attackProfileStateParams set [0, CBA_missionTime]; _attackProfileStateParams set [0, CBA_missionTime];
_attackProfileStateParams set [1, _firedLOS]; _attackProfileStateParams set [1, _firedLOS];

View File

@ -16,14 +16,27 @@
* *
* Public: No * Public: No
*/ */
#define PITCH_UP_TIME 1
params ["", "_args", "_seekerStateParams"]; params ["", "_args", "_seekerStateParams", "", "", "_targetData"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"]; _args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"];
_firedEH params ["","","","","","","_projectile"]; _firedEH params ["","","","","","","_projectile"];
_launchParams params ["", "_targetLaunchParams", "", "_attackProfile"]; _launchParams params ["", "_targetLaunchParams", "", "_attackProfile"];
_targetLaunchParams params ["", "", "_launchPos"]; _targetLaunchParams params ["", "", "_launchPos"];
_stateParams params ["", "", "", "", "_navigationParams"];
if (_attackProfile == QGVAR(directAttack)) exitWith {[0,0,0]}; if (_attackProfile == QGVAR(directAttack)) exitWith {
_navigationParams set [5, 1];
[0,0,0]
};
_seekerStateParams params ["", "", "", "_originalPitchRate", "_startTime"];
_navigationParams params ["", "_pitchRate"];
// pitch up for the first second of flight to begin an over-fly trajectory
private _pitchChange = linearConversion [0, PITCH_UP_TIME, CBA_missionTime - _startTime, 2, 0, true];
_navigationParams set [1, _originalPitchRate + _pitchChange];
_navigationParams set [5, ((CBA_missionTime - _startTime) min PITCH_UP_TIME) / PITCH_UP_TIME];
private _projPos = getPosASL _projectile; private _projPos = getPosASL _projectile;