CounterDroneBackend/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
tian e782062377 feat: Unity 端接入 DataService — 前端可直接 CRUD 基础数据
SimulationRunner 新增 DataService 属性
SimulationBootstrap 自动创建 DataService 并注入 Runner
前端调用: runner.DataService.GetAllDrones() 等
2026-06-17 15:50:22 +08:00

135 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 前端参考模板 — 展示两种运行方式:
/// 1. 预设模式:直接运行数据库中的 [Demo] 想定(最简单)
/// 2. 自定义模式:用 DefaultData 预设快速组装新想定 → Planner → 仿真
/// </summary>
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<AmmunitionSpec>().ToList();
var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
var threats = new List<DroneWave>
{
new DroneWave { WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"] }
};
var fireUnits = new List<FireUnit>();
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<DetectionSource>());
scenario.SaveCloudDispersal(taskId, new CloudDispersal
{
AerosolType = (int)AerosolType.InertGas,
DisperseHeight = detail.Targets[0].TypicalAltitude,
Source = "Algorithm",
});
scenario.SaveDeployment(taskId, new List<EquipmentDeployment>
{
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<SimulationRunner>();
if (Runner == null) Runner = gameObject.AddComponent<SimulationRunner>();
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<Camera>(); 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));
}
}
}