Skip to content

Commit

Permalink
GunManager: use exact targeting solution
Browse files Browse the repository at this point in the history
- Ported from the FixedGuns implementation.
- Thanks to azieba for writing the solution in the first place.
- Uses an exact solution to the question of when the projectile will get there, and then an approximation of the ship's acceleration over that time.
- Provides quite a bit better probability-of-hit for ballistic weapons
  • Loading branch information
sturnclaw committed Dec 7, 2024
1 parent ab67426 commit 771dc70
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 15 deletions.
59 changes: 45 additions & 14 deletions src/ship/GunManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "GunManager.h"

#include "Beam.h"
#include "DynamicBody.h"
#include "Projectile.h"
#include "Game.h"
#include "ModelBody.h"
Expand Down Expand Up @@ -366,9 +367,14 @@ void GunManager::StaticUpdate(float deltaTime)
const matrix3x3d &orient = m_parent->GetOrient();
const vector3d relPosition = gs.target->GetPositionRelTo(m_parent);
const vector3d relVelocity = gs.target->GetVelocityRelTo(m_parent->GetFrame()) - m_parent->GetVelocity();
vector3d relAccel = vector3d(0, 0, 0);

// bring velocity and acceleration into ship-space
CalcWeaponLead(&gun, relPosition * orient, relVelocity * orient);
if (gs.target->IsType(ObjectType::DYNAMICBODY)) {
relAccel = static_cast<const DynamicBody *>(gs.target)->GetLastForce() / gs.target->GetMass();
}

// bring position, velocity and acceleration into ship-space
CalcWeaponLead(&gun, relPosition * orient, relVelocity * orient, relAccel * orient);
} else {
gun.currentLead = vector3f(0, 0, 1);
gun.currentLeadPos = vector3d(0, 0, 0);
Expand Down Expand Up @@ -459,7 +465,7 @@ void GunManager::Fire(WeaponState *weapon, GroupState *group)
}

// Note that position and relative velocity are in the coordinate system of the host body
void GunManager::CalcWeaponLead(WeaponState *state, vector3d position, vector3d relativeVelocity)
void GunManager::CalcWeaponLead(WeaponState *state, vector3d position, vector3d relativeVelocity, vector3d relativeAccel)
{
// Compute the forward vector for the weapon mount
const matrix4x4f &xform = GetMountTransform(state);
Expand All @@ -472,17 +478,42 @@ void GunManager::CalcWeaponLead(WeaponState *state, vector3d position, vector3d
// Account for the distance between the weapon mount and the center of the parent
position -= vector3d(xform.GetTranslate());

// 1. First, generate a basic approximation to estimate travel time
vector3d leadpos = position + relativeVelocity * (position.Length() / projspeed);
// 2. This second-order approximation yields a much more accurate estimate
float travelTime = leadpos.Length() / projspeed;
leadpos = position + relativeVelocity * travelTime;
// A third-order approximation here would need to take into account the target's
// expected acceleration over the duration by using a moving average of acceleration
// rather than taking the instantaneous forces acting on the body.
// Thus, we refrain from implementing it for now.

state->currentLeadPos = leadpos;
//Exact lead calculation. We start with:
// |targpos * l + targvel| = projspeed
//we solve for l which can be interpreted as 1/time for the projectile to reach the target
//it gives:
// |targpos|^2 * l^2 + targpos*targvel * 2l + |targvel|^2 - projspeed^2 = 0;
// so it gives scalar quadratic equation with two possible solutions - we care only about the positive one - shooting forward
// A basic math for solving, there is probably more elegant and efficient way to do this:
double a = position.LengthSqr();
double b = position.Dot(relativeVelocity) * 2;
double c = relativeVelocity.LengthSqr() - projspeed * projspeed;
double delta = b * b - 4 * a * c;

vector3d leadPos = position;

if (delta >= 0) {
//l = (-b + sqrt(delta)) / 2a; t=1/l; a>0
double t = 2 * a / (-b + sqrt(delta));

if (t < 0 || t > state->data.projectile.lifespan) {
//no positive solution or target too far
} else {
//This is an exact solution as opposed to 2 step approximation used before.
//It does not improve the accuracy as expected though.
//If the target is accelerating and is far enough then this aim assist will
//actually make sure that it is mpossible to hit..
leadPos = position + relativeVelocity * t;

//lets try to adjust for acceleration of the target ship
//s=a*t^2/2 -> hitting steadily accelerating ships works at much greater distance
leadPos += relativeAccel * t * t * 0.5;
}
} else {
//no solution
}

state->currentLeadPos = leadPos;
} else if (state->data.projectileType == PROJECTILE_BEAM) {
// Beam weapons should just aim at the target
state->currentLeadPos = position;
Expand Down
2 changes: 1 addition & 1 deletion src/ship/GunManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class GunManager : public LuaWrappable {

// Calculate the position a given gun should aim at to hit the current target body
// This is effectively the position of the target at T+n
void CalcWeaponLead(WeaponState *state, vector3d position, vector3d relativeVelocity);
void CalcWeaponLead(WeaponState *state, vector3d position, vector3d relativeVelocity, vector3d relativeAccel);

const matrix4x4f &GetMountTransform(WeaponState *weapon);

Expand Down

0 comments on commit 771dc70

Please sign in to comment.