ActiveProtect/Models/Missile.cs

342 lines
14 KiB
C#

using System;
using ActiveProtect.SimulationEnvironment;
namespace ActiveProtect.Models
{
/// <summary>
/// 表示仿真中的导弹
/// </summary>
public class Missile : SimulationElement
{
/// <summary>
/// 导弹类型
/// </summary>
public MissileType Type { get; protected set; }
/// <summary>
/// 导弹飞行的各个阶段
/// </summary>
public enum FlightStage
{
Launch, // 发射阶段
Acceleration, // 加速阶段
Cruise, // 巡航阶段
TerminalGuidance, // 终端制导阶段
Attack, // 攻击阶段
Explosion // 爆炸阶段
}
public double Speed { get; protected set; }
public double MaxSpeed { get; protected set; }
public string TargetId { get; protected set; }
public double MaxFlightTime { get; protected set; }
public double MaxFlightDistance { get; protected set; }
public double FlightTime { get; protected set; }
public double FlightDistance { get; protected set; }
public double DistanceToTarget { get; protected set; }
public MissileDistanceParams DistanceParams { get; protected set; }
public FlightStageConfig StageConfig { get; protected set; }
public FlightStage CurrentStage { get; protected set; }
public bool IsActive { get; protected set; }
protected Vector3D Velocity;
private Vector3D LastTargetPosition;
private const double N = 3; // 比例导引系数
public double ThrustAcceleration { get; protected set; }
public double EngineBurnTime { get; protected set; }
public double MaxEngineBurnTime { get; protected set; }
public double MaxAcceleration { get; protected set; } = 100; // m/s^2
public double ProportionalNavigationCoefficient { get; set; }
public bool IsGuidanceLost { get; protected set; } = false;
protected double LostGuidanceTime { get; set; } = 0;
protected const double MaxLostGuidanceTime = 1.0; // 1秒后自毁
public Missile(string id, MissileConfig missileConfig, ISimulationManager simulationManager)
: base(id, missileConfig.InitialPosition, missileConfig.InitialOrientation, simulationManager)
{
Speed = missileConfig.InitialSpeed;
MaxSpeed = missileConfig.MaxSpeed;
MaxFlightTime = missileConfig.MaxFlightTime;
MaxFlightDistance = missileConfig.MaxFlightDistance;
DistanceParams = missileConfig.DistanceParams;
StageConfig = missileConfig.StageConfig;
IsActive = true;
FlightTime = 0;
FlightDistance = 0;
SimulationManager = simulationManager;
ThrustAcceleration = missileConfig.ThrustAcceleration;
EngineBurnTime = 0;
MaxEngineBurnTime = missileConfig.MaxEngineBurnTime;
MaxAcceleration = missileConfig.MaxAcceleration;
ProportionalNavigationCoefficient = missileConfig.ProportionalNavigationCoefficient;
TargetId = $"Tank_{missileConfig.TargetIndex + 1}";
Vector3D horizontalDirection = new Vector3D(Orientation.ToVector().X, 0, Orientation.ToVector().Z).Normalize();
Velocity = horizontalDirection * missileConfig.InitialSpeed;
// 设置初始阶段
CurrentStage = StageConfig.EnableLaunch ? FlightStage.Launch :
StageConfig.EnableAcceleration ? FlightStage.Acceleration :
StageConfig.EnableCruise ? FlightStage.Cruise :
StageConfig.EnableTerminalGuidance ? FlightStage.TerminalGuidance :
StageConfig.EnableAttack ? FlightStage.Attack : FlightStage.Cruise;
Console.WriteLine($"导弹 {Id} 的初始阶: {CurrentStage}");
LastTargetPosition = Position; // 初始化 LastTargetPosition
// 订阅相关事件
simulationManager.SubscribeToEvent<LaserIlluminationEvent>(OnLaserIllumination);
simulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
public override void Update(double deltaTime)
{
if (!IsActive) return;
// 更新发动机燃烧时间
UpdateEngineBurnTime(deltaTime);
// 使用 RK4 方法更新导弹状态
Vector3D k1, k2, k3, k4;
Vector3D v1, v2, v3, v4;
(k1, v1) = CalculateDerivatives(Position, Velocity, deltaTime);
(k2, v2) = CalculateDerivatives(Position + k1 * (deltaTime / 2), Velocity + v1 * (deltaTime / 2), deltaTime / 2);
(k3, v3) = CalculateDerivatives(Position + k2 * (deltaTime / 2), Velocity + v2 * (deltaTime / 2), deltaTime / 2);
(k4, v4) = CalculateDerivatives(Position + k3 * deltaTime, Velocity + v3 * deltaTime, deltaTime);
Position += (k1 + k2 * 2 + k3 * 2 + k4) * (deltaTime / 6);
Velocity += (v1 + v2 * 2 + v3 * 2 + v4) * (deltaTime / 6);
// 更新其他状态
Speed = Velocity.Magnitude();
if (Speed > MaxSpeed)
{
Speed = MaxSpeed;
Velocity = Velocity.Normalize() * MaxSpeed;
}
Orientation = Orientation.FromVector(Velocity);
FlightTime += deltaTime;
FlightDistance += Speed * deltaTime;
UpdateDistanceToTarget(Vector3D.Distance(Position, SimulationManager.GetEntityById(TargetId).Position));
// 首先检查是否命中目标
if (CheckHit())
{
Explode();
return;
}
// 然后检查是否应该自毁
if (ShouldSelfDestruct())
{
SelfDestruct();
return;
}
// 更新飞行阶段
UpdateFlightStage();
if (IsGuidanceLost)
{
HandleLostGuidance(deltaTime);
}
else
{
// 重置失去引导的时间
LostGuidanceTime = 0;
}
// 发布事件示例
if (CurrentStage == FlightStage.Launch)
{
PublishEvent(new MissileFireEvent { TargetId = TargetId });
}
}
private bool CheckHit()
{
return DistanceToTarget <= DistanceParams.ExplosionDistance;
}
private bool ShouldSelfDestruct()
{
return FlightTime >= MaxFlightTime || FlightDistance >= MaxFlightDistance || Position.Y <= 0;
}
private void UpdateEngineBurnTime(double deltaTime)
{
if (CurrentStage == FlightStage.Acceleration && EngineBurnTime < MaxEngineBurnTime)
{
EngineBurnTime += deltaTime;
}
}
private (Vector3D, Vector3D) CalculateDerivatives(Vector3D position, Vector3D velocity, double deltaTime)
{
Vector3D targetPosition = SimulationManager.GetEntityById(TargetId).Position;
Vector3D LOS = targetPosition - position;
Vector3D LOSRate = (targetPosition - LastTargetPosition) / deltaTime - velocity;
Vector3D guidanceAcceleration = Vector3D.CrossProduct(Vector3D.CrossProduct(LOS, LOSRate), LOS).Normalize()
* ProportionalNavigationCoefficient * velocity.Magnitude();
LastTargetPosition = targetPosition;
// 添加推力加速度
Vector3D thrustAcceleration = (CurrentStage == FlightStage.Acceleration && EngineBurnTime < MaxEngineBurnTime)
? Orientation.ToVector() * ThrustAcceleration
: Orientation.ToVector() * (ThrustAcceleration * 0.1); // 小的持续推力
// 添加重力补偿
Vector3D gravityCompensation = new Vector3D(0, 9.81, 0);
guidanceAcceleration += gravityCompensation;
Vector3D totalAcceleration = guidanceAcceleration + thrustAcceleration;
// 添加重力加速度
Vector3D gravityAcceleration = new(0, -9.81, 0);
totalAcceleration += gravityAcceleration;
// 限制最大加速度
if (totalAcceleration.Magnitude() > MaxAcceleration)
{
totalAcceleration = totalAcceleration.Normalize() * MaxAcceleration;
}
return (velocity, totalAcceleration);
}
private void UpdateFlightStage()
{
switch (CurrentStage)
{
case FlightStage.Launch:
if (StageConfig.EnableAcceleration) CurrentStage = FlightStage.Acceleration;
else if (StageConfig.EnableCruise) CurrentStage = FlightStage.Cruise;
else if (StageConfig.EnableTerminalGuidance) CurrentStage = FlightStage.TerminalGuidance;
else if (StageConfig.EnableAttack) CurrentStage = FlightStage.Attack;
break;
case FlightStage.Acceleration:
if (EngineBurnTime >= MaxEngineBurnTime || Speed >= MaxSpeed * 0.95)
{
if (StageConfig.EnableCruise) CurrentStage = FlightStage.Cruise;
else if (StageConfig.EnableTerminalGuidance) CurrentStage = FlightStage.TerminalGuidance;
else if (StageConfig.EnableAttack) CurrentStage = FlightStage.Attack;
}
break;
case FlightStage.Cruise:
if (StageConfig.EnableTerminalGuidance && DistanceToTarget <= DistanceParams.TerminalGuidanceDistance)
CurrentStage = FlightStage.TerminalGuidance;
else if (StageConfig.EnableAttack && DistanceToTarget <= DistanceParams.AttackDistance)
CurrentStage = FlightStage.Attack;
break;
case FlightStage.TerminalGuidance:
if (StageConfig.EnableAttack && DistanceToTarget <= DistanceParams.AttackDistance)
CurrentStage = FlightStage.Attack;
break;
case FlightStage.Attack:
if (DistanceToTarget <= DistanceParams.ExplosionDistance)
Explode();
break;
}
}
public void UpdateDistanceToTarget(double distance)
{
DistanceToTarget = distance;
}
public void Explode()
{
IsActive = false;
SimulationManager.HandleTargetHit(TargetId, Id);
Console.WriteLine($"导弹 {Id} 在 {Position} 爆炸,命中目标!");
}
public void SelfDestruct()
{
IsActive = false;
string reason = FlightTime >= MaxFlightTime ? "超出最大飞行时间" :
FlightDistance >= MaxFlightDistance ? "超出最大飞行距" :
Position.Y <= 0 ? "高度小于等于0" :
IsGuidanceLost ? "失去引导" : "未知原因";
Console.WriteLine($"导弹 {Id} 自毁。原因: {reason}");
}
public override string GetStatus()
{
return $"导弹 {Id}:\n" +
$" 位置: {Position}\n" +
$" 速度: {Speed:F2} m/s\n" +
$" 当前阶段: {CurrentStage}\n" +
$" 飞行时间: {FlightTime:F2}/{MaxFlightTime:F2}\n" +
$" 飞行距离: {FlightDistance:F2}/{MaxFlightDistance:F2}\n" +
$" 距离目标: {DistanceToTarget:F2}\n" +
$" 发动机工作时间: {EngineBurnTime:F2}/{MaxEngineBurnTime:F2}\n" +
$" 失去引导: {(IsGuidanceLost ? "" : "")}\n" +
$" 失去引导时间: {LostGuidanceTime:F2}/{MaxLostGuidanceTime:F2}";
}
public void SetProportionalNavigationCoefficient(double newCoefficient)
{
ProportionalNavigationCoefficient = newCoefficient;
Console.WriteLine($"导弹 {Id} 的比例导引系数已更新为 {newCoefficient}");
}
protected virtual void LoseGuidance()
{
if (!IsGuidanceLost)
{
Console.WriteLine($"导弹 {Id} 失去引导");
IsGuidanceLost = true;
}
}
protected virtual void HandleLostGuidance(double deltaTime)
{
LostGuidanceTime += deltaTime;
if (LostGuidanceTime >= MaxLostGuidanceTime)
{
Console.WriteLine($"导弹 {Id} 失去引导超过 {MaxLostGuidanceTime} 秒,自毁");
SelfDestruct();
}
}
private void OnLaserIllumination(LaserIlluminationEvent evt)
{
if (evt.TargetId == TargetId)
{
Console.WriteLine($"导弹 {Id} 检测到目标 {TargetId} 被激光照射");
RegainGuidance();
}
}
private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
{
if (evt.TargetId == TargetId)
{
Console.WriteLine($"导弹 {Id} 检测到目标 {TargetId} 激光照射停止");
LoseGuidance();
}
}
protected virtual void RegainGuidance()
{
if (IsGuidanceLost)
{
Console.WriteLine($"导弹 {Id} 重新获得引导");
IsGuidanceLost = false;
LostGuidanceTime = 0;
}
}
}
}