using System.Collections.Generic; using System.Linq; using CounterDrone.Core; using CounterDrone.Core.Algorithms; using CounterDrone.Core.Models; using CounterDrone.Core.Repository; using CounterDrone.Core.Services; using UnityEngine; using UVector3 = UnityEngine.Vector3; using AVector3 = CounterDrone.Core.Algorithms.Vector3; namespace CounterDrone.Unity { /// /// 前端参考模板 — 展示两种运行方式: /// 1. 预设模式:直接运行数据库中的 [Demo] 想定(最简单) /// 2. 自定义模式:用 DefaultData 预设快速组装新想定 → Planner → 仿真 /// public class SimulationBootstrap : MonoBehaviour { [Header("Mode")] [SerializeField] private bool _usePreset = true; // true=预设想定, false=自定义 [Header("Custom Settings")] [SerializeField] private float _droneSpeed = 200f; [SerializeField] private float _routeLength = 5000f; [SerializeField] private float _timeScale = 4f; public SimulationRunner Runner { get; private set; } void Start() { var paths = new UnityPathProvider(); var dbm = new DatabaseManager(paths); var db = dbm.OpenMainDb(); // 自动种子弹药 + 6个[Demo]想定 + 基础数据 SqliteConnectionTracker.Track(db); var dataService = new DataService( new AmmunitionSpecRepository(db), new FireUnitSpecRepository(db), new DroneSpecRepository(db), new SensorSpecRepository(db), new EnvironmentSpecRepository(db), new FormationTemplateRepository(db), new RouteTemplateRepository(db)); 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)); string taskId; if (_usePreset) { // 方式1:直接使用预设想定(最简单) var demos = scenario.SearchTasks("Demo", null, null, 1, 1); if (demos.TotalCount == 0) { 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 task = scenario.CreateTask("Unity Demo", ""); taskId = task.Id; scenario.SaveScene(taskId, defaults.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); scenario.SaveTarget(taskId, defaults.Drones.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 { 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, 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(); // 运行仿真 Runner = GetComponent(); if (Runner == null) Runner = gameObject.AddComponent(); else Runner.enabled = true; Runner.DataService = dataService; Runner.LoadAndStart(taskId); Runner.Engine.TimeScale = _timeScale; // 定位相机 var cam = Camera.main; 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)); } } }