增加了 Unity 适配器

This commit is contained in:
Tian jianyong 2024-11-20 12:40:04 +08:00
parent af702cbbda
commit a3ec9df001
7 changed files with 330 additions and 51 deletions

View File

@ -13,10 +13,13 @@
<ItemGroup>
<Compile Remove="obj\**" />
<Compile Remove="src\Tests\**" />
<Compile Remove="src\Adapters\**" />
<EmbeddedResource Remove="obj\**" />
<EmbeddedResource Remove="src\Tests\**" />
<EmbeddedResource Remove="src\Adapters\**" />
<None Remove="obj\**" />
<None Remove="src\Tests\**" />
<None Remove="src\Adapters\**" />
</ItemGroup>
</Project>

View File

@ -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
);
}
}
}

View File

@ -0,0 +1,31 @@
using UnityEngine;
namespace ActiveProtect.Adapters
{
/// <summary>
/// Unity导弹组件
/// </summary>
public class UnityMissile : UnitySimulationElement
{
[SerializeField]
private string missileId;
[SerializeField]
private MissileProperties missileProperties;
private Missile _missile;
protected override SimulationElement CreateSimulationElement()
{
var unityManager = FindObjectOfType<UnitySimulationManager>();
_missile = new Missile(missileId, transform.position.ToVector3D(),
transform.rotation.ToOrientation(), unityManager, missileProperties);
return _missile;
}
// Unity特有的功能如粒子效果
public void OnMissileLaunch()
{
GetComponent<ParticleSystem>()?.Play();
}
}
}

View File

@ -0,0 +1,42 @@
using UnityEngine;
namespace ActiveProtect.Adapters
{
/// <summary>
/// Unity仿真元素基类将我们的SimulationElement适配到MonoBehaviour
/// </summary>
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();
}
}

View File

@ -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
{
/// <summary>
/// Unity仿真管理器适配器将Unity的事件系统适配到我们的接口
/// </summary>
public class UnitySimulationManager : MonoBehaviour, ISimulationManager
{
// Unity事件字典用于事件订阅和发布
private readonly Dictionary<Type, UnityEvent<SimulationEvent>> _eventDictionary
= new Dictionary<Type, UnityEvent<SimulationEvent>>();
// 仿真元素列表
private readonly List<SimulationElement> _elements = new List<SimulationElement>();
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>(T evt) where T : SimulationEvent
{
PublishEvent(evt as SimulationEvent);
}
public void SubscribeToEvent<T>(Action<T> handler) where T : SimulationEvent
{
var eventType = typeof(T);
if (!_eventDictionary.ContainsKey(eventType))
{
_eventDictionary[eventType] = new UnityEvent<SimulationEvent>();
}
_eventDictionary[eventType].AddListener(evt =>
{
if (evt is T typedEvent)
{
handler(typedEvent);
}
});
}
public void UnsubscribeFromEvent<T>(Action<T> handler) where T : SimulationEvent
{
var eventType = typeof(T);
if (_eventDictionary.ContainsKey(eventType))
{
// Unity的事件系统不支持直接移除特定处理器需要重新创建事件
var newEvent = new UnityEvent<SimulationEvent>();
_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<UnitySimulationElement>();
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<SimulationElement> GetElements()
{
return new List<SimulationElement>(_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();
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
namespace ActiveProtect.Simulation
{
/// <summary>
/// 仿真管理器接口,定义了仿真管理器的基本功能
/// </summary>
public interface ISimulationManager
{
/// <summary>
/// 当前仿真时间
/// </summary>
double CurrentTime { get; }
/// <summary>
/// 添加仿真元素
/// </summary>
void AddElement(SimulationElement element);
/// <summary>
/// 仿真元素列表
/// </summary>
List<SimulationElement> GetElements();
/// <summary>
/// 根据ID获取仿真实体
/// </summary>
SimulationElement GetEntityById(string id);
/// <summary>
/// 处理目标被击中事件
/// </summary>
void HandleTargetHit();
/// <summary>
/// 发布仿真事件
/// </summary>
void PublishEvent(SimulationEvent evt);
/// <summary>
/// 订阅仿真事件
/// </summary>
void SubscribeToEvent<T>(Action<T> handler) where T : SimulationEvent;
/// <summary>
/// 取消订阅仿真事件
/// </summary>
void UnsubscribeFromEvent<T>(Action<T> handler) where T : SimulationEvent;
/// <summary>
/// 取消所有事件订阅
/// </summary>
void UnsubscribeAllEvents(SimulationElement element);
}
}

View File

@ -6,57 +6,6 @@ using ActiveProtect.Utility;
namespace ActiveProtect.Simulation
{
/// <summary>
/// 仿真管理器接口,定义了仿真管理器的基本功能
/// </summary>
public interface ISimulationManager
{
/// <summary>
/// 当前仿真时间
/// </summary>
double CurrentTime { get; }
/// <summary>
/// 添加仿真元素
/// </summary>
void AddElement(SimulationElement element);
/// <summary>
/// 仿真元素列表
/// </summary>
List<SimulationElement> GetElements();
/// <summary>
/// 根据ID获取仿真实体
/// </summary>
SimulationElement GetEntityById(string id);
/// <summary>
/// 处理目标被击中事件
/// </summary>
void HandleTargetHit();
/// <summary>
/// 发布仿真事件
/// </summary>
void PublishEvent(SimulationEvent evt);
/// <summary>
/// 订阅仿真事件
/// </summary>
void SubscribeToEvent<T>(Action<T> handler) where T : SimulationEvent;
/// <summary>
/// 取消订阅仿真事件
/// </summary>
void UnsubscribeFromEvent<T>(Action<T> handler) where T : SimulationEvent;
/// <summary>
/// 取消所有事件订阅
/// </summary>
void UnsubscribeAllEvents(SimulationElement element);
}
/// <summary>
/// 仿真管理器类,负责管理整个仿真过程
/// </summary>