CounterDroneBackend/docs/对接文档_Unity前端.md
tian d8470bad30 Phase 10: 探测实时链路开发 + planner 诊断 + 硬编码消除
- 3D球冠探测: IsInCoverage, DetectionEntity, Tick 第5步扫描
- 探测融合: EarliestDetection 采样法, break 修复
- planner 诊断: HasInterceptWindow 拦截窗口检查
- TryGenerateFireEvents 返回拒绝原因
- Summary 含失败原因+建议值(探测范围/弧长)
- PlanningFailed 事件: 引擎在规划失败时发出事件
- 硬编码消除: Phase2Duration→AmmunitionSpec
  ExpansionFactor/TimingSafetyMargin→PlannerConfig
  速度从 waypoint.Speed 读取(不用 TypicalSpeed)
- TestData 改为从 seeded 数据库读取(不再内嵌 JSON)
- 默认数据加 3D球冠参数,巡航导弹速度300→200
  空基航路10km→20km, 位置调整
- 222 测试全通过
2026-06-16 14:10:23 +08:00

7.1 KiB
Raw Blame History

后端对接文档Unity 前端)

版本V1.3
日期2026-06-15
Unity 版本2022.3.62f3c1


一、交付物

从仓库取以下内容,拖入 Unity 项目:

src/Unity/Assets/Plugins/              ← DLLCore + sqlite + JSON
src/Unity/Assets/Scripts/Managers/     ← 桥接脚本

将以下文件放到 Unity 项目 Assets/StreamingAssets/ 目录下:
  data/defaults.json                    ← 默认数据(首次运行自动复制到 persistentDataPath
  data/planner_config.json              ← 规划器配置(同上)

二、快速开始

  1. defaults.jsonplanner_config.json 放到 Assets/StreamingAssets/
  2. 创建空 GameObjectSimulationBootstrap
  3. 运行 — 自动种子数据库,选一个 [Demo] 想定,启动仿真

SimulationBootstrap 是前端开发的参考模板,展示预设模式(一行代码)和自定义模式(用 DefaultData 预设组装)两种路径。

如需验证全部模块,挂 ManagerVerification → Inspector 右键 Run Full Verification


三、核心流程(使用预设想定)

3.1 列出预设想定

var scenario = GetComponent<ScenarioManager>();
var demos = scenario.Search("Demo", null, null, 1, 100);
// → 返回 6 个预设想定:
//   [Demo] 无防御-无人机抵达目标
//   [Demo] 管控区域侵入
//   [Demo] 活塞拦截-西风5ms
//   [Demo] 喷气式拦截-活性材料
//   [Demo] 空基拦截-东风5ms
//   [Demo] 3架空基编队拦截

3.2 启动仿真(一行代码)

var runner = GetComponent<SimulationRunner>();

// 选一个预设想定,直接运行(引擎自动调用 Planner 生成发射计划)
runner.LoadAndStart(demos.Items[0].Id);  // 例如:活塞拦截-西风5ms
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.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);
runner.Engine.OnZoneIntruded     += (d, z) => AlertIntrusion(z.Name);
runner.Engine.OnSimulationEnded  += () => GenerateReport();

3.3 自定义想定(高级)

var scenario = GetComponent<ScenarioManager>();
var task = scenario.CreateTask("自定义想定", "");

// 使用默认数据预设快速配置
var defaults = DefaultData.Load(new UnityPathProvider());
scenario.SaveScene(task.Id, defaults.Weather.First(w => w.Id == "sunny-calm").ToCombatScene());
scenario.SaveTarget(task.Id, defaults.Targets.First(p => p.Id == "shahed").ToTargetConfig());
scenario.SaveRoute(task.Id, "default",
    defaults.Formations.First(f => f.Id == "single").ToRoutePlan(),
    defaults.Routes.First(r => r.Id == "5km-h500").ToWaypoints(200));
scenario.SaveDeployment(task.Id, new List<EquipmentDeployment>
{
    defaults.FireUnits.First(f => f.Id == "ground-light")
        .ToEquipmentDeployment(AerosolType.InertGas, 1, 1500, 0, 50),
});
scenario.SaveCloudDispersal(task.Id, new CloudDispersal
    { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 });
scenario.UpdateStep(task.Id, 5);

// 运行
var runner = GetComponent<SimulationRunner>();
runner.LoadAndStart(task.Id);

3.3 仿真事件一览

事件 参数 触发时机
OnTargetDetected DroneEntity, DetectionEntity 无人机首次进入探测设备 3D 球冠范围(离开后再次进入会重新触发)
OnMunitionLaunched MunitionEntity 发射计划时间到达
OnCloudGenerated CloudEntity 弹药到达释放高度
OnDroneDestroyed DroneEntity HP ≤ 0
OnDroneReachedTarget DroneEntity 到达最后航路点
OnZoneIntruded DroneEntity, ControlZoneEntity 进入管控区
OnSimulationEnded 所有无人机状态 ≠ Flying

四、Manager API

ScenarioManager

var mgr = GetComponent<ScenarioManager>();
SimTask task = mgr.CreateTask("任务名", "");           // 空字符串 = 自动编号 SIM-yyyyMMdd-xxx
mgr.SaveScene(id, combatScene);
mgr.SaveTarget(id, targetConfig);
mgr.SaveDeployment(id, equipmentList);
mgr.SaveCloud(id, cloudDispersal);
mgr.SaveRoute(id, "default", routePlan, waypoints);
TaskFullConfig detail = mgr.GetDetail(id);
PagedResult<SimTask> result = mgr.Search("关键词", from, to, page, pageSize);

SimulationRunner

var runner = GetComponent<SimulationRunner>();
runner.Engine.SetFireSchedule(fireEvents);  // 从推荐方案取
runner.LoadAndStart(taskId);
runner.Engine.TimeScale = 4f;
runner.Engine.State / Drones / Clouds / Munitions / DetectionEntities / Events
runner.Stop();

ReportManager

var mgr = GetComponent<ReportManager>();
var config = scenario.GetDetail(taskId);
var report = mgr.Generate(taskId, config, runner.Engine.Events.ToList(), 
                           runner.Engine.Drones[0].Status.ToString(), 
                           runner.Engine.SimulationTime);
string path = mgr.Export(report.Id);  // → persistentDataPath/reports/xxx.md

ReplayController

var replay = GetComponent<ReplayController>();
replay.LoadReplay(taskId);
// replay.TotalFrames / replay.GetFrame(frameIndex)

五、默认数据

首次运行 DatabaseManager.OpenMainDb() 自动从 data/defaults.json 种子数据库:

类别 内容 示例 ID
弹药 3 种 inert / active / fuel
编队 6 种 single / line-3 / swarm-10
航线 6 条 3km-h300 / 5km-h500 / 10km-h500 / 20km-h500
火力单元 4 种 ground-light / ground-standard / ground-heavy / air-standard
无人机 6 种 quadcopter / shahed / cruise-missile
探测设备 4 种 radar-mr / eo-station
天气 6 种 sunny-calm / fog / night
预设想定 6 个 [Demo] 活塞拦截-西风5ms

所有默认数据名称含 [Demo] 前缀UI 中可识别。预设可通过 API 直接获取:

var defaults = DefaultData.Load(new UnityPathProvider());
var ammo = defaults.Ammunition.First(a => a.Id == "inert");
var route = defaults.Routes.First(r => r.Id == "5km-h500").ToWaypoints(200);
var unit = defaults.FireUnits.First(f => f.Id == "ground-light").ToEquipmentDeployment(AerosolType.InertGas, 1, 1500, 0, 50);

六、更新后端

cd src/CounterDrone.Core
dotnet publish -c Release -o ../../unity_plugins
# 覆盖 Assets/Plugins/CounterDrone.Core/ 下所有 DLL

七、参考

文档 位置
架构设计 docs/design/architecture/总体架构设计.md
实体事件映射 docs/design/technical/仿真器实体与事件映射.md
任务跟踪 docs/implementation/tasks/实施计划与任务跟踪.md
测试报告 test/reports/(每次集成测试自动生成)
测试状态 191 测试24 秒(集成)