如何用线性速度、位置数据精准射击移动目标?C#实现咨询
精准击杀移动目标的计算方案与代码优化
问题场景
我方飞船(黑色右箭头)与敌方目标(红色圆圈,半径1.2)均做无加速运动,需仅通过位置、线性速度、半径数据实现精准击杀。
备注:子弹生命周期0.5秒,速度40,半径0.2。
当前实现代码
用户尝试用C#实现该功能,代码如下:
public static Vector3 CalculatePreciseShootDirection(Vector3 shipPosition, Vector3 shipVelocity, Vector3 targetPosition, Vector3 targetVelocity, float bulletLifetime, float bulletSpeed) { Vector3 shipFuturePosition = shipPosition + bulletLifetime * shipVelocity; Vector3 targetFuturePosition = targetPosition + bulletLifetime * targetVelocity; Vector3 relativeDisplacement = targetFuturePosition - shipFuturePosition; float timeToTarget = Vector3Magnitude(relativeDisplacement) / bulletSpeed; Vector3 adjustedTargetPosition = targetPosition + timeToTarget * targetVelocity; Vector3 shootingDirection = adjustedTargetPosition - shipPosition; float shootingDistance = Vector3Magnitude(shootingDirection); float maxDistance = bulletLifetime * bulletSpeed; if (shootingDistance <= maxDistance) { return Vector3.Normalize(shootingDirection); } else { return Vector3.Zero; } } private static float Vector3Magnitude(Vector3 v) { return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z); }
现有问题与改进方案
1. 核心逻辑偏差
当前代码先假设子弹飞行满生命周期来计算双方未来位置,再反推飞行时间,这个逻辑存在循环矛盾——正确的做法是直接建立子弹飞行轨迹与目标移动轨迹的交汇方程,求解子弹击中目标的合法时间。
设:
- 我方位置:$S_0$,速度:$V_s$
- 敌方位置:$T_0$,速度:$V_t$
- 子弹速度:$V_b$(标量),飞行时间:$t$
- 敌方半径:$R_t$,子弹半径:$R_b$
子弹发射后,我方位置随时间变化为 $S(t) = S_0 + V_s \cdot t$,敌方位置为 $T(t) = T_0 + V_t \cdot t$。子弹从我方当前位置发射,飞行方向为单位向量 $\vec{d}$,则子弹位置为 $B(t) = S_0 + V_b \cdot \vec{d} \cdot t$。
击中条件为:$|B(t) - T(t)| \leq R_t + R_b$(考虑双方体积的碰撞判定),同时 $0 < t \leq t_{max}$($t_{max}$为子弹生命周期)。
代入位置整理后,可得到关于 $t$ 的二次方程:
$$(\Delta V \cdot \Delta V - V_b^2) t^2 + 2 (\Delta S \cdot \Delta V) t + (\Delta S \cdot \Delta S - (R_t + R_b)^2) = 0$$
其中:
- 初始相对位移 $\Delta S = T_0 - S_0$
- 相对速度 $\Delta V = V_t - V_s$
求解该方程,筛选出合法的正实数时间(不超过子弹生命周期),再代入计算击中时的目标位置,即可得到准确射击方向。
2. 代码优化点
- 用Unity内置的
Vector3.Magnitude/Vector3.SqrMagnitude替代自定义的Vector3Magnitude,提升性能 - 加入碰撞半径的判定,避免忽略目标和子弹体积导致的误差
- 修正核心逻辑,通过求解二次方程得到准确击中时间
优化后的代码示例
public static Vector3 CalculatePreciseShootDirection(Vector3 shipPos, Vector3 shipVel, Vector3 targetPos, Vector3 targetVel, float bulletLifetime, float bulletSpeed, float targetRadius, float bulletRadius) { float combinedRadius = targetRadius + bulletRadius; Vector3 deltaPos = targetPos - shipPos; Vector3 deltaVel = targetVel - shipVel; // 二次方程参数:a*t² + b*t + c = 0 float a = Vector3.Dot(deltaVel, deltaVel) - bulletSpeed * bulletSpeed; float b = 2 * Vector3.Dot(deltaPos, deltaVel); float c = Vector3.Dot(deltaPos, deltaPos) - combinedRadius * combinedRadius; // 求解判别式,无实根则无法击中 float discriminant = b * b - 4 * a * c; if (discriminant < 0) { return Vector3.zero; } float sqrtDiscriminant = Mathf.Sqrt(discriminant); float t1 = (-b + sqrtDiscriminant) / (2 * a); float t2 = (-b - sqrtDiscriminant) / (2 * a); // 筛选合法时间:正且不超过子弹生命周期,优先取最短时间 float validTime = float.MaxValue; if (t1 > 0 && t1 <= bulletLifetime) { validTime = t1; } if (t2 > 0 && t2 <= bulletLifetime && t2 < validTime) { validTime = t2; } if (validTime == float.MaxValue) { return Vector3.zero; } // 计算击中时的目标位置,生成射击方向 Vector3 hitTargetPos = targetPos + targetVel * validTime; Vector3 shootDir = hitTargetPos - shipPos; return shootDir.sqrMagnitude < 0.001f ? Vector3.zero : shootDir.normalized; }
说明
- 该方案考虑了目标与子弹的体积碰撞,通过数学方程求解得到精准的击中时间,避免了原逻辑的循环假设问题
- 返回
Vector3.zero表示当前条件下无法在子弹生命周期内击中目标
内容的提问来源于stack exchange,提问作者PerduGames
相关产品推荐
相关产品推荐

