From a3ec9df0018f46214385782d170702968e121db9 Mon Sep 17 00:00:00 2001 From: Tian jianyong <11429339@qq.com> Date: Wed, 20 Nov 2024 12:40:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=20Unity=20=E9=80=82?= =?UTF-8?q?=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ActiveProtect.csproj | 3 + src/Adapters/UnityExtensions.cs | 32 +++++ src/Adapters/UnityMissile.cs | 31 +++++ src/Adapters/UnitySimulationElement.cs | 42 +++++++ src/Adapters/UnitySimulationManager.cs | 166 +++++++++++++++++++++++++ src/Simulation/ISimulationManager.cs | 56 +++++++++ src/Simulation/SimulationManager.cs | 51 -------- 7 files changed, 330 insertions(+), 51 deletions(-) create mode 100644 src/Adapters/UnityExtensions.cs create mode 100644 src/Adapters/UnityMissile.cs create mode 100644 src/Adapters/UnitySimulationElement.cs create mode 100644 src/Adapters/UnitySimulationManager.cs create mode 100644 src/Simulation/ISimulationManager.cs diff --git a/ActiveProtect.csproj b/ActiveProtect.csproj index 08dcf2c..2e5a9d3 100644 --- a/ActiveProtect.csproj +++ b/ActiveProtect.csproj @@ -13,10 +13,13 @@ + + + diff --git a/src/Adapters/UnityExtensions.cs b/src/Adapters/UnityExtensions.cs new file mode 100644 index 0000000..1502b1f --- /dev/null +++ b/src/Adapters/UnityExtensions.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +namespace ActiveProtect.Adapters +{ + public static class UnityExtensions + { + public static Vector3D ToVector3D(this Vector3 v) + { + return new Vector3D(v.x, v.y, v.z); + } + + public static Vector3 ToVector3(this Vector3D v) + { + return new Vector3((float)v.X, (float)v.Y, (float)v.Z); + } + + public static Orientation ToOrientation(this Quaternion q) + { + Vector3 euler = q.eulerAngles; + return new Orientation(euler.x, euler.y, euler.z); + } + + public static Quaternion ToQuaternion(this Orientation o) + { + return Quaternion.Euler( + (float)o.Pitch, + (float)o.Yaw, + (float)o.Roll + ); + } + } +} \ No newline at end of file diff --git a/src/Adapters/UnityMissile.cs b/src/Adapters/UnityMissile.cs new file mode 100644 index 0000000..f114cfb --- /dev/null +++ b/src/Adapters/UnityMissile.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace ActiveProtect.Adapters +{ + /// + /// Unity导弹组件 + /// + public class UnityMissile : UnitySimulationElement + { + [SerializeField] + private string missileId; + [SerializeField] + private MissileProperties missileProperties; + + private Missile _missile; + + protected override SimulationElement CreateSimulationElement() + { + var unityManager = FindObjectOfType(); + _missile = new Missile(missileId, transform.position.ToVector3D(), + transform.rotation.ToOrientation(), unityManager, missileProperties); + return _missile; + } + + // Unity特有的功能,如粒子效果 + public void OnMissileLaunch() + { + GetComponent()?.Play(); + } + } +} \ No newline at end of file diff --git a/src/Adapters/UnitySimulationElement.cs b/src/Adapters/UnitySimulationElement.cs new file mode 100644 index 0000000..285ffcc --- /dev/null +++ b/src/Adapters/UnitySimulationElement.cs @@ -0,0 +1,42 @@ +using UnityEngine; + +namespace ActiveProtect.Adapters +{ + /// + /// Unity仿真元素基类,将我们的SimulationElement适配到MonoBehaviour + /// + public abstract class UnitySimulationElement : MonoBehaviour + { + protected SimulationElement SimElement { get; private set; } + + protected virtual void Awake() + { + // 创建对应的仿真元素 + SimElement = CreateSimulationElement(); + } + + protected virtual void Update() + { + // 使用Unity的时间步长 + SimElement?.Update(Time.deltaTime); + + // 同步位置和旋转 + if (SimElement != null) + { + transform.position = new Vector3( + (float)SimElement.Position.X, + (float)SimElement.Position.Y, + (float)SimElement.Position.Z + ); + + transform.rotation = Quaternion.Euler( + (float)SimElement.Orientation.Pitch, + (float)SimElement.Orientation.Yaw, + (float)SimElement.Orientation.Roll + ); + } + } + + protected abstract SimulationElement CreateSimulationElement(); + } +} \ No newline at end of file diff --git a/src/Adapters/UnitySimulationManager.cs b/src/Adapters/UnitySimulationManager.cs new file mode 100644 index 0000000..bf4759c --- /dev/null +++ b/src/Adapters/UnitySimulationManager.cs @@ -0,0 +1,166 @@ +using UnityEngine; +using UnityEngine.Events; +using System; +using System.Collections.Generic; +using ActiveProtect.Simulation; +using ActiveProtect.Models; +using ActiveProtect.Utility; + +namespace ActiveProtect.Adapters +{ + /// + /// Unity仿真管理器适配器,将Unity的事件系统适配到我们的接口 + /// + public class UnitySimulationManager : MonoBehaviour, ISimulationManager + { + // Unity事件字典,用于事件订阅和发布 + private readonly Dictionary> _eventDictionary + = new Dictionary>(); + + // 仿真元素列表 + private readonly List _elements = new List(); + + public double CurrentTime => Time.time; + + // 实现非泛型的 PublishEvent + public void PublishEvent(SimulationEvent evt) + { + var eventType = evt.GetType(); + if (_eventDictionary.TryGetValue(eventType, out var unityEvent)) + { + unityEvent.Invoke(evt); + } + } + + // 实现泛型的 PublishEvent + public void PublishEvent(T evt) where T : SimulationEvent + { + PublishEvent(evt as SimulationEvent); + } + + public void SubscribeToEvent(Action handler) where T : SimulationEvent + { + var eventType = typeof(T); + if (!_eventDictionary.ContainsKey(eventType)) + { + _eventDictionary[eventType] = new UnityEvent(); + } + + _eventDictionary[eventType].AddListener(evt => + { + if (evt is T typedEvent) + { + handler(typedEvent); + } + }); + } + + public void UnsubscribeFromEvent(Action handler) where T : SimulationEvent + { + var eventType = typeof(T); + if (_eventDictionary.ContainsKey(eventType)) + { + // Unity的事件系统不支持直接移除特定处理器,需要重新创建事件 + var newEvent = new UnityEvent(); + _eventDictionary[eventType] = newEvent; + } + } + + public void UnsubscribeAllEvents(SimulationElement element) + { + // 在Unity中,我们可能需要遍历所有事件类型并移除相关的监听器 + foreach (var eventPair in _eventDictionary) + { + eventPair.Value.RemoveAllListeners(); + } + } + + public SimulationElement GetEntityById(string id) + { + // 首先在我们的列表中查找 + var element = _elements.Find(e => e.Id == id); + if (element != null) return element; + + // 如果没找到,使用Unity的查找机制 + var go = GameObject.Find(id); + var unityElement = go?.GetComponent(); + return unityElement?.SimElement ?? throw new InvalidOperationException($"Entity with id {id} not found"); + } + + public void AddElement(SimulationElement element) + { + if (!_elements.Contains(element)) + { + _elements.Add(element); + } + } + + public List GetElements() + { + return new List(_elements); + } + + public void HandleTargetHit() + { + // 遍历所有活跃的导弹和坦克 + var missiles = _elements.FindAll(e => e is MissileBase && e.IsActive); + var tanks = _elements.FindAll(e => e is Tank && e.IsActive); + + foreach (var missile in missiles) + { + if (missile is MissileBase actualMissile) + { + foreach (var tank in tanks) + { + if (tank is Tank actualTank) + { + double distance = Vector3D.Distance(missile.Position, tank.Position); + if (distance <= actualMissile.MissileProperties.ExplosionRadius) + { + // 发布命中事件 + PublishEvent(new TargetHitEvent + { + TargetId = tank.Id, + MissileId = missile.Id + }); + + // 处理伤害 + actualTank.TakeDamage(50, true); + Debug.Log($"目标 {tank.Id} 被导弹 {missile.Id} 击中"); + } + } + } + } + } + } + + private void Update() + { + // 在Unity的Update中更新所有仿真元素 + foreach (var element in _elements.ToArray()) + { + if (element.IsActive) + { + element.Update(Time.deltaTime); + } + } + + // 处理目标命中检测 + HandleTargetHit(); + + // 清理不活跃的元素 + _elements.RemoveAll(e => !e.IsActive); + } + + private void OnDestroy() + { + // 清理所有事件订阅 + foreach (var eventPair in _eventDictionary) + { + eventPair.Value.RemoveAllListeners(); + } + _eventDictionary.Clear(); + _elements.Clear(); + } + } +} \ No newline at end of file diff --git a/src/Simulation/ISimulationManager.cs b/src/Simulation/ISimulationManager.cs new file mode 100644 index 0000000..6651ae4 --- /dev/null +++ b/src/Simulation/ISimulationManager.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace ActiveProtect.Simulation +{ + /// + /// 仿真管理器接口,定义了仿真管理器的基本功能 + /// + public interface ISimulationManager + { + /// + /// 当前仿真时间 + /// + double CurrentTime { get; } + + /// + /// 添加仿真元素 + /// + void AddElement(SimulationElement element); + + /// + /// 仿真元素列表 + /// + List GetElements(); + + /// + /// 根据ID获取仿真实体 + /// + SimulationElement GetEntityById(string id); + + /// + /// 处理目标被击中事件 + /// + void HandleTargetHit(); + + /// + /// 发布仿真事件 + /// + void PublishEvent(SimulationEvent evt); + + /// + /// 订阅仿真事件 + /// + void SubscribeToEvent(Action handler) where T : SimulationEvent; + + /// + /// 取消订阅仿真事件 + /// + void UnsubscribeFromEvent(Action handler) where T : SimulationEvent; + + /// + /// 取消所有事件订阅 + /// + void UnsubscribeAllEvents(SimulationElement element); + } +} \ No newline at end of file diff --git a/src/Simulation/SimulationManager.cs b/src/Simulation/SimulationManager.cs index f386f92..fbeca0e 100644 --- a/src/Simulation/SimulationManager.cs +++ b/src/Simulation/SimulationManager.cs @@ -6,57 +6,6 @@ using ActiveProtect.Utility; namespace ActiveProtect.Simulation { - /// - /// 仿真管理器接口,定义了仿真管理器的基本功能 - /// - public interface ISimulationManager - { - /// - /// 当前仿真时间 - /// - double CurrentTime { get; } - - /// - /// 添加仿真元素 - /// - void AddElement(SimulationElement element); - - /// - /// 仿真元素列表 - /// - List GetElements(); - - /// - /// 根据ID获取仿真实体 - /// - SimulationElement GetEntityById(string id); - - /// - /// 处理目标被击中事件 - /// - void HandleTargetHit(); - - /// - /// 发布仿真事件 - /// - void PublishEvent(SimulationEvent evt); - - /// - /// 订阅仿真事件 - /// - void SubscribeToEvent(Action handler) where T : SimulationEvent; - - /// - /// 取消订阅仿真事件 - /// - void UnsubscribeFromEvent(Action handler) where T : SimulationEvent; - - /// - /// 取消所有事件订阅 - /// - void UnsubscribeAllEvents(SimulationElement element); - } - /// /// 仿真管理器类,负责管理整个仿真过程 ///