Make homing projectiles travel at a constant horizontal speed.

Homing projectiles previously behaved like expanding circles, where the projectile was drawn
at the closest point on the circle to the target. Which meant that if the target was fast,
moving at right angles to the direction of the projectile, the projectile would move fast,
too (and would not be travelling in the direction it was facing).

Homing projectiles now go at a constant speed towards the target, using target prediction.
To test the target prediction, set the projectile speed to something close to 700 (same as a
VTOL), and try to get the VTOL to escape.
master
Cyp 2011-12-25 23:48:17 +01:00
parent 01e8e2ebd4
commit e727873581
2 changed files with 27 additions and 2 deletions

View File

@ -24,6 +24,9 @@
#ifndef _gtime_h #ifndef _gtime_h
#define _gtime_h #define _gtime_h
#include "lib/framework/vector.h"
struct NETQUEUE; struct NETQUEUE;
struct Rational; struct Rational;
@ -173,6 +176,13 @@ static inline int32_t gameTimeAdjustedAverage(int numerator, int denominator)
{ {
return quantiseFraction(numerator, GAME_TICKS_PER_SEC*denominator, gameTime + deltaGameTime, gameTime); return quantiseFraction(numerator, GAME_TICKS_PER_SEC*denominator, gameTime + deltaGameTime, gameTime);
} }
/// Returns the numerator/denominator times deltaGameTime, converted to seconds. The return value is rounded up or down, such that it is exactly right on average.
static inline Vector3i gameTimeAdjustedAverage(Vector3i numerator, int denominator)
{
return Vector3i(quantiseFraction(numerator.x, GAME_TICKS_PER_SEC*denominator, gameTime + deltaGameTime, gameTime),
quantiseFraction(numerator.y, GAME_TICKS_PER_SEC*denominator, gameTime + deltaGameTime, gameTime),
quantiseFraction(numerator.z, GAME_TICKS_PER_SEC*denominator, gameTime + deltaGameTime, gameTime));
}
void sendPlayerGameTime(void); ///< Sends a GAME_GAME_TIME message with gameTime plus latency to our game queues. void sendPlayerGameTime(void); ///< Sends a GAME_GAME_TIME message with gameTime plus latency to our game queues.
void recvPlayerGameTime(NETQUEUE queue); ///< Processes a GAME_GAME_TIME message. void recvPlayerGameTime(NETQUEUE queue); ///< Processes a GAME_GAME_TIME message.

View File

@ -755,11 +755,26 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
// If it's homing and has a target (not a miss)... // If it's homing and has a target (not a miss)...
// Home at the centre of the part that was visible when firing. // Home at the centre of the part that was visible when firing.
psProj->dst = psProj->psDest->pos + Vector3i(0, 0, establishTargetHeight(psProj->psDest) - psProj->partVisible/2); psProj->dst = psProj->psDest->pos + Vector3i(0, 0, establishTargetHeight(psProj->psDest) - psProj->partVisible/2);
DROID *targetDroid = castDroid(psProj->psDest);
if (targetDroid != NULL)
{
// Do target prediction.
Vector3i delta = psProj->dst - psProj->pos;
int flightTime = iHypot(removeZ(delta)) * GAME_TICKS_PER_SEC / psStats->flightSpeed;
psProj->dst += Vector3i(iSinCosR(targetDroid->sMove.moveDir, targetDroid->sMove.speed*flightTime / GAME_TICKS_PER_SEC), 0);
}
} }
Vector3i delta = psProj->dst - psProj->src; Vector3i delta = psProj->dst - psProj->pos;
int targetDistance = std::max(iHypot(removeZ(delta)), 1); int targetDistance = std::max(iHypot(removeZ(delta)), 1);
if (psProj->psDest == NULL && targetDistance < 10000)
{
psProj->dst = psProj->pos + delta*10; // Target missing, so just keep going in a straight line.
}
currentDistance = timeSoFar * psStats->flightSpeed / GAME_TICKS_PER_SEC; currentDistance = timeSoFar * psStats->flightSpeed / GAME_TICKS_PER_SEC;
psProj->pos = psProj->src + delta * currentDistance/targetDistance; Vector3i step = gameTimeAdjustedAverage(delta * psStats->flightSpeed, targetDistance);
psProj->pos += step;
psProj->rot.direction = iAtan2(removeZ(delta));
psProj->rot.pitch = iAtan2(delta.z, targetDistance);
break; break;
} }
default: default: