diff --git a/Assets/TEngine/Runtime/ECS.meta b/Assets/TEngine/Runtime/ECS.meta new file mode 100644 index 00000000..fb5b638a --- /dev/null +++ b/Assets/TEngine/Runtime/ECS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d941eb2d2cbae294bb5d2788bb724e80 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ArrayPool.cs b/Assets/TEngine/Runtime/ECS/ArrayPool.cs new file mode 100644 index 00000000..c276893f --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ArrayPool.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace TEngine +{ + public interface IIndex + { + int Index { get; set; } + } + + internal class HashSetDebugView where T : IIndex + { + private readonly ArrayPool m_Set; + + public HashSetDebugView(ArrayPool set) + { + m_Set = set ?? throw new ArgumentNullException(nameof(set)); + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items + { + get + { + return m_Set.ToArray(); + } + } + } + + [DebuggerTypeProxy(typeof(HashSetDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + public class ArrayPool where T:IIndex + { + internal T[] m_Items = new T[256]; + internal bool[] Buckets = new bool[256]; + private int m_Index; + private int count; + + public T this[int index] + { + get + { + return m_Items[index]; + } + set + { + m_Items[index] = value; + } + } + + public int Count + { + get + { + return count; + } + } + + public T[] ToArray() + { + List elements = new List(); + for (int i = 0; i < m_Items.Length; i++) + { + if (Buckets[i]) + { + elements.Add(m_Items[i]); + } + } + return elements.ToArray(); + } + + public void Remove(T item) + { + lock (this) + { + m_Items[item.Index] = default; + Buckets[item.Index] = false; + } + } + + public void Add(T item) + { + lock (this) + { + if (item.Index != -1) + { + if (!Buckets[item.Index]) + { + m_Items[item.Index] = item; + Buckets[item.Index] = true; + return; + } + } + + m_Items[m_Index] = item; + Buckets[m_Index] = true; + item.Index = m_Index; + m_Index++; + if (m_Index >= m_Items.Length) + { + T[] newItems = new T[m_Items.Length * 2]; + bool[] newBuckets = new bool[m_Items.Length * 2]; + Array.Copy(m_Items,0,newItems,0,m_Items.Length); + Array.Copy(Buckets, 0, newBuckets, 0, Buckets.Length); + m_Items = newItems; + Buckets = newBuckets; + } + count = m_Index; + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/ArrayPool.cs.meta b/Assets/TEngine/Runtime/ECS/ArrayPool.cs.meta new file mode 100644 index 00000000..19b33d1b --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ArrayPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1abd052a77e45b44ebcde5803fb36afd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/Demo.meta b/Assets/TEngine/Runtime/ECS/Demo.meta new file mode 100644 index 00000000..5a88a2d9 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Demo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 385b1863dab169f4fa291611477e106c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs b/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs new file mode 100644 index 00000000..79350d96 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs @@ -0,0 +1,25 @@ +using TEngine; +using UnityEngine; + +public class ECSDemoApp : MonoBehaviour +{ + public ECSGameSystem EcsGameSystem = new ECSGameSystem(); + public GameObject @object; + + void Start() + { + var entity = EcsGameSystem.Create(); + ECSActor actor = entity.AddComponent(); + actor.Name = typeof(ECSActor).ToString(); + actor.gameObject = Instantiate(@object); + actor.transform = actor.gameObject.GetComponent(); + entity.AddComponent(); + + Debug.Log(entity.ToString()); + } + + void Update() + { + EcsGameSystem.OnUpdate(); + } +} diff --git a/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs.meta b/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs.meta new file mode 100644 index 00000000..e837b29f --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Demo/ECSDemoApp.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb5cf3287e9bf3949820b50049e4bd8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs b/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs new file mode 100644 index 00000000..e2e2a496 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs @@ -0,0 +1,9 @@ +using TEngine; + +public class ECSGameSystem : ECSSystem +{ + public void OnUpdate() + { + Update(); + } +} diff --git a/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs.meta b/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs.meta new file mode 100644 index 00000000..3884ee61 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Demo/ECSGameSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9d458a85873d744ab382e86008b89a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSActor.cs b/Assets/TEngine/Runtime/ECS/ECSActor.cs new file mode 100644 index 00000000..457bf862 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSActor.cs @@ -0,0 +1,13 @@ +namespace TEngine +{ + /// + /// ECS Actor + /// + public class ECSActor : ECSComponent + { + public string Name; + public UnityEngine.GameObject gameObject; + public UnityEngine.Transform transform; + public uint ActorID; + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/ECSActor.cs.meta b/Assets/TEngine/Runtime/ECS/ECSActor.cs.meta new file mode 100644 index 00000000..f0150236 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSActor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34a977c48d3bdb144b7c923a702164b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSComponent.cs b/Assets/TEngine/Runtime/ECS/ECSComponent.cs new file mode 100644 index 00000000..4f630a73 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSComponent.cs @@ -0,0 +1,14 @@ +namespace TEngine +{ + /// + /// ECS构架可以将此组件从Entity上移除这个组件并丢入对象池,给其他此刻需要此组件的Entity使用, + /// 因此可以节省大量的内存反复创建和释放, 这也是ECS的特性可以大量重复使用组件 + /// + public class ECSComponent:ECSObject + { +#pragma warning disable IDE1006 + public Entity Entity { get; set; } +#pragma warning restore IDE1006 + } +} + diff --git a/Assets/TEngine/Runtime/ECS/ECSComponent.cs.meta b/Assets/TEngine/Runtime/ECS/ECSComponent.cs.meta new file mode 100644 index 00000000..c23d0901 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e93d874e10ffedf488c413fd5bbc012f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs b/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs new file mode 100644 index 00000000..f1d502bf --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace TEngine +{ + public enum ECSDebugType + { + Entity, + System, + Component + } + + [Serializable] + public class ECSCmptDebugKeyInfo + { + public string m_name; + public string val; + } + + [Serializable] + public class ECSCmptDebugInfo + { + public string m_name; + public ECSDebugType Type = ECSDebugType.Component; + public List m_info = new List(); + } + + public class ECSDebugBehaviour : UnityEngine.MonoBehaviour + { + public List m_ECSInfo = new List(); + public ECSCmptDebugInfo AddDebugCmpt(string cmptName) + { + var cmptInfo = m_ECSInfo.Find((item) => { return item.m_name == cmptName; }); + if (cmptInfo == null) + { + cmptInfo = new ECSCmptDebugInfo(); + cmptInfo.m_name = cmptName; + m_ECSInfo.Add(cmptInfo); ; + } + + return cmptInfo; + } + + public void RmvDebugCmpt(string cmptName) + { + m_ECSInfo.RemoveAll((item) => { return item.m_name == cmptName; }); + } + + public void SetDebugInfo(string cmptName, string key, string val) + { + var cmptInfo = AddDebugCmpt(cmptName); + var entry = cmptInfo.m_info.Find((t) => { return t.m_name == key; }); + if (entry == null) + { + entry = new ECSCmptDebugKeyInfo(); + entry.m_name = key; + cmptInfo.m_info.Add(entry); + } + + entry.val = val; + } + + public void RemoveAllDebugInfo(string cmpName) + { + var cmpInfo = m_ECSInfo.Find((item) => { return item.m_name == cmpName; }); + if (cmpInfo != null) + { + cmpInfo.m_info.Clear(); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs.meta b/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs.meta new file mode 100644 index 00000000..3e53690c --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSDebugBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2dc3e456c545c0c419624e068b9249dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs b/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs new file mode 100644 index 00000000..2327b482 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; + +namespace TEngine +{ + public class ECSEventCmpt : ECSComponent + { + private GameEvent _gameEvent; + + #region AddEventListener + public void AddEventListener(int eventId, Action action) + { + _gameEvent.AddEventListener(eventId, action); + } + + public void AddEventListener(int eventId, Action action) + { + _gameEvent.AddEventListener(eventId, action); + } + + public void AddEventListener(int eventId, Action action) + { + _gameEvent.AddEventListener(eventId, action); + } + + public void AddEventListener(int eventId, Action action) + { + _gameEvent.AddEventListener(eventId, action); + } + #endregion + + #region RemoveEventListener + public void RemoveEventListener(int eventId, Action action) + { + _gameEvent.RemoveEventListener(eventId, action); + } + + public void RemoveEventListener(int eventId, Action action) + { + _gameEvent.RemoveEventListener(eventId, action); + } + + public void RemoveEventListener(int eventId, Action action) + { + _gameEvent.RemoveEventListener(eventId, action); + } + + public void RemoveEventListener(int eventId, Action action) + { + _gameEvent.RemoveEventListener(eventId, action); + } + #endregion + + #region Send + public void Send(int eventId, T info) + { + _gameEvent.Send(eventId, info); + } + + public void Send(int eventId, T info, U info2) + { + _gameEvent.Send(eventId, info, info2); + } + + public void Send(int eventId, T info, U info2, W info3) + { + _gameEvent.Send(eventId, info, info2, info3); + } + + public void Send(int eventId) + { + _gameEvent.Send(eventId); + } + #endregion + + #region Clear + public void Clear() + { + GameMemPool.Free(_gameEvent); + } + #endregion + + #region 生命周期 + public override void OnDestroy() + { + Clear(); + } + + public override void Awake() + { + _gameEvent = GameMemPool.Alloc(); + Entity.Event = this; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs.meta b/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs.meta new file mode 100644 index 00000000..ad55fd08 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSEventCmpt.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af343da6b3090a24b92fc12aaa638411 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSObject.cs b/Assets/TEngine/Runtime/ECS/ECSObject.cs new file mode 100644 index 00000000..d8850f07 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSObject.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; + +namespace TEngine +{ + /// + /// ECS架构基类Object + /// + public class ECSObject + { + internal int HashCode; + + internal ECSSystem System; + + public ECSObject() + { + HashCode = GetType().GetHashCode(); + } + + public virtual void Awake() { } + + public virtual void OnDestroy() { } + + /// + /// Remove The ECSEntity or Component And Throw the ECSObject to ArrayPool When AddComponent Or Create Can Use Again + /// + /// + /// 此对象是否可以复用,复用会将对象丢入System对象池中 等待再次使用,如果是Entity对象,并且不复用的话,则把Entity所使用的组件也不复用 + public static void Destroy(ECSObject ecsObject, bool reuse = true) + { + if (ecsObject is ECSComponent ecsComponent) + { + ecsComponent.Entity.Components.Remove(ecsComponent); + if (ecsComponent is IUpdate update) + { + ecsComponent.Entity.Updates.Remove(update); + } + if (reuse) + { + ecsComponent.Entity.System.Push(ecsComponent); + } + ecsObject.OnDestroy(); + return; + } + else if (ecsObject is Entity entity) + { + entity.System.RemoveEntity(entity); + entity.OnDestroy(); + while (entity.Components.Count > 0) + { + ECSComponent ecsComponentTemp = entity.Components[0]; + entity.Components.RemoveAt(0); + ecsComponentTemp.OnDestroy(); + if (reuse) + { + entity.System.Push(ecsComponentTemp); + } + } + entity.Updates.Clear(); + entity.CanUpdate = false; + if (reuse) + { + entity.System.Push(entity); + } + } + } + + public T FindObjectOfType() where T : ECSObject + { + Type type = typeof(T); + var elements = System.Entities.ToArray(); + for (int i = 0; i < elements.Length; i++) + { + if (elements[i].GetType() == type) + { + return elements[i] as T; + } + for (int n = 0; n < elements[i].Components.Count; n++) + { + if (elements[i].Components[n].GetType() == type) + { + return elements[i].Components[n] as T; + } + } + } + return null; + } + + public T[] FindObjectsOfType() where T : ECSObject + { + Type type = typeof(T); + var items = System.Entities.ToArray(); + List elements = new List(); + for (int i = 0; i < items.Length; i++) + { + if (items[i].GetType() == type) + { + elements.Add(items[i] as T); + } + for (int n = 0; n < items[i].Components.Count; n++) + { + if (items[i].Components[n].GetType() == type) + { + elements.Add(items[i].Components[n] as T); + } + } + } + return elements.ToArray(); + } + } +} + diff --git a/Assets/TEngine/Runtime/ECS/ECSObject.cs.meta b/Assets/TEngine/Runtime/ECS/ECSObject.cs.meta new file mode 100644 index 00000000..e9b31b25 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 003794832e9179348a633bdafe92ff3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/ECSSystem.cs b/Assets/TEngine/Runtime/ECS/ECSSystem.cs new file mode 100644 index 00000000..17af12e3 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSSystem.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace TEngine +{ + /// + /// ECS系统 管理Entity、ECSComponent复用对象池 + /// + [Serializable] + public class ECSSystem : IDisposable + { + private static ECSSystem instance = new ECSSystem(); + public static ECSSystem Instance => instance; + private readonly Dictionary> m_ObjectPool = new Dictionary>(); + internal readonly ArrayPool Entities = new ArrayPool(); + private bool m_IsDispose = false; + + public void AddEntity(Entity entity) + { + entity.System = this; + entity.Awake(); + Entities.Add(entity); + } + + public void RemoveEntity(Entity entity) + { + Entities.Remove(entity); + } + + /// + /// Get Object From ECSSystem + /// + /// + /// + public T Get() where T : ECSObject, new() + { + int type = typeof(T).GetHashCode(); + if (m_ObjectPool.TryGetValue(type,out Stack stack)) + { + if (stack.Count > 0) + { + return (T) stack.Pop(); + } + goto Instantiate; + } + stack = new Stack(); + m_ObjectPool.Add(type,stack); + Instantiate: T ecsObject = new T(); + return ecsObject; + } + + /// + /// Push Object To ECSSystem + /// + public void Push(ECSObject ecsObject) + { + int type = ecsObject.HashCode; + + if (m_ObjectPool.TryGetValue(type,out Stack stack)) + { + stack.Push(ecsObject); + return; + } + stack = new Stack(); + m_ObjectPool.Add(type,stack); + stack.Push(ecsObject); + } + + public T Create() where T : Entity, new() + { + T entity = Get(); + AddEntity(entity); + return entity; + } + + public T Create(T entity) where T : Entity, new() + { + AddEntity(entity); + return entity; + } + + /// + /// 更新ECS系统 + /// + /// 线程池是否并行 + public void Update(bool worker = false) + { + Run(worker); + } + + /// + /// 更新ECS物理系统 + /// + /// 线程池是否并行 + public void FixedUpdate(bool worker = false) + { + RunFixed(worker); + } + + /// + /// 运行ECS系统 + /// + /// 线程池是否并行 + public void Run(bool worker = false) + { + int count = Entities.Count; + if (!worker) + { + for (int i = 0; i < count; i++) + { + if (!Entities.Buckets[i]) + { + continue; + } + + if (!Entities[i].CanUpdate) + { + continue; + } + Entities[i].Execute(); + } + } + else + { + Parallel.For(0, count, i => + { + if (!Entities.Buckets[i]) + { + return; + } + + if (!Entities[i].CanUpdate) + { + return; + } + Entities[i].Execute(); + }); + } + } + + public void RunFixed(bool worker = false) + { + int count = Entities.Count; + if (!worker) + { + for (int i = 0; i < count; i++) + { + if (!Entities.Buckets[i]) + { + continue; + } + + if (!Entities[i].CanFixedUpdate) + { + continue; + } + Entities[i].FixedUpdate(); + } + } + else + { + Parallel.For(0, count, i => + { + if (!Entities.Buckets[i]) + { + return; + } + + if (!Entities[i].CanFixedUpdate) + { + return; + } + Entities[i].FixedUpdate(); + }); + } + } + + public void Dispose() + { + if (m_IsDispose) + { + return; + } + m_IsDispose = true; + } + + public T FindObjectOfType() where T : ECSObject + { + Type type = typeof(T); + var elements = Entities.ToArray(); + for (int i = 0; i < elements.Length; i++) + { + if (elements[i].GetType() == type) + { + return elements[i] as T; + } + + for (int j = 0; j < elements[i].Components.Count; j++) + { + if (elements[i].Components[j].GetType() == type) + { + return elements[i].Components[j] as T; + } + } + } + return null; + } + + public T[] FindObjectsOfType() where T : ECSObject + { + Type type = typeof(T); + var entities = Entities.ToArray(); + List elements = new List(); + for (int i = 0; i < entities.Length; i++) + { + if (entities[i].GetType() == type) + { + elements.Add(entities[i] as T); + } + for (int n = 0; n < entities[i].Components.Count; n++) + { + if (entities[i].Components[n].GetType() == type) + { + elements.Add(entities[i].Components[n] as T); + } + } + } + return elements.ToArray(); + } + } +} + diff --git a/Assets/TEngine/Runtime/ECS/ECSSystem.cs.meta b/Assets/TEngine/Runtime/ECS/ECSSystem.cs.meta new file mode 100644 index 00000000..08b322ab --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/ECSSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2bd4af51a518164c8da0dff296b2bb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/Entity.cs b/Assets/TEngine/Runtime/ECS/Entity.cs new file mode 100644 index 00000000..b668a171 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Entity.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine +{ + public class Entity : ECSObject, IIndex + { + [SerializeField] + internal List Components = new List(); + internal List Updates = new List(); + internal List FixedUpdates = new List(); + internal bool InActive; + internal bool CanUpdate; + internal bool CanFixedUpdate; + public int Index { get; set; } = -1; + public ECSEventCmpt Event { get; set; } + public Entity() + { + InActive = true; + System = ECSSystem.Instance; + } + + ~Entity() + { + InActive = false; + } + + internal void Execute() + { + for (int i = 0; i < Updates.Count; i++) + { + Updates[i].Update(); + } + } + + internal void FixedUpdate() + { + for (int i = 0; i < FixedUpdates.Count; i++) + { + FixedUpdates[i].FixedUpdate(); + } + } + + public void RmvComponent() where T : ECSComponent, new() + { + for (int i = 0; i < Components.Count; i++) + { + if (Components[i] is T component) + { + if (component is IUpdate update) + { + Updates.Remove(update); + } + else if (component is IFixedUpdate fixedUpdate) + { + FixedUpdates.Remove(fixedUpdate); + } + + System.Push(component); + + CanUpdate = Updates.Count > 0; + + CanFixedUpdate = FixedUpdates.Count > 0; + } + } + +#if UNITY_EDITOR + CheckDebugInfo(); +#endif + } + + public void RmvComponent(Type componentType) + { + for (int i = 0; i < Components.Count; i++) + { + if (Components[i].GetType() == componentType) + { + + if (componentType is IUpdate update) + { + Updates.Remove(update); + + CanUpdate = Updates.Count > 0; + } + else if (componentType is IFixedUpdate fixedUpdate) + { + FixedUpdates.Remove(fixedUpdate); + + CanFixedUpdate = FixedUpdates.Count > 0; + } + //if (componentType is ECSComponent component) + //{ + // System.Push(component); + //} + } + } +#if UNITY_EDITOR + CheckDebugInfo(); +#endif + } + + public T AddComponent() where T : ECSComponent, new() + { +#if UNITY_EDITOR + CheckDebugInfo(); +#endif + T component = System.Get(); + component.Entity = this; + component.System = System; + Components.Add(component); + component.Awake(); + if (component is IUpdate update) + { + Updates.Add(update); + } + else if (component is IFixedUpdate fixedUpdate) + { + FixedUpdates.Add(fixedUpdate); + } + CanUpdate = Updates.Count > 0; + CanFixedUpdate = FixedUpdates.Count > 0; + return component; + } + + public ECSComponent AddComponent(ECSComponent component) + { +#if UNITY_EDITOR + CheckDebugInfo(); +#endif + component.Entity = this; + component.System = System; + Components.Add(component); + component.Awake(); + if (component is IUpdate update) + { + Updates.Add(update); + } + else if (component is IFixedUpdate fixedUpdate) + { + FixedUpdates.Add(fixedUpdate); + } + CanUpdate = Updates.Count > 0; + CanFixedUpdate = FixedUpdates.Count > 0; + return component; + } + + public T GetComponent() where T : ECSComponent + { + for (int i = 0; i < Components.Count; i++) + { + if (Components[i] is T type) + { + return type; + } + } + + return null; + } + + public ECSComponent GetComponent(Type componentType) + { + for (int i = 0; i < Components.Count; i++) + { + if (Components[i].GetType() == componentType) + { + return Components[i]; + } + } + + return null; + } + + public T[] GetComponents() where T : ECSComponent + { + List elements = new List(); + for (int i = 0; i < Components.Count; i++) + { + if (Components[i] is T type) + { + elements.Add(type); + } + } + return elements.ToArray(); + } + + public List GetComponentsList() where T : ECSComponent + { + List elements = new List(); + for (int i = 0; i < Components.Count; i++) + { + if (Components[i] is T type) + { + elements.Add(type); + } + } + return elements; + } + + public ECSComponent[] GetComponents(Type comType) + { + List elements = new List(); + for (int i = 0; i < Components.Count; i++) + { + { + if (Components[i].GetType() == comType) + { + elements.Add(Components[i]); + } + } + } + return elements.ToArray(); + } + + + public override string ToString() + { + string str = "["; + for (int i = 0; i < Components.Count; i++) + { + str += Components[i].GetType().Name + ","; + } + str = str.TrimEnd(','); + str += "]"; + return $"{GetType().Name} Components: {str}"; + } + + + public void CheckDebugInfo(GameObject gameObject) + { +#if UNITY_EDITOR + if (gameObject == null) + { + return; + } + + var debugBehaviour = UnityUtil.AddMonoBehaviour(gameObject); + debugBehaviour.m_ECSInfo.Clear(); + for (int i = 0; i < this.Components.Count; i++) + { + var component = this.Components[i]; + var cmptName = component.GetType().Name; + debugBehaviour.SetDebugInfo(cmptName, "", ""); + } +#endif + } + public void CheckDebugInfo() + { +#if UNITY_EDITOR + //var actorEntity = this as Entity; + + //if (actorEntity == null) + //{ + // return; + //} + + //if (actorEntity.gameObject == null) + //{ + // return; + //} + + //var debugBehaviour = UnityUtil.AddMonoBehaviour(actorEntity.gameObject); + //debugBehaviour.m_ECSInfo.Clear(); + //for (int i = 0; i < this.Components.Count; i++) + //{ + // var component = this.Components[i]; + // var cmptName = component.GetType().Name; + // debugBehaviour.SetDebugInfo(cmptName, "", ""); + //} +#endif + } + } +} diff --git a/Assets/TEngine/Runtime/ECS/Entity.cs.meta b/Assets/TEngine/Runtime/ECS/Entity.cs.meta new file mode 100644 index 00000000..03366bfb --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/Entity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f5bc14d7a4aee34695c39d02ae1c71f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/HotfixComponent.cs b/Assets/TEngine/Runtime/ECS/HotfixComponent.cs new file mode 100644 index 00000000..1c9fc418 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/HotfixComponent.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine +{ + public class HotfixComponent : ECSComponent,IUpdate + { + public object[] Values; + public Action OnAwake, OnUpdate, OnDestroyExt; + + public override void Awake() + { + OnAwake?.Invoke(); + } + + void IUpdate.Update() + { + OnUpdate?.Invoke(); + } + + public override void OnDestroy() + { + OnDestroyExt?.Invoke(); + OnAwake = null; + OnUpdate = null; + OnDestroyExt = null; + } + } +} + diff --git a/Assets/TEngine/Runtime/ECS/HotfixComponent.cs.meta b/Assets/TEngine/Runtime/ECS/HotfixComponent.cs.meta new file mode 100644 index 00000000..0017d198 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/HotfixComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0ac8b7fabe2bae499a568608191eb57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs b/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs new file mode 100644 index 00000000..ac961f4b --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs @@ -0,0 +1,10 @@ +namespace TEngine +{ + /// + /// ECS组件更新接口(减少组件for循环开销) + /// + public interface IFixedUpdate + { + void FixedUpdate(); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs.meta b/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs.meta new file mode 100644 index 00000000..448ce4a6 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/IFixedUpdate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7fb8a7b6d364a024e8a49a0a731de201 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/IUpdate.cs b/Assets/TEngine/Runtime/ECS/IUpdate.cs new file mode 100644 index 00000000..1f2d4bd5 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/IUpdate.cs @@ -0,0 +1,10 @@ +namespace TEngine +{ + /// + /// ECS组件更新接口(减少组件for循环开销) + /// + public interface IUpdate + { + void Update(); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/IUpdate.cs.meta b/Assets/TEngine/Runtime/ECS/IUpdate.cs.meta new file mode 100644 index 00000000..510b8ebd --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/IUpdate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7918afae3870234391c6e6f521e9ca7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/ECS/UpdateComponent.cs b/Assets/TEngine/Runtime/ECS/UpdateComponent.cs new file mode 100644 index 00000000..ebfa41e8 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/UpdateComponent.cs @@ -0,0 +1,13 @@ +namespace TEngine +{ + /// + /// 热更层由此组件进行更新(ILRuntime不支持多继承, 接口继承) + /// + public class UpdateComponent :ECSComponent, IUpdate + { + public virtual void Update() + { + + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/ECS/UpdateComponent.cs.meta b/Assets/TEngine/Runtime/ECS/UpdateComponent.cs.meta new file mode 100644 index 00000000..2a670870 --- /dev/null +++ b/Assets/TEngine/Runtime/ECS/UpdateComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02463b27b6971f0408bb517c32c66553 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: