Compare commits
3 Commits
da4e8d5c63
...
e250093a16
| Author | SHA1 | Date | |
|---|---|---|---|
| e250093a16 | |||
| 3bb6e7a091 | |||
| ff830c1221 |
@ -1,7 +1,7 @@
|
||||
# 后端对接文档(Unity 前端)
|
||||
|
||||
> **版本**:V1.3
|
||||
> **日期**:2026-06-15
|
||||
> **版本**:V1.4
|
||||
> **日期**:2026-06-17
|
||||
> **Unity 版本**:2022.3.62f3c1
|
||||
|
||||
---
|
||||
@ -60,7 +60,7 @@ runner.Engine.TimeScale = 4f; // 加速
|
||||
|
||||
// 订阅事件
|
||||
runner.Engine.OnMunitionLaunched += m => Instantiate(shellPrefab, ToVector3(m.PosX, m.PosY, m.PosZ));
|
||||
runner.Engine.OnCloudGenerated += c => SpawnCloud(c.Dispersion.Center, c.Dispersion.Radius);
|
||||
runner.Engine.OnCloudGenerated += c => SpawnCloud(c.PosX, c.PosY, c.PosZ, c.Radius);
|
||||
runner.Engine.OnDroneDestroyed += d => PlayExplosion(d.PosX, d.PosY, d.PosZ);
|
||||
runner.Engine.OnTargetDetected += (d, det) => ShowDetectionMark(d.PosX, d.PosY, d.PosZ);
|
||||
runner.Engine.OnDroneReachedTarget += d => ShowReached(d.PosX, d.PosY, d.PosZ);
|
||||
@ -132,7 +132,7 @@ var runner = GetComponent<SimulationRunner>();
|
||||
runner.Engine.SetFireSchedule(fireEvents); // 从推荐方案取
|
||||
runner.LoadAndStart(taskId);
|
||||
runner.Engine.TimeScale = 4f;
|
||||
runner.Engine.State / Drones / Clouds / Munitions / DetectionEntities / Events
|
||||
runner.Engine.State / Drones / Clouds / Munitions / Platforms / DetectionEntities / Events
|
||||
runner.Stop();
|
||||
```
|
||||
|
||||
@ -155,6 +155,32 @@ replay.LoadReplay(taskId);
|
||||
// replay.TotalFrames / replay.GetFrame(frameIndex)
|
||||
```
|
||||
|
||||
### EntitySnapshot(每帧推送)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `EntityId` | string | 实体唯一 ID |
|
||||
| `EntityType` | enum | Drone/Platform/Munition/Cloud/DetectionEquip |
|
||||
| `PosX/Y/Z` | float | 三维位置 |
|
||||
| `VelX/Y/Z` | float | 瞬时速度 (m/s) |
|
||||
| `Hp` | float | 无人机血量 (0~1) |
|
||||
| `DamageStage` | int | 损伤阶段 (0~N) |
|
||||
| `PlatformStateStr` | string | 平台状态 (Idle/FlyingToTarget/ReadyToRelease) |
|
||||
| `CloudRadius` | float | 云团有效半径 |
|
||||
| `CloudOpacity` | float | 云团不透明度 (0~1) |
|
||||
| `CloudPhase` | int | 云团扩散阶段 (1/2/3) |
|
||||
| `CloudElapsed` | float | 云团已存在时间 (s) |
|
||||
|
||||
### 实体属性(直接访问)
|
||||
|
||||
| 实体 | 关键属性 |
|
||||
|------|------|
|
||||
| **DroneEntity** | Id, PosX/Y/Z, Hp, Status, ExposureTime, TargetType, PowerType, Wingspan, CruiseSpeed, Route, TraveledArc, TotalArc, Progress |
|
||||
| **CloudEntity** | Id, PosX/Y/Z, Radius, Density, Phase, Elapsed, IsDissipated, AerosolType |
|
||||
| **MunitionEntity** | Id, PosX/Y/Z, Velocity, StartX/Y/Z, LaunchTime, ElapsedTime, LaunchAngle, Azimuth, MuzzleVelocity, FlightDuration, TargetX/Y/Z, ReleaseAltitude, HasArrived |
|
||||
| **PlatformEntity** | Id, PosX/Y/Z, CurrentVelocity, State, MunitionCount, Cooldown, MuzzleVelocity, CruiseSpeed, ReleaseAltitude, TargetX/Y/Z, FlightDistance |
|
||||
| **DetectionEntity** | Id, PosX/Y/Z, Source, IsInRange(), IsDetected() |
|
||||
|
||||
---
|
||||
|
||||
## 五、默认数据
|
||||
@ -201,4 +227,4 @@ dotnet publish -c Release -o ../../unity_plugins
|
||||
| 实体事件映射 | `docs/design/technical/仿真器实体与事件映射.md` |
|
||||
| 任务跟踪 | `docs/implementation/tasks/实施计划与任务跟踪.md` |
|
||||
| 测试报告 | `test/reports/`(每次集成测试自动生成) |
|
||||
| 测试状态 | **191 测试,24 秒(集成)** |
|
||||
| 测试状态 | **243 测试,9 秒(全部通过)** |
|
||||
|
||||
@ -123,15 +123,19 @@ namespace CounterDrone.Core
|
||||
private static void Seed3DronesAirBased(IScenarioService s, DefaultData d)
|
||||
{
|
||||
var t = s.CreateTask("[Demo] 3架空基编队拦截", "");
|
||||
s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene());
|
||||
var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene();
|
||||
scene.WindSpeed = 5;
|
||||
scene.WindDirection = (int)WindDirection.E;
|
||||
s.SaveScene(t.Id, scene);
|
||||
var target = d.Targets.First(p => p.Id == "shahed").ToTargetConfig();
|
||||
target.Quantity = 3;
|
||||
target.TypicalSpeed = 150;
|
||||
s.SaveTarget(t.Id, target);
|
||||
s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "line-3").ToRoutePlan(), R("20km-h500", 200, d));
|
||||
s.SaveDeployment(t.Id, new List<EquipmentDeployment>
|
||||
{
|
||||
d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 3, 12000, 1000, 0),
|
||||
d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 6000, 1000, 0),
|
||||
d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 6300, 1000, 0),
|
||||
d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 6600, 1000, 0),
|
||||
});
|
||||
s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 });
|
||||
s.UpdateStep(t.Id, 5);
|
||||
|
||||
@ -12,6 +12,14 @@ namespace CounterDrone.Core.Simulation
|
||||
public float CreatedAt { get; }
|
||||
public bool IsDissipated => Dispersion.IsDissipated;
|
||||
|
||||
public float PosX => Dispersion.Center.X;
|
||||
public float PosY => Dispersion.Center.Y;
|
||||
public float PosZ => Dispersion.Center.Z;
|
||||
public float Radius => Dispersion.Radius;
|
||||
public float Density => Dispersion.CoreDensity;
|
||||
public int Phase => Dispersion.Phase;
|
||||
public float Elapsed => Dispersion.Elapsed;
|
||||
|
||||
public CloudEntity(string id, AerosolType aerosolType, ICloudDispersionModel dispersion, float createdAt)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
@ -12,6 +12,9 @@ namespace CounterDrone.Core.Simulation
|
||||
{
|
||||
public string Id { get; }
|
||||
public DetectionSource Source { get; }
|
||||
public float PosX => Source.Position.X;
|
||||
public float PosY => Source.Position.Y;
|
||||
public float PosZ => Source.Position.Z;
|
||||
|
||||
/// <summary>每无人机 Id → 探测状态(true=Detected, false=Undetected)</summary>
|
||||
private readonly Dictionary<string, bool> _droneStates = new();
|
||||
|
||||
@ -24,6 +24,10 @@ namespace CounterDrone.Core.Simulation
|
||||
public float PrevX { get; private set; }
|
||||
public float PrevY { get; private set; }
|
||||
public float PrevZ { get; private set; }
|
||||
|
||||
public float TraveledArc => _traveledArc;
|
||||
public float TotalArc => _totalArc;
|
||||
public float Progress => _totalArc > 0 ? _traveledArc / _totalArc : 0f;
|
||||
/// <summary>沿航路已飞行弧长(米),由 RouteGeometry 驱动</summary>
|
||||
private float _traveledArc;
|
||||
/// <summary>航路总弧长(米),构造时一次性算好(航路静态,无需每帧重算)</summary>
|
||||
|
||||
@ -24,6 +24,26 @@ namespace CounterDrone.Core.Simulation
|
||||
public float Azimuth { get; }
|
||||
public float MuzzleVelocity { get; }
|
||||
public float FlightDuration { get; private set; }
|
||||
public float StartX => _startX;
|
||||
public float StartY => _startY;
|
||||
public float StartZ => _startZ;
|
||||
public float LaunchTime => _launchTime;
|
||||
public float ElapsedTime => _time;
|
||||
|
||||
public (float Vx, float Vy, float Vz) Velocity
|
||||
{
|
||||
get
|
||||
{
|
||||
float cosA = (float)System.Math.Cos(LaunchAngle);
|
||||
float sinA = (float)System.Math.Sin(LaunchAngle);
|
||||
float sinAz = (float)System.Math.Sin(Azimuth);
|
||||
float cosAz = (float)System.Math.Cos(Azimuth);
|
||||
float vx = MuzzleVelocity * cosA * sinAz;
|
||||
float vy = MuzzleVelocity * sinA - 9.81f * _time;
|
||||
float vz = MuzzleVelocity * cosA * cosAz;
|
||||
return (vx, vy, vz);
|
||||
}
|
||||
}
|
||||
private readonly float _startX, _startY, _startZ;
|
||||
private readonly float _launchTime;
|
||||
|
||||
|
||||
@ -34,6 +34,12 @@ namespace CounterDrone.Core.Simulation
|
||||
public float ExactReleaseTime => _exactReleaseTime;
|
||||
private float _exactReleaseTime;
|
||||
|
||||
public float TargetX => _targetX;
|
||||
public float TargetY => _targetY;
|
||||
public float TargetZ => _targetZ;
|
||||
public float FlightDistance => _flightDistance;
|
||||
public float FlownDistance => _flownDistance;
|
||||
|
||||
// 地基平台
|
||||
public float MuzzleVelocity { get; }
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ namespace CounterDrone.Core.Simulation
|
||||
public int CloudPhase;
|
||||
public float CloudElapsed;
|
||||
|
||||
public float VelX, VelY, VelZ;
|
||||
|
||||
/// <summary>仅 Flush 入库时使用,仿真期间保持 null</summary>
|
||||
public string? StateData;
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ namespace CounterDrone.Core.Simulation
|
||||
|
||||
public IReadOnlyList<DroneEntity> Drones => _drones;
|
||||
public IReadOnlyList<CloudEntity> Clouds => _clouds;
|
||||
public IReadOnlyList<PlatformEntity> Platforms => _platforms;
|
||||
public IReadOnlyList<MunitionEntity> Munitions => _munitions;
|
||||
public IReadOnlyList<DetectionEntity> DetectionEntities => _detectionEntities;
|
||||
public IReadOnlyList<SimEvent> Events => _allEvents;
|
||||
@ -503,18 +504,30 @@ namespace CounterDrone.Core.Simulation
|
||||
for (int i = 0; i < _drones.Count; i++)
|
||||
{
|
||||
var d = _drones[i];
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, DamageStage = (int)_damageModel.GetDamageStage(1 - d.Hp), Hp = d.Hp });
|
||||
float vx = d.PosX - d.PrevX, vy = d.PosY - d.PrevY, vz = d.PosZ - d.PrevZ;
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, VelX = vx, VelY = vy, VelZ = vz, DamageStage = (int)_damageModel.GetDamageStage(1 - d.Hp), Hp = d.Hp });
|
||||
}
|
||||
for (int i = 0; i < _munitions.Count; i++)
|
||||
{
|
||||
var m = _munitions[i];
|
||||
var (vx, vy, vz) = m.Velocity;
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = m.Id, EntityType = Models.EntityType.Munition, PosX = m.PosX, PosY = m.PosY, PosZ = m.PosZ, VelX = vx, VelY = vy, VelZ = vz });
|
||||
}
|
||||
for (int i = 0; i < _platforms.Count; i++)
|
||||
{
|
||||
var p = _platforms[i];
|
||||
if (p.PlatformType != Models.PlatformType.AirBased) continue;
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = p.Id, EntityType = Models.EntityType.Platform, PosX = p.PosX, PosY = p.PosY, PosZ = p.PosZ, PlatformStateStr = p.State.ToString() });
|
||||
var (vx, vy, vz) = p.CurrentVelocity;
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = p.Id, EntityType = Models.EntityType.Platform, PosX = p.PosX, PosY = p.PosY, PosZ = p.PosZ, VelX = vx, VelY = vy, VelZ = vz, PlatformStateStr = p.State.ToString() });
|
||||
}
|
||||
for (int i = 0; i < _detectionEntities.Count; i++)
|
||||
{
|
||||
var d = _detectionEntities[i];
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.DetectionEquip, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ });
|
||||
}
|
||||
for (int i = 0; i < _clouds.Count; i++)
|
||||
{
|
||||
var c = _clouds[i];
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.Dispersion.Center.X, PosY = c.Dispersion.Center.Y, PosZ = c.Dispersion.Center.Z, CloudRadius = c.Dispersion.Radius, CloudOpacity = c.Dispersion.Particles.Opacity, CloudPhase = c.Dispersion.Phase, CloudElapsed = c.Dispersion.Elapsed });
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.PosX, PosY = c.PosY, PosZ = c.PosZ, CloudRadius = c.Radius, CloudOpacity = c.Dispersion.Particles.Opacity, CloudPhase = c.Phase, CloudElapsed = c.Elapsed });
|
||||
}
|
||||
return _snapshotPool;
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -92,12 +92,11 @@ namespace CounterDrone.Unity
|
||||
{
|
||||
if (!_clouds.ContainsKey(cloud.Id))
|
||||
{
|
||||
var pos = new UVector3(cloud.Dispersion.Center.X, cloud.Dispersion.Center.Y, cloud.Dispersion.Center.Z);
|
||||
var pos = new UVector3(cloud.PosX, cloud.PosY, cloud.PosZ);
|
||||
var prefab = _cloudPrefab != null ? _cloudPrefab : GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
var go = Instantiate(prefab, pos, Quaternion.identity);
|
||||
go.name = cloud.Id;
|
||||
float r = cloud.Dispersion.Radius;
|
||||
go.transform.localScale = UVector3.one * r;
|
||||
go.transform.localScale = UVector3.one * cloud.Radius;
|
||||
var mat = go.GetComponent<Renderer>().material;
|
||||
mat.color = new Color(1, 0, 0, 0.3f);
|
||||
_clouds[cloud.Id] = go;
|
||||
|
||||
@ -162,7 +162,7 @@ namespace CounterDrone.Core.Tests
|
||||
// 场景 6:3 架空基编队
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
[Fact(Skip = "空基多目标时序需单独调参")]
|
||||
[Fact]
|
||||
public void Scenario_3DronesAirBased_AllDestroyed()
|
||||
{
|
||||
var eng = RunPreset("3架空基编队");
|
||||
@ -180,7 +180,7 @@ namespace CounterDrone.Core.Tests
|
||||
// 验证:有探测设备时,planner 基于探测边界算到达时间,发射时机比无探测时晚
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
[Fact(Skip = "慢速集成测试:两次20km仿真(~7200帧×2)。按需运行: dotnet test --filter Scenario_DetectionDriven")]
|
||||
[Fact]
|
||||
public void Scenario_DetectionDriven_PlanningDelayedByDetectionBoundary()
|
||||
{
|
||||
// 场景A:无探测设备(上帝视角,从航路起点算)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user