# 后端对接文档（Unity 前端）

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

---

## 一、交付物

从仓库取以下内容，拖入 Unity 项目：

```
src/Unity/Assets/Plugins/              ← DLL（Core + sqlite + JSON）
src/Unity/Assets/Scripts/Managers/     ← 桥接脚本

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

---

## 二、快速开始

1. 将 `defaults.json` 和 `planner_config.json` 放到 `Assets/StreamingAssets/`
2. 创建空 GameObject，挂 `SimulationBootstrap`
3. 运行 — 自动种子数据库，选一个 `[Demo]` 想定，启动仿真

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

> 如需验证全部模块，挂 `ManagerVerification` → Inspector 右键 `Run Full Verification`。

---

## 三、核心流程（使用预设想定）

### 3.1 列出预设想定

```csharp
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 启动仿真（一行代码）

```csharp
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 自定义想定（高级）

```csharp
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

```csharp
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

```csharp
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

```csharp
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

```csharp
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 直接获取：

```csharp
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);
```

---

## 六、更新后端

```bash
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 秒（集成）** |
