From 5f589c964488907d447148aaefb6965742356589 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 15 Jun 2026 16:27:45 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20SimulationBootstrap=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E4=B8=BA=E5=89=8D=E7=AB=AF=E5=8F=82=E8=80=83=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两种模式: 预设(选[Demo]想定) / 自定义(DefaultData预设组装) 移除手动插弹药(已自动种子), 使用 DefaultData 天气/目标/航线/火力单元预设 UnityPathProvider 从 StreamingAssets 自动复制数据文件 前端文档: 快速开始改为挂 SimulationBootstrap 即运行 --- docs/对接文档_Unity前端.md | 13 +- .../CounterDrone.Core/CounterDrone.Core.dll | Bin 145920 -> 145920 bytes .../CounterDrone.Core/CounterDrone.Core.pdb | Bin 59244 -> 59248 bytes .../Scripts/Managers/SimulationBootstrap.cs | 163 ++++++++---------- 4 files changed, 82 insertions(+), 94 deletions(-) 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 3a1672918dc028852ea270e2ae1dc90c0dfe19a4..0d3bf12dfd9c3495f0e9a308ff2d9dca9a0af413 100644 GIT binary patch delta 250 zcmZqp!qM=BV?qbZKfknPjXkYBj9Yt{zHkYI@^gB3x4v*(dXs6P%G2PB+b3`{r7CL} zTBMmLnOm9|7@DS-T9~Anm>F7{7$=!pSQwa@rX(dBS{fx=ni`vKm(^$b&B*eT&vNf{ z8AGN3f&Sdt8^UjJI4$aWcW{C8^N#5?hD_B8P|;71prRo4OL*(}U2}}R&c&GYZ5KCU zl4SKaWUydJV=!k(0%A)B69xkYLk3fZ6d<+$^3s6V3@8GUF$SsuiCO^Fn1R(KF(iZK TjDWNyP{f$QbbGxyQzH`qQQ}LP delta 250 zcmZqp!qM=BV?qZ@L)F^_jXkYBj9Yt{zHkY!EM3bsIeMzwzT1Jt!4KO*w@=_^N>$b{ zO)^Ucf}}(t5w8RuMbIa|r`b@tWS$tPM{x@C5 zkSRbw@k$A|`25EnyUTY>IihO0sx`>P4@r* diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.pdb index 0959f3ed694c68e904874f1067309cd335e67014..88c1caf48733d2742cf4ccfcad32496799fe4659 100644 GIT binary patch delta 5785 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 delta 5781 zcmY+|2Ut|c+Q9L5*@eZ1$Wo>40ye~g6e$Y`YXch!QUxiRNI*fbL=#<2u|$)oB<5mb zB5E$^jRiMo60t-v*2I=5#t%!lu||nrOT0Il@4rWK3(xbL_nnzDvvbawJqP&pJM}N$ zsaKTgKd%t+PbeuZ5D~-oGdj3^(lUJa>@VJH?9-evYE#Rzx)slC%$8s$nLWTsKEMtf zfi}cRf5N;w4PS%P?aR-o|_Q5bLoC+p!CKaR?`H7C(lWb#j&D z23qk5PoW9dNe4K=1A6qpSY#p}MVO8`Scs)qi7HfM6YB6KzQ$3U!zEn9T|6>VJcHdp z&K$1rL3i{;IAW2E4CG-7%J4E4U

>FIb0KY{wVak0UsRCS1V{w2RP57nnUL4CsYm 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 I-7-Y;|7_KVYXATM 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)); } } }