diff --git a/docs/对接文档_Unity前端.md b/docs/对接文档_Unity前端.md
index 8288382..63578ff 100644
--- a/docs/对接文档_Unity前端.md
+++ b/docs/对接文档_Unity前端.md
@@ -21,14 +21,15 @@ src/Unity/Assets/Scripts/Managers/ ← 桥接脚本
---
-## 二、快速验证
+## 二、快速开始
-1. 将上述文件拖入 Unity 项目,等编译
-2. 创建空 GameObject,挂 `ManagerVerification`
-3. Inspector 右键 → `Run Full Verification`
-4. Console 看到 7 行 OK
+1. 将 `defaults.json` 和 `planner_config.json` 放到 `Assets/StreamingAssets/`
+2. 创建空 GameObject,挂 `SimulationBootstrap`
+3. 运行 — 自动种子数据库,选一个 `[Demo]` 想定,启动仿真
-> 首次运行自动种子 6 个 `[Demo]` 预设想定到数据库,含完整配置。
+> `SimulationBootstrap` 是前端开发的参考模板,展示预设模式(一行代码)和自定义模式(用 DefaultData 预设组装)两种路径。
+
+> 如需验证全部模块,挂 `ManagerVerification` → Inspector 右键 `Run Full Verification`。
---
diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll
index 3a16729..0d3bf12 100644
Binary files a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll and b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll differ
diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb
index 0959f3e..88c1caf 100644
Binary files a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb and b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb differ
diff --git a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
index 5bd66ef..5456498 100644
--- a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
+++ b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
@@ -1,5 +1,5 @@
using System.Collections.Generic;
-using System.IO;
+using System.Linq;
using CounterDrone.Core;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
@@ -11,12 +11,19 @@ using AVector3 = CounterDrone.Core.Algorithms.Vector3;
namespace CounterDrone.Unity
{
- /// 一键启动:创建想定 → 推荐方案 → 运行仿真
+ ///
+ /// 前端参考模板 — 展示两种运行方式:
+ /// 1. 预设模式:直接运行数据库中的 [Demo] 想定(最简单)
+ /// 2. 自定义模式:用 DefaultData 预设快速组装新想定 → Planner → 仿真
+ ///
public class SimulationBootstrap : MonoBehaviour
{
- [Header("Simulation Settings")]
+ [Header("Mode")]
+ [SerializeField] private bool _usePreset = true; // true=预设想定, false=自定义
+
+ [Header("Custom Settings")]
[SerializeField] private float _droneSpeed = 200f;
- [SerializeField] private float _routeLength = 20000f;
+ [SerializeField] private float _routeLength = 5000f;
[SerializeField] private float _timeScale = 4f;
public SimulationRunner Runner { get; private set; }
@@ -24,117 +31,97 @@ namespace CounterDrone.Unity
void Start()
{
var paths = new UnityPathProvider();
-
- // 清理上次残留数据
- if (System.IO.File.Exists(paths.GetMainDbPath()))
- System.IO.File.Delete(paths.GetMainDbPath());
- if (System.IO.Directory.Exists(paths.GetFramesDir()))
- System.IO.Directory.Delete(paths.GetFramesDir(), true);
-
var dbm = new DatabaseManager(paths);
- var db = dbm.OpenMainDb();
+ var db = dbm.OpenMainDb(); // 自动种子弹药 + 6个[Demo]想定
SqliteConnectionTracker.Track(db);
- foreach (var a in DefaultData.Load(paths).Ammunition) db.Insert(a);
- // 创建想定
var scenario = new ScenarioService(
new SimTaskRepository(db), new CombatSceneRepository(db),
new ControlZoneRepository(db), new TargetConfigRepository(db),
new EquipmentDeploymentRepository(db), new CloudDispersalRepository(db),
new RoutePlanRepository(db), new WaypointRepository(db));
- var task = scenario.CreateTask("Unity Demo", "");
- var taskId = task.Id;
+ string taskId;
- scenario.SaveScene(taskId, new CombatScene
+ if (_usePreset)
{
- SceneType = (int)SceneType.Plain, WindSpeed = 3,
- WindDirection = (int)WindDirection.W, Temperature = 22,
- });
- scenario.SaveTarget(taskId, new TargetConfig
- {
- WaveId = "default",
- TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston,
- Quantity = 1, TypicalSpeed = _droneSpeed, TypicalAltitude = 500,
- });
- scenario.SaveRoute(taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
- new List
+ // 方式1:直接使用预设想定(最简单)
+ var demos = scenario.SearchTasks("[Demo]", null, null, 1, 1);
+ if (demos.TotalCount == 0)
{
- new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = _droneSpeed },
- new Waypoint { PosX = _routeLength, PosY = 500, PosZ = 0, Speed = _droneSpeed },
- });
+ Debug.LogError("无预设想定,请确认 defaults.json 已放入 StreamingAssets");
+ return;
+ }
+ taskId = demos.Items[0].Id;
+ Debug.Log($"使用预设想定: {demos.Items[0].Name}");
+ }
+ else
+ {
+ // 方式2:用 DefaultData 预设组装自定义想定
+ var defaults = DefaultData.Load(paths);
- // 推荐方案
- var detail = scenario.GetTaskDetail(taskId);
- var ammoCatalog = db.Table().ToList();
- var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
- var droneGroup = new DroneWave
- {
- WaveId = "default",
- Target = detail.Targets[0],
- Route = detail.Routes[0],
- Waypoints = detail.WaypointGroups["default"],
- };
- var fireUnits = new List();
- for (int i = 0; i < 8; i++)
- fireUnits.Add(new FireUnit
- {
- Id = $"u{i}",
- Type = PlatformType.GroundBased,
- Position = new AVector3(5000 + i * 50, 0, 50),
- MuzzleVelocity = 800,
- TotalMunitions = 3,
- AmmoTypes = new() { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel },
- });
- var result = planner.Plan(fireUnits, new List { droneGroup }, detail.Scene, new List());
- scenario.SaveCloudDispersal(taskId, new CloudDispersal
- {
- PositionX = (droneGroup.Waypoints[0].PosX + droneGroup.Waypoints[^1].PosX) / 2,
- PositionY = (droneGroup.Waypoints[0].PosY + droneGroup.Waypoints[^1].PosY) / 2,
- PositionZ = (droneGroup.Waypoints[0].PosZ + droneGroup.Waypoints[^1].PosZ) / 2,
- DisperseHeight = droneGroup.Target.TypicalAltitude,
- Source = "Algorithm",
- });
+ var task = scenario.CreateTask("Unity Demo", "");
+ taskId = task.Id;
- var equips = new List();
- foreach (var u in fireUnits)
- equips.Add(new EquipmentDeployment
+ scenario.SaveScene(taskId, defaults.Weather.First(w => w.Id == "sunny-calm").ToCombatScene());
+ scenario.SaveTarget(taskId, defaults.Targets.First(p => p.Id == "shahed").ToTargetConfig());
+ scenario.SaveRoute(taskId, "default",
+ defaults.Formations.First(f => f.Id == "single").ToRoutePlan(),
+ defaults.Routes.First(r => _routeLength > 6000 ? r.Id == "10km-h500" : r.Id == "5km-h500")
+ .ToWaypoints(_droneSpeed));
+
+ // 从火力单元预设构建 FireUnit → Planner 自动生成方案 → 引擎执行
+ var detail = scenario.GetTaskDetail(taskId);
+ var ammoCatalog = db.Table().ToList();
+ var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
+ var threats = new List
{
- EquipmentRole = (int)EquipmentRole.LaunchPlatform,
- PlatformType = (int)u.Type, Quantity = 1,
- PositionX = u.Position.X, PositionY = u.Position.Y, PositionZ = u.Position.Z,
- AerosolType = (int)u.AmmoTypes[0],
- MunitionCount = u.TotalMunitions, Cooldown = u.Cooldown, MuzzleVelocity = u.MuzzleVelocity,
- CruiseSpeed = u.CruiseSpeed > 0 ? u.CruiseSpeed : null,
- ReleaseAltitude = u.ReleaseAltitude > 0 ? u.ReleaseAltitude : null,
+ new DroneWave { WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"] }
+ };
+ var fireUnits = new List();
+ for (int i = 0; i < 4; i++)
+ fireUnits.Add(new FireUnit
+ {
+ Id = $"fu{i}",
+ Type = PlatformType.GroundBased,
+ Position = new AVector3(_routeLength / 2f + i * 50, 0, 50),
+ MuzzleVelocity = 800, TotalMunitions = 3,
+ AmmoTypes = new() { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel },
+ });
+ var plan = planner.Plan(fireUnits, threats, detail.Scene, new List());
+
+ scenario.SaveCloudDispersal(taskId, new CloudDispersal
+ {
+ AerosolType = (int)AerosolType.InertGas,
+ DisperseHeight = detail.Targets[0].TypicalAltitude,
+ Source = "Algorithm",
});
- scenario.SaveDeployment(taskId, equips);
+ scenario.SaveDeployment(taskId, new List
+ {
+ defaults.FireUnits.First(f => f.Id == "ground-light")
+ .ToEquipmentDeployment(AerosolType.InertGas, 1, _routeLength / 2f, 0, 50),
+ });
+ scenario.UpdateStep(taskId, 5);
+
+ Debug.Log($"自定义想定: {plan.Best.ThreatsEngaged} 威胁已分配, {plan.Best.MergedSchedule.Count} 发, 概率 {plan.Best.OverallProbability:P0}");
+ }
SqliteConnectionTracker.Untrack(db);
db.Dispose();
- var plan = result.Best;
- Debug.Log($"规划方案: {plan.ThreatsEngaged} 威胁被分配, {plan.MergedSchedule.Count} 发, 概率 {plan.OverallProbability:P0}");
-
// 运行仿真
Runner = GetComponent();
if (Runner == null) Runner = gameObject.AddComponent();
else Runner.enabled = true;
Runner.LoadAndStart(taskId);
+ Runner.Engine.TimeScale = _timeScale;
- // 设置倍速
- if (Runner.Engine != null)
- {
- Runner.Engine.TimeScale = _timeScale;
- Debug.Log($"Engine state: {Runner.Engine.State}, drones: {Runner.Engine.Drones.Count}");
- }
-
- // 移动 Game 视图相机
+ // 定位相机
var cam = Camera.main;
- if (cam == null) { var cgo = new GameObject("MainCamera"); cgo.AddComponent(); cam = cgo.GetComponent(); cam.tag = "MainCamera"; }
- cam.transform.position = new UVector3(_routeLength / 2f, 2000, -3000);
- cam.transform.LookAt(new UVector3(_routeLength / 2f, 500, 0));
- Debug.Log("Camera positioned. In Scene view, double-click 'Simulation' in Hierarchy to focus.");
+ if (cam == null) { cam = new GameObject("MainCamera").AddComponent(); cam.tag = "MainCamera"; }
+ float midX = _usePreset ? 2500f : _routeLength / 2f;
+ cam.transform.position = new UVector3(midX, 2000, -3000);
+ cam.transform.LookAt(new UVector3(midX, 500, 0));
}
}
}