Add main mesh fixes
This commit is contained in:
184
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsComponent.cs
Normal file
184
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsComponent.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
/// <summary>
|
||||
/// Marks component type to be not auto-filled as GetX in filter.
|
||||
/// </summary>
|
||||
public interface IEcsIgnoreInFilter { }
|
||||
|
||||
/// <summary>
|
||||
/// Marks component type as resettable with custom logic.
|
||||
/// </summary>
|
||||
public interface IEcsAutoReset {
|
||||
void Reset ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks field of IEcsSystem class to be ignored during dependency injection.
|
||||
/// </summary>
|
||||
public sealed class EcsIgnoreInjectAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Marks field of component to be not checked for null on component removing.
|
||||
/// Works only in DEBUG mode!
|
||||
/// </summary>
|
||||
[System.Diagnostics.Conditional ("DEBUG")]
|
||||
[AttributeUsage (AttributeTargets.Field)]
|
||||
public sealed class EcsIgnoreNullCheckAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Global descriptor of used component type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type.</typeparam>
|
||||
public static class EcsComponentType<T> where T : class {
|
||||
// ReSharper disable StaticMemberInGenericType
|
||||
public static readonly int TypeIndex;
|
||||
public static readonly Type Type;
|
||||
public static readonly bool IsAutoReset;
|
||||
public static readonly bool IsIgnoreInFilter;
|
||||
// ReSharper restore StaticMemberInGenericType
|
||||
|
||||
static EcsComponentType () {
|
||||
TypeIndex = Interlocked.Increment (ref EcsComponentPool.ComponentTypesCount);
|
||||
Type = typeof (T);
|
||||
IsAutoReset = typeof (IEcsAutoReset).IsAssignableFrom (Type);
|
||||
IsIgnoreInFilter = typeof (IEcsIgnoreInFilter).IsAssignableFrom (Type);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
public sealed class EcsComponentPool {
|
||||
/// <summary>
|
||||
/// Global component type counter.
|
||||
/// First component will be "1" for correct filters updating (add component on positive and remove on negative).
|
||||
/// </summary>
|
||||
internal static int ComponentTypesCount;
|
||||
|
||||
#if DEBUG
|
||||
readonly List<System.Reflection.FieldInfo> _nullableFields = new List<System.Reflection.FieldInfo> (8);
|
||||
#endif
|
||||
|
||||
public object[] Items = new Object[128];
|
||||
|
||||
Func<object> _customCtor;
|
||||
readonly Type _type;
|
||||
readonly bool _isAutoReset;
|
||||
|
||||
int[] _reservedItems = new int[128];
|
||||
int _itemsCount;
|
||||
int _reservedItemsCount;
|
||||
|
||||
internal EcsComponentPool (Type cType, bool isAutoReset) {
|
||||
_type = cType;
|
||||
_isAutoReset = isAutoReset;
|
||||
#if DEBUG
|
||||
// collect all marshal-by-reference fields.
|
||||
var fields = _type.GetFields ();
|
||||
for (var i = 0; i < fields.Length; i++) {
|
||||
var field = fields[i];
|
||||
if (!Attribute.IsDefined (field, typeof (EcsIgnoreNullCheckAttribute))) {
|
||||
var type = field.FieldType;
|
||||
var underlyingType = Nullable.GetUnderlyingType (type);
|
||||
if (!type.IsValueType || (underlyingType != null && !underlyingType.IsValueType)) {
|
||||
if (type != typeof (string)) {
|
||||
_nullableFields.Add (field);
|
||||
}
|
||||
}
|
||||
if (type == typeof (EcsEntity)) {
|
||||
_nullableFields.Add (field);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets custom constructor for component instances.
|
||||
/// </summary>
|
||||
/// <param name="ctor"></param>
|
||||
public void SetCustomCtor (Func<object> ctor) {
|
||||
#if DEBUG
|
||||
// ReSharper disable once JoinNullCheckWithUsage
|
||||
if (ctor == null) { throw new Exception ("Ctor is null."); }
|
||||
#endif
|
||||
_customCtor = ctor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets new capacity (if more than current amount).
|
||||
/// </summary>
|
||||
/// <param name="capacity">New value.</param>
|
||||
public void SetCapacity (int capacity) {
|
||||
if (capacity > Items.Length) {
|
||||
Array.Resize (ref Items, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public int New () {
|
||||
int id;
|
||||
if (_reservedItemsCount > 0) {
|
||||
id = _reservedItems[--_reservedItemsCount];
|
||||
} else {
|
||||
id = _itemsCount;
|
||||
if (_itemsCount == Items.Length) {
|
||||
Array.Resize (ref Items, _itemsCount << 1);
|
||||
}
|
||||
var instance = _customCtor != null ? _customCtor () : Activator.CreateInstance (_type);
|
||||
// reset brand new instance if component implements IEcsAutoReset.
|
||||
if (_isAutoReset) {
|
||||
((IEcsAutoReset) instance).Reset ();
|
||||
}
|
||||
Items[_itemsCount++] = instance;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public object GetItem (int idx) {
|
||||
return Items[idx];
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void Recycle (int idx) {
|
||||
if (_isAutoReset) {
|
||||
((IEcsAutoReset) Items[idx]).Reset ();
|
||||
}
|
||||
#if DEBUG
|
||||
// check all marshal-by-reference typed fields for nulls.
|
||||
var obj = Items[idx];
|
||||
for (int i = 0, iMax = _nullableFields.Count; i < iMax; i++) {
|
||||
if (_nullableFields[i].FieldType.IsValueType) {
|
||||
if (_nullableFields[i].FieldType == typeof (EcsEntity) && ((EcsEntity) _nullableFields[i].GetValue (obj)).Owner != null) {
|
||||
throw new Exception (
|
||||
$"Memory leak for \"{_type.Name}\" component: \"{_nullableFields[i].Name}\" field not null-ed with EcsEntity.Null. If you are sure that it's not - mark field with [EcsIgnoreNullCheck] attribute.");
|
||||
}
|
||||
} else {
|
||||
if (_nullableFields[i].GetValue (obj) != null) {
|
||||
throw new Exception (
|
||||
$"Memory leak for \"{_type.Name}\" component: \"{_nullableFields[i].Name}\" field not null-ed. If you are sure that it's not - mark field with [EcsIgnoreNullCheck] attribute.");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_reservedItemsCount == _reservedItems.Length) {
|
||||
Array.Resize (ref _reservedItems, _reservedItemsCount << 1);
|
||||
}
|
||||
_reservedItems[_reservedItemsCount++] = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15b66a8957ba23c418ec0f6922d999e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
301
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsEntity.cs
Normal file
301
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsEntity.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
/// <summary>
|
||||
/// Entity descriptor.
|
||||
/// </summary>
|
||||
public struct EcsEntity {
|
||||
internal int Id;
|
||||
internal ushort Gen;
|
||||
internal EcsWorld Owner;
|
||||
|
||||
public static readonly EcsEntity Null = new EcsEntity ();
|
||||
|
||||
/// <summary>
|
||||
/// Attaches or finds already attached component to entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of component.</typeparam>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public T Set<T> () where T : class {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant add component to destroyed entity."); }
|
||||
#endif
|
||||
var typeIdx = EcsComponentType<T>.TypeIndex;
|
||||
// check already attached components.
|
||||
for (int i = 0, iiMax = entityData.ComponentsCountX2; i < iiMax; i += 2) {
|
||||
if (entityData.Components[i] == typeIdx) {
|
||||
return (T) Owner.ComponentPools[typeIdx].Items[entityData.Components[i + 1]];
|
||||
}
|
||||
}
|
||||
// attach new component.
|
||||
if (entityData.Components.Length == entityData.ComponentsCountX2) {
|
||||
Array.Resize (ref entityData.Components, entityData.ComponentsCountX2 << 1);
|
||||
}
|
||||
entityData.Components[entityData.ComponentsCountX2++] = typeIdx;
|
||||
|
||||
var pool = Owner.GetPool<T> ();
|
||||
|
||||
var idx = pool.New ();
|
||||
entityData.Components[entityData.ComponentsCountX2++] = idx;
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
|
||||
Owner.DebugListeners[ii].OnComponentListChanged (this);
|
||||
}
|
||||
#endif
|
||||
Owner.UpdateFilters (typeIdx, this, entityData);
|
||||
return (T) pool.Items[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets component attached to entity or null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of component.</typeparam>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public T Get<T> () where T : class {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant check component on destroyed entity."); }
|
||||
#endif
|
||||
var typeIdx = EcsComponentType<T>.TypeIndex;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
if (entityData.Components[i] == typeIdx) {
|
||||
return (T) Owner.ComponentPools[typeIdx].Items[entityData.Components[i + 1]];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes component from entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of component.</typeparam>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void Unset<T> () where T : class {
|
||||
Unset (EcsComponentType<T>.TypeIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes component from entity.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
internal void Unset (int typeIndex) {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
// save copy to local var for protect from cleanup fields outside.
|
||||
var owner = Owner;
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
|
||||
#endif
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
if (entityData.Components[i] == typeIndex) {
|
||||
owner.UpdateFilters (-typeIndex, this, entityData);
|
||||
owner.ComponentPools[typeIndex].Recycle (entityData.Components[i + 1]);
|
||||
// remove current item and move last component to this gap.
|
||||
entityData.ComponentsCountX2 -= 2;
|
||||
if (i < entityData.ComponentsCountX2) {
|
||||
entityData.Components[i] = entityData.Components[entityData.ComponentsCountX2];
|
||||
entityData.Components[i + 1] = entityData.Components[entityData.ComponentsCountX2 + 1];
|
||||
}
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
|
||||
Owner.DebugListeners[ii].OnComponentListChanged (this);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
// unrolled and inlined Destroy() call.
|
||||
if (entityData.ComponentsCountX2 == 0) {
|
||||
owner.RecycleEntityData (Id, ref entityData);
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
|
||||
owner.DebugListeners[ii].OnEntityDestroyed (this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets component index at component pool.
|
||||
/// If component doesn't exists "-1" will be returned.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of component.</typeparam>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public int GetComponentIndexInPool<T> () where T : class {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant check component on destroyed entity."); }
|
||||
#endif
|
||||
var typeIdx = EcsComponentType<T>.TypeIndex;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
if (entityData.Components[i] == typeIdx) {
|
||||
return entityData.Components[i + 1];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets internal identifier.
|
||||
/// </summary>
|
||||
public int GetInternalId () {
|
||||
return Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes components from entity and destroys it.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void Destroy () {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
// save copy to local var for protect from cleanup fields outside.
|
||||
EcsEntity savedEntity;
|
||||
savedEntity.Id = Id;
|
||||
savedEntity.Gen = Gen;
|
||||
savedEntity.Owner = Owner;
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
|
||||
#endif
|
||||
// remove components first.
|
||||
for (var i = entityData.ComponentsCountX2 - 2; i >= 0; i -= 2) {
|
||||
savedEntity.Owner.UpdateFilters (-entityData.Components[i], savedEntity, entityData);
|
||||
savedEntity.Owner.ComponentPools[entityData.Components[i]].Recycle (entityData.Components[i + 1]);
|
||||
entityData.ComponentsCountX2 -= 2;
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < savedEntity.Owner.DebugListeners.Count; ii++) {
|
||||
savedEntity.Owner.DebugListeners[ii].OnComponentListChanged (savedEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
entityData.ComponentsCountX2 = 0;
|
||||
savedEntity.Owner.RecycleEntityData (savedEntity.Id, ref entityData);
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < savedEntity.Owner.DebugListeners.Count; ii++) {
|
||||
savedEntity.Owner.DebugListeners[ii].OnEntityDestroyed (savedEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is entity null-ed.
|
||||
/// </summary>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsNull () {
|
||||
return Id == 0 && Gen == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is entity alive.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAlive () {
|
||||
if (Owner == null) { return false; }
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
return entityData.Gen == Gen && entityData.ComponentsCountX2 >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets components count on entity.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public int GetComponentsCount () {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
|
||||
#endif
|
||||
return entityData.ComponentsCountX2 <= 0 ? 0 : (entityData.ComponentsCountX2 >> 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all components on entity.
|
||||
/// </summary>
|
||||
/// <param name="list">List to put results in it. if null - will be created.</param>
|
||||
/// <returns>Amount of components in list.</returns>
|
||||
public int GetComponents (ref object[] list) {
|
||||
ref var entityData = ref Owner.GetEntityData (this);
|
||||
#if DEBUG
|
||||
if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
|
||||
#endif
|
||||
var itemsCount = entityData.ComponentsCountX2 >> 1;
|
||||
if (list == null || list.Length < itemsCount) {
|
||||
list = new object[itemsCount];
|
||||
}
|
||||
for (int i = 0, j = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2, j++) {
|
||||
list[j] = Owner.ComponentPools[entityData.Components[i]].GetItem (entityData.Components[i + 1]);
|
||||
}
|
||||
return itemsCount;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator == (in EcsEntity lhs, in EcsEntity rhs) {
|
||||
return lhs.Id == rhs.Id && lhs.Gen == rhs.Gen;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator != (in EcsEntity lhs, in EcsEntity rhs) {
|
||||
return lhs.Id != rhs.Id || lhs.Gen != rhs.Gen;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode () {
|
||||
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||
// not readonly for performance reason - no ctor calls for EcsEntity struct.
|
||||
return Id.GetHashCode () ^ (Gen.GetHashCode () << 2);
|
||||
// ReSharper restore NonReadonlyMemberInGetHashCode
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals (object other) {
|
||||
if (!(other is EcsEntity)) {
|
||||
return false;
|
||||
}
|
||||
var rhs = (EcsEntity) other;
|
||||
return Id == rhs.Id && Gen == rhs.Gen;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString () {
|
||||
return IsNull () ? "Entity-Null" : $"Entity-{Id}:{Gen}";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsEntity.cs.meta
Normal file
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsEntity.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2813e6db660119c42933cad837cdc77a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
696
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsFilter.cs
Normal file
696
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsFilter.cs
Normal file
@@ -0,0 +1,696 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
/// <summary>
|
||||
/// Common interface for all filter listeners.
|
||||
/// </summary>
|
||||
public interface IEcsFilterListener {
|
||||
void OnEntityAdded (in EcsEntity entity);
|
||||
void OnEntityRemoved (in EcsEntity entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Container for filtered entities based on specified constraints.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public abstract class EcsFilter {
|
||||
public EcsEntity[] Entities = new EcsEntity[EcsHelpers.FilterEntitiesSize];
|
||||
protected int EntitiesCount;
|
||||
|
||||
int _lockCount;
|
||||
|
||||
DelayedOp[] _delayedOps = new DelayedOp[EcsHelpers.FilterEntitiesSize];
|
||||
int _delayedOpsCount;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
protected IEcsFilterListener[] Listeners = new IEcsFilterListener[4];
|
||||
protected int ListenersCount;
|
||||
// ReSharper restore MemberCanBePrivate.Global
|
||||
|
||||
protected internal int[] IncludedTypeIndices;
|
||||
protected internal int[] ExcludedTypeIndices;
|
||||
|
||||
public Type[] IncludedTypes;
|
||||
public Type[] ExcludedTypes;
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator () {
|
||||
return new Enumerator (this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets entities count.
|
||||
/// </summary>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public int GetEntitiesCount () {
|
||||
return EntitiesCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is filter not contains entities.
|
||||
/// </summary>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsEmpty () {
|
||||
return EntitiesCount == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes listener to filter events.
|
||||
/// </summary>
|
||||
/// <param name="listener">Listener.</param>
|
||||
public void AddListener (IEcsFilterListener listener) {
|
||||
#if DEBUG
|
||||
for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
|
||||
if (Listeners[i] == listener) {
|
||||
throw new Exception ("Listener already subscribed.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (Listeners.Length == ListenersCount) {
|
||||
Array.Resize (ref Listeners, ListenersCount << 1);
|
||||
}
|
||||
Listeners[ListenersCount++] = listener;
|
||||
}
|
||||
|
||||
// ReSharper disable once CommentTypo
|
||||
/// <summary>
|
||||
/// Unsubscribes listener from filter events.
|
||||
/// </summary>
|
||||
/// <param name="listener">Listener.</param>
|
||||
public void RemoveListener (IEcsFilterListener listener) {
|
||||
for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
|
||||
if (Listeners[i] == listener) {
|
||||
ListenersCount--;
|
||||
// cant fill gap with last element due listeners order is important.
|
||||
Array.Copy (Listeners, i + 1, Listeners, i, ListenersCount - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is filter compatible with components on entity with optional added / removed component.
|
||||
/// </summary>
|
||||
/// <param name="entityData">Entity data.</param>
|
||||
/// <param name="addedRemovedTypeIndex">Optional added (greater 0) or removed (less 0) component. Will be ignored if zero.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
internal bool IsCompatible (in EcsWorld.EcsEntityData entityData, int addedRemovedTypeIndex) {
|
||||
var incIdx = IncludedTypeIndices.Length - 1;
|
||||
for (; incIdx >= 0; incIdx--) {
|
||||
var typeIdx = IncludedTypeIndices[incIdx];
|
||||
var idx = entityData.ComponentsCountX2 - 2;
|
||||
for (; idx >= 0; idx -= 2) {
|
||||
var typeIdx2 = entityData.Components[idx];
|
||||
if (typeIdx2 == -addedRemovedTypeIndex) {
|
||||
continue;
|
||||
}
|
||||
if (typeIdx2 == addedRemovedTypeIndex || typeIdx2 == typeIdx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// not found.
|
||||
if (idx == -2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// one of required component not found.
|
||||
if (incIdx != -1) {
|
||||
return false;
|
||||
}
|
||||
// check for excluded components.
|
||||
if (ExcludedTypeIndices != null) {
|
||||
for (var excIdx = 0; excIdx < ExcludedTypeIndices.Length; excIdx++) {
|
||||
var typeIdx = ExcludedTypeIndices[excIdx];
|
||||
for (var idx = entityData.ComponentsCountX2 - 2; idx >= 0; idx -= 2) {
|
||||
var typeIdx2 = entityData.Components[idx];
|
||||
if (typeIdx2 == -addedRemovedTypeIndex) {
|
||||
continue;
|
||||
}
|
||||
if (typeIdx2 == addedRemovedTypeIndex || typeIdx2 == typeIdx) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
protected bool AddDelayedOp (bool isAdd, in EcsEntity entity) {
|
||||
if (_lockCount <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (_delayedOps.Length == _delayedOpsCount) {
|
||||
Array.Resize (ref _delayedOps, _delayedOpsCount << 1);
|
||||
}
|
||||
ref var op = ref _delayedOps[_delayedOpsCount++];
|
||||
op.IsAdd = isAdd;
|
||||
op.Entity = entity;
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
protected void ProcessListeners (bool isAdd, in EcsEntity entity) {
|
||||
if (isAdd) {
|
||||
for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
|
||||
Listeners[i].OnEntityAdded (entity);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
|
||||
Listeners[i].OnEntityRemoved (entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
void Lock () {
|
||||
_lockCount++;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
void Unlock () {
|
||||
#if DEBUG
|
||||
if (_lockCount <= 0) {
|
||||
throw new Exception ($"Invalid lock-unlock balance for \"{GetType ().Name}\".");
|
||||
}
|
||||
#endif
|
||||
_lockCount--;
|
||||
if (_lockCount == 0 && _delayedOpsCount > 0) {
|
||||
// process delayed operations.
|
||||
for (int i = 0, iMax = _delayedOpsCount; i < iMax; i++) {
|
||||
ref var op = ref _delayedOps[i];
|
||||
if (op.IsAdd) {
|
||||
OnAddEntity (op.Entity);
|
||||
} else {
|
||||
OnRemoveEntity (op.Entity);
|
||||
}
|
||||
}
|
||||
_delayedOpsCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// For debug purposes. Check filters equality by included / excluded components.
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter to compare.</param>
|
||||
internal bool AreComponentsSame (EcsFilter filter) {
|
||||
if (IncludedTypeIndices.Length != filter.IncludedTypeIndices.Length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < IncludedTypeIndices.Length; i++) {
|
||||
if (Array.IndexOf (filter.IncludedTypeIndices, IncludedTypeIndices[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((ExcludedTypeIndices == null && filter.ExcludedTypeIndices != null) ||
|
||||
(ExcludedTypeIndices != null && filter.ExcludedTypeIndices == null)) {
|
||||
return false;
|
||||
}
|
||||
if (ExcludedTypeIndices != null) {
|
||||
if (filter.ExcludedTypeIndices == null || ExcludedTypeIndices.Length != filter.ExcludedTypeIndices.Length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < ExcludedTypeIndices.Length; i++) {
|
||||
if (Array.IndexOf (filter.ExcludedTypeIndices, ExcludedTypeIndices[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Event for adding compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
public abstract void OnAddEntity (in EcsEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Event for removing non-compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
public abstract void OnRemoveEntity (in EcsEntity entity);
|
||||
|
||||
public struct Enumerator : IDisposable {
|
||||
readonly EcsFilter _filter;
|
||||
readonly int _count;
|
||||
int _idx;
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
internal Enumerator (EcsFilter filter) {
|
||||
_filter = filter;
|
||||
_count = _filter.GetEntitiesCount ();
|
||||
_idx = -1;
|
||||
_filter.Lock ();
|
||||
}
|
||||
|
||||
public int Current {
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
get => _idx;
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose () {
|
||||
_filter.Unlock ();
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext () {
|
||||
return ++_idx < _count;
|
||||
}
|
||||
}
|
||||
|
||||
struct DelayedOp {
|
||||
public bool IsAdd;
|
||||
public EcsEntity Entity;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public class EcsFilter<Inc1> : EcsFilter where Inc1 : class {
|
||||
public Inc1[] Get1;
|
||||
readonly bool _allow1;
|
||||
|
||||
protected EcsFilter () {
|
||||
_allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
|
||||
Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
|
||||
IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex };
|
||||
IncludedTypes = new[] { EcsComponentType<Inc1>.Type };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for adding compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnAddEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (true, entity)) { return; }
|
||||
if (Entities.Length == EntitiesCount) {
|
||||
Array.Resize (ref Entities, EntitiesCount << 1);
|
||||
if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
|
||||
}
|
||||
// inlined and optimized World.GetComponent() call.
|
||||
ref var entityData = ref entity.Owner.GetEntityData (entity);
|
||||
var allow1 = _allow1;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
var typeIdx = entityData.Components[i];
|
||||
var itemIdx = entityData.Components[i + 1];
|
||||
if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
|
||||
Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow1 = false;
|
||||
}
|
||||
}
|
||||
Entities[EntitiesCount++] = entity;
|
||||
ProcessListeners (true, entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for removing non-compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnRemoveEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (false, entity)) { return; }
|
||||
for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
|
||||
if (Entities[i] == entity) {
|
||||
EntitiesCount--;
|
||||
if (i < EntitiesCount) {
|
||||
Entities[i] = Entities[EntitiesCount];
|
||||
if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
|
||||
}
|
||||
ProcessListeners (false, entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1> : EcsFilter<Inc1> where Exc1 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1, Exc2> : EcsFilter<Inc1> where Exc1 : class where Exc2 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public class EcsFilter<Inc1, Inc2> : EcsFilter where Inc1 : class where Inc2 : class {
|
||||
public Inc1[] Get1;
|
||||
public Inc2[] Get2;
|
||||
readonly bool _allow1;
|
||||
readonly bool _allow2;
|
||||
|
||||
protected EcsFilter () {
|
||||
_allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
|
||||
_allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
|
||||
Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
|
||||
IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex, EcsComponentType<Inc2>.TypeIndex };
|
||||
IncludedTypes = new[] { EcsComponentType<Inc1>.Type, EcsComponentType<Inc2>.Type };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for adding compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnAddEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (true, entity)) { return; }
|
||||
if (Entities.Length == EntitiesCount) {
|
||||
Array.Resize (ref Entities, EntitiesCount << 1);
|
||||
if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
|
||||
if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
|
||||
}
|
||||
// inlined and optimized World.GetComponent() call.
|
||||
ref var entityData = ref entity.Owner.GetEntityData (entity);
|
||||
var allow1 = _allow1;
|
||||
var allow2 = _allow2;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
var typeIdx = entityData.Components[i];
|
||||
var itemIdx = entityData.Components[i + 1];
|
||||
if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
|
||||
Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow1 = false;
|
||||
}
|
||||
if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
|
||||
Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow2 = false;
|
||||
}
|
||||
}
|
||||
Entities[EntitiesCount++] = entity;
|
||||
ProcessListeners (true, entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for removing non-compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnRemoveEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (false, entity)) { return; }
|
||||
for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
|
||||
if (Entities[i] == entity) {
|
||||
EntitiesCount--;
|
||||
if (i < EntitiesCount) {
|
||||
Entities[i] = Entities[EntitiesCount];
|
||||
if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
|
||||
if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
|
||||
}
|
||||
ProcessListeners (false, entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1> : EcsFilter<Inc1, Inc2> where Exc1 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2> where Exc1 : class where Exc2 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public class EcsFilter<Inc1, Inc2, Inc3> : EcsFilter where Inc1 : class where Inc2 : class where Inc3 : class {
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
public Inc1[] Get1;
|
||||
public Inc2[] Get2;
|
||||
public Inc3[] Get3;
|
||||
// ReSharper restore MemberCanBePrivate.Global
|
||||
readonly bool _allow1;
|
||||
readonly bool _allow2;
|
||||
readonly bool _allow3;
|
||||
|
||||
protected EcsFilter () {
|
||||
_allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
|
||||
_allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
|
||||
_allow3 = !EcsComponentType<Inc3>.IsIgnoreInFilter;
|
||||
Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get3 = _allow3 ? new Inc3[EcsHelpers.FilterEntitiesSize] : null;
|
||||
IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex, EcsComponentType<Inc2>.TypeIndex, EcsComponentType<Inc3>.TypeIndex };
|
||||
IncludedTypes = new[] { EcsComponentType<Inc1>.Type, EcsComponentType<Inc2>.Type, EcsComponentType<Inc3>.Type };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for adding compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnAddEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (true, entity)) { return; }
|
||||
if (Entities.Length == EntitiesCount) {
|
||||
Array.Resize (ref Entities, EntitiesCount << 1);
|
||||
if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
|
||||
if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
|
||||
if (_allow3) { Array.Resize (ref Get3, EntitiesCount << 1); }
|
||||
}
|
||||
// inlined and optimized World.GetComponent() call.
|
||||
ref var entityData = ref entity.Owner.GetEntityData (entity);
|
||||
var allow1 = _allow1;
|
||||
var allow2 = _allow2;
|
||||
var allow3 = _allow3;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
var typeIdx = entityData.Components[i];
|
||||
var itemIdx = entityData.Components[i + 1];
|
||||
if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
|
||||
Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow1 = false;
|
||||
}
|
||||
if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
|
||||
Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow2 = false;
|
||||
}
|
||||
if (allow3 && typeIdx == EcsComponentType<Inc3>.TypeIndex) {
|
||||
Get3[EntitiesCount] = (Inc3) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow3 = false;
|
||||
}
|
||||
}
|
||||
Entities[EntitiesCount++] = entity;
|
||||
ProcessListeners (true, entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for removing non-compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnRemoveEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (false, entity)) { return; }
|
||||
for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
|
||||
if (Entities[i] == entity) {
|
||||
EntitiesCount--;
|
||||
if (i < EntitiesCount) {
|
||||
Entities[i] = Entities[EntitiesCount];
|
||||
if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
|
||||
if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
|
||||
if (_allow3) { Get3[i] = Get3[EntitiesCount]; }
|
||||
}
|
||||
ProcessListeners (false, entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1> : EcsFilter<Inc1, Inc2, Inc3> where Exc1 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2, Inc3> where Exc1 : class where Exc2 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public class EcsFilter<Inc1, Inc2, Inc3, Inc4> : EcsFilter where Inc1 : class where Inc2 : class where Inc3 : class where Inc4 : class {
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
public Inc1[] Get1;
|
||||
public Inc2[] Get2;
|
||||
public Inc3[] Get3;
|
||||
public Inc4[] Get4;
|
||||
// ReSharper restore MemberCanBePrivate.Global
|
||||
readonly bool _allow1;
|
||||
readonly bool _allow2;
|
||||
readonly bool _allow3;
|
||||
readonly bool _allow4;
|
||||
|
||||
protected EcsFilter () {
|
||||
_allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
|
||||
_allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
|
||||
_allow3 = !EcsComponentType<Inc3>.IsIgnoreInFilter;
|
||||
_allow4 = !EcsComponentType<Inc4>.IsIgnoreInFilter;
|
||||
Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get3 = _allow3 ? new Inc3[EcsHelpers.FilterEntitiesSize] : null;
|
||||
Get4 = _allow4 ? new Inc4[EcsHelpers.FilterEntitiesSize] : null;
|
||||
IncludedTypeIndices = new[] {
|
||||
EcsComponentType<Inc1>.TypeIndex,
|
||||
EcsComponentType<Inc2>.TypeIndex,
|
||||
EcsComponentType<Inc3>.TypeIndex,
|
||||
EcsComponentType<Inc4>.TypeIndex
|
||||
};
|
||||
IncludedTypes = new[] {
|
||||
EcsComponentType<Inc1>.Type,
|
||||
EcsComponentType<Inc2>.Type,
|
||||
EcsComponentType<Inc3>.Type,
|
||||
EcsComponentType<Inc4>.Type
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for adding compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnAddEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (true, entity)) { return; }
|
||||
if (Entities.Length == EntitiesCount) {
|
||||
Array.Resize (ref Entities, EntitiesCount << 1);
|
||||
if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
|
||||
if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
|
||||
if (_allow3) { Array.Resize (ref Get3, EntitiesCount << 1); }
|
||||
if (_allow4) { Array.Resize (ref Get4, EntitiesCount << 1); }
|
||||
}
|
||||
// inlined and optimized World.GetComponent() call.
|
||||
ref var entityData = ref entity.Owner.GetEntityData (entity);
|
||||
var allow1 = _allow1;
|
||||
var allow2 = _allow2;
|
||||
var allow3 = _allow3;
|
||||
var allow4 = _allow4;
|
||||
for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
|
||||
var typeIdx = entityData.Components[i];
|
||||
var itemIdx = entityData.Components[i + 1];
|
||||
if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
|
||||
Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow1 = false;
|
||||
}
|
||||
if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
|
||||
Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow2 = false;
|
||||
}
|
||||
if (allow3 && typeIdx == EcsComponentType<Inc3>.TypeIndex) {
|
||||
Get3[EntitiesCount] = (Inc3) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow3 = false;
|
||||
}
|
||||
if (allow4 && typeIdx == EcsComponentType<Inc4>.TypeIndex) {
|
||||
Get4[EntitiesCount] = (Inc4) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
|
||||
allow4 = false;
|
||||
}
|
||||
}
|
||||
Entities[EntitiesCount++] = entity;
|
||||
ProcessListeners (true, entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for removing non-compatible entity to filter.
|
||||
/// Warning: Don't call manually!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public override void OnRemoveEntity (in EcsEntity entity) {
|
||||
if (AddDelayedOp (false, entity)) { return; }
|
||||
for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
|
||||
if (Entities[i] == entity) {
|
||||
EntitiesCount--;
|
||||
if (i < EntitiesCount) {
|
||||
Entities[i] = Entities[EntitiesCount];
|
||||
if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
|
||||
if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
|
||||
if (_allow3) { Get3[i] = Get3[EntitiesCount]; }
|
||||
if (_allow4) { Get4[i] = Get4[EntitiesCount]; }
|
||||
}
|
||||
ProcessListeners (false, entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1> : EcsFilter<Inc1, Inc2, Inc3, Inc4> where Exc1 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
|
||||
}
|
||||
}
|
||||
|
||||
public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2, Inc3, Inc4> where Exc1 : class where Exc2 : class {
|
||||
protected Exclude () {
|
||||
ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
|
||||
ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsFilter.cs.meta
Normal file
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsFilter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b90027f88e50ab84091856e4c71b9710
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
68
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsHelpers.cs
Normal file
68
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsHelpers.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2019 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
static class EcsHelpers {
|
||||
const int EntityComponentsCount = 8;
|
||||
public const int FilterEntitiesSize = 256;
|
||||
public const int EntityComponentsCountX2 = EntityComponentsCount * 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast List replacement for growing only collections.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of item.</typeparam>
|
||||
public class EcsGrowList<T> {
|
||||
public T[] Items;
|
||||
public int Count;
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGrowList (int capacity) {
|
||||
Items = new T[capacity];
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void Add (T item) {
|
||||
if (Items.Length == Count) {
|
||||
Array.Resize (ref Items, Items.Length << 1);
|
||||
}
|
||||
Items[Count++] = item;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public void EnsureCapacity (int count) {
|
||||
if (Items.Length < count) {
|
||||
var len = Items.Length << 1;
|
||||
while (len <= count) {
|
||||
len <<= 1;
|
||||
}
|
||||
Array.Resize (ref Items, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
// Unity IL2CPP performance optimization attribute.
|
||||
namespace Unity.IL2CPP.CompilerServices {
|
||||
enum Option {
|
||||
NullChecks = 1,
|
||||
ArrayBoundsChecks = 2
|
||||
}
|
||||
|
||||
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
|
||||
class Il2CppSetOptionAttribute : Attribute {
|
||||
public Option Option { get; private set; }
|
||||
public object Value { get; private set; }
|
||||
|
||||
public Il2CppSetOptionAttribute (Option option, object value) { Option = option; Value = value; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsHelpers.cs.meta
Normal file
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsHelpers.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93f7c1225241e624693cdb11c7f16e19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
373
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsSystem.cs
Normal file
373
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsSystem.cs
Normal file
@@ -0,0 +1,373 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
/// <summary>
|
||||
/// Base interface for all systems.
|
||||
/// </summary>
|
||||
public interface IEcsSystem { }
|
||||
|
||||
/// <summary>
|
||||
/// Interface for PreInit systems. PreInit() will be called before Init().
|
||||
/// </summary>
|
||||
public interface IEcsPreInitSystem : IEcsSystem {
|
||||
void PreInit ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for Init systems. Init() will be called before Run().
|
||||
/// </summary>
|
||||
public interface IEcsInitSystem : IEcsSystem {
|
||||
void Init ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for AfterDestroy systems. AfterDestroy() will be called after Destroy().
|
||||
/// </summary>
|
||||
public interface IEcsAfterDestroySystem : IEcsSystem {
|
||||
void AfterDestroy ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for Destroy systems. Destroy() will be called last in system lifetime cycle.
|
||||
/// </summary>
|
||||
public interface IEcsDestroySystem : IEcsSystem {
|
||||
void Destroy ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for Run systems.
|
||||
/// </summary>
|
||||
public interface IEcsRunSystem : IEcsSystem {
|
||||
void Run ();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Debug interface for systems events processing.
|
||||
/// </summary>
|
||||
public interface IEcsSystemsDebugListener {
|
||||
void OnSystemsDestroyed ();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Logical group of systems.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
public sealed class EcsSystems : IEcsInitSystem, IEcsDestroySystem, IEcsRunSystem {
|
||||
public readonly string Name;
|
||||
public readonly EcsWorld World;
|
||||
readonly EcsGrowList<IEcsSystem> _allSystems = new EcsGrowList<IEcsSystem> (64);
|
||||
readonly EcsGrowList<EcsSystemsRunItem> _runSystems = new EcsGrowList<EcsSystemsRunItem> (64);
|
||||
readonly Dictionary<int, int> _namedRunSystems = new Dictionary<int, int> (64);
|
||||
readonly Dictionary<Type, object> _injections = new Dictionary<Type, object> (32);
|
||||
bool _injected;
|
||||
#if DEBUG
|
||||
bool _inited;
|
||||
bool _destroyed;
|
||||
readonly List<IEcsSystemsDebugListener> _debugListeners = new List<IEcsSystemsDebugListener> (4);
|
||||
|
||||
/// <summary>
|
||||
/// Adds external event listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">Event listener.</param>
|
||||
public void AddDebugListener (IEcsSystemsDebugListener listener) {
|
||||
if (listener == null) { throw new Exception ("listener is null"); }
|
||||
_debugListeners.Add (listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes external event listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">Event listener.</param>
|
||||
public void RemoveDebugListener (IEcsSystemsDebugListener listener) {
|
||||
if (listener == null) { throw new Exception ("listener is null"); }
|
||||
_debugListeners.Remove (listener);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance of EcsSystems group.
|
||||
/// </summary>
|
||||
/// <param name="world">EcsWorld instance.</param>
|
||||
/// <param name="name">Custom name for this group.</param>
|
||||
public EcsSystems (EcsWorld world, string name = null) {
|
||||
World = world;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds new system to processing.
|
||||
/// </summary>
|
||||
/// <param name="system">System instance.</param>
|
||||
/// <param name="namedRunSystem">Optional name of system.</param>
|
||||
public EcsSystems Add (IEcsSystem system, string namedRunSystem = null) {
|
||||
#if DEBUG
|
||||
if (system == null) { throw new Exception ("System is null."); }
|
||||
if (_inited) { throw new Exception ("Cant add system after initialization."); }
|
||||
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
|
||||
if (!string.IsNullOrEmpty (namedRunSystem) && !(system is IEcsRunSystem)) { throw new Exception ("Cant name non-IEcsRunSystem."); }
|
||||
#endif
|
||||
_allSystems.Add (system);
|
||||
if (system is IEcsRunSystem) {
|
||||
if (namedRunSystem != null) {
|
||||
_namedRunSystems[namedRunSystem.GetHashCode ()] = _runSystems.Count;
|
||||
}
|
||||
_runSystems.Add (new EcsSystemsRunItem () { Active = true, System = (IEcsRunSystem) system });
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetNamedRunSystem (string name) {
|
||||
return _namedRunSystems.TryGetValue (name.GetHashCode (), out var idx) ? idx : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets IEcsRunSystem active state.
|
||||
/// </summary>
|
||||
/// <param name="idx">Index of system.</param>
|
||||
/// <param name="state">New state of system.</param>
|
||||
public void SetRunSystemState (int idx, bool state) {
|
||||
#if DEBUG
|
||||
if (idx < 0 || idx >= _runSystems.Count) { throw new Exception ("Invalid index"); }
|
||||
#endif
|
||||
_runSystems.Items[idx].Active = state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets IEcsRunSystem active state.
|
||||
/// </summary>
|
||||
/// <param name="idx">Index of system.</param>
|
||||
public bool GetRunSystemState (int idx) {
|
||||
#if DEBUG
|
||||
if (idx < 0 || idx >= _runSystems.Count) { throw new Exception ("Invalid index"); }
|
||||
#endif
|
||||
return _runSystems.Items[idx].Active;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all systems. Important: Don't change collection!
|
||||
/// </summary>
|
||||
public EcsGrowList<IEcsSystem> GetAllSystems () {
|
||||
return _allSystems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all run systems. Important: Don't change collection!
|
||||
/// </summary>
|
||||
public EcsGrowList<EcsSystemsRunItem> GetRunSystems () {
|
||||
return _runSystems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects instance of object type to all compatible fields of added systems.
|
||||
/// </summary>
|
||||
/// <param name="obj">Instance.</param>
|
||||
public EcsSystems Inject<T> (T obj) {
|
||||
#if DEBUG
|
||||
if (_inited) { throw new Exception ("Cant inject after initialization."); }
|
||||
#endif
|
||||
_injections[typeof (T)] = obj;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes injections immediately.
|
||||
/// Can be used to DI before Init() call.
|
||||
/// </summary>
|
||||
public EcsSystems ProcessInjects () {
|
||||
#if DEBUG
|
||||
if (_inited) { throw new Exception ("Cant inject after initialization."); }
|
||||
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
|
||||
#endif
|
||||
if (!_injected) {
|
||||
_injected = true;
|
||||
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
|
||||
var nestedSystems = _allSystems.Items[i] as EcsSystems;
|
||||
if (nestedSystems != null) {
|
||||
foreach (var pair in _injections) {
|
||||
nestedSystems._injections[pair.Key] = pair.Value;
|
||||
}
|
||||
nestedSystems.ProcessInjects ();
|
||||
} else {
|
||||
InjectDataToSystem (_allSystems.Items[i], World, _injections);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers component type as one-frame for auto-removing at end of Run() call.
|
||||
/// </summary>
|
||||
public EcsSystems OneFrame<T> () where T : class {
|
||||
Add (new RemoveOneFrame<T> ());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes registration for new systems, initialize all registered.
|
||||
/// </summary>
|
||||
public void Init () {
|
||||
#if DEBUG
|
||||
if (_inited) { throw new Exception ("Already inited."); }
|
||||
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
|
||||
#endif
|
||||
ProcessInjects ();
|
||||
// IEcsPreInitSystem processing.
|
||||
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
|
||||
var system = _allSystems.Items[i];
|
||||
if (system is IEcsPreInitSystem) {
|
||||
((IEcsPreInitSystem) system).PreInit ();
|
||||
#if DEBUG
|
||||
World.CheckForLeakedEntities ($"{system.GetType ().Name}.PreInit()");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// IEcsInitSystem processing.
|
||||
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
|
||||
var system = _allSystems.Items[i];
|
||||
if (system is IEcsInitSystem) {
|
||||
((IEcsInitSystem) system).Init ();
|
||||
#if DEBUG
|
||||
World.CheckForLeakedEntities ($"{system.GetType ().Name}.Init()");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
_inited = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes all IEcsRunSystem systems.
|
||||
/// </summary>
|
||||
public void Run () {
|
||||
#if DEBUG
|
||||
if (!_inited) { throw new Exception ($"[{Name ?? "NONAME"}] EcsSystems should be initialized before."); }
|
||||
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
|
||||
#endif
|
||||
for (int i = 0, iMax = _runSystems.Count; i < iMax; i++) {
|
||||
var runItem = _runSystems.Items[i];
|
||||
if (runItem.Active) {
|
||||
runItem.System.Run ();
|
||||
}
|
||||
#if DEBUG
|
||||
if (World.CheckForLeakedEntities (null)) {
|
||||
throw new Exception ($"Empty entity detected, possible memory leak in {_runSystems.Items[i].GetType ().Name}.Run ()");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys registered data.
|
||||
/// </summary>
|
||||
public void Destroy () {
|
||||
#if DEBUG
|
||||
if (_destroyed) { throw new Exception ("Already destroyed."); }
|
||||
_destroyed = true;
|
||||
#endif
|
||||
// IEcsDestroySystem processing.
|
||||
for (var i = _allSystems.Count - 1; i >= 0; i--) {
|
||||
var system = _allSystems.Items[i];
|
||||
if (system is IEcsDestroySystem) {
|
||||
((IEcsDestroySystem) system).Destroy ();
|
||||
#if DEBUG
|
||||
World.CheckForLeakedEntities ($"{system.GetType ().Name}.Destroy ()");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// IEcsAfterDestroySystem processing.
|
||||
for (var i = _allSystems.Count - 1; i >= 0; i--) {
|
||||
var system = _allSystems.Items[i];
|
||||
if (system is IEcsAfterDestroySystem) {
|
||||
((IEcsAfterDestroySystem) system).AfterDestroy ();
|
||||
#if DEBUG
|
||||
World.CheckForLeakedEntities ($"{system.GetType ().Name}.AfterDestroy ()");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
for (int i = 0, iMax = _debugListeners.Count; i < iMax; i++) {
|
||||
_debugListeners[i].OnSystemsDestroyed ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects custom data to fields of ISystem instance.
|
||||
/// </summary>
|
||||
/// <param name="system">ISystem instance.</param>
|
||||
/// <param name="world">EcsWorld instance.</param>
|
||||
/// <param name="injections">Additional instances for injection.</param>
|
||||
public static void InjectDataToSystem (IEcsSystem system, EcsWorld world, Dictionary<Type, object> injections) {
|
||||
var systemType = system.GetType ();
|
||||
var worldType = world.GetType ();
|
||||
var filterType = typeof (EcsFilter);
|
||||
var ignoreType = typeof (EcsIgnoreInjectAttribute);
|
||||
|
||||
foreach (var f in systemType.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
|
||||
// skip statics or fields with [EcsIgnoreInject] attribute.
|
||||
if (f.IsStatic || Attribute.IsDefined (f, ignoreType)) {
|
||||
continue;
|
||||
}
|
||||
// EcsWorld
|
||||
if (f.FieldType.IsAssignableFrom (worldType)) {
|
||||
f.SetValue (system, world);
|
||||
continue;
|
||||
}
|
||||
// EcsFilter
|
||||
#if DEBUG
|
||||
if (f.FieldType == filterType) {
|
||||
throw new Exception ($"Cant use EcsFilter type at \"{system}\" system for dependency injection, use generic version instead");
|
||||
}
|
||||
#endif
|
||||
if (f.FieldType.IsSubclassOf (filterType)) {
|
||||
f.SetValue (system, world.GetFilter (f.FieldType));
|
||||
continue;
|
||||
}
|
||||
// Other injections.
|
||||
foreach (var pair in injections) {
|
||||
if (f.FieldType.IsAssignableFrom (pair.Key)) {
|
||||
f.SetValue (system, pair.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System for removing OneFrame component.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">OneFrame component type.</typeparam>
|
||||
sealed class RemoveOneFrame<T> : IEcsRunSystem where T : class {
|
||||
readonly EcsFilter<T> _oneFrames = null;
|
||||
|
||||
void IEcsRunSystem.Run () {
|
||||
foreach (var idx in _oneFrames) {
|
||||
_oneFrames.Entities[idx].Unset<T> ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IEcsRunSystem instance with active state.
|
||||
/// </summary>
|
||||
public sealed class EcsSystemsRunItem {
|
||||
public bool Active;
|
||||
public IEcsRunSystem System;
|
||||
}
|
||||
}
|
||||
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsSystem.cs.meta
Normal file
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsSystem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 622f9ce2b229d5849b5a8f60a1486ddd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
463
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsWorld.cs
Normal file
463
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsWorld.cs
Normal file
@@ -0,0 +1,463 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// The MIT License
|
||||
// Simple Entity Component System framework https://github.com/Leopotam/ecs
|
||||
// Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Leopotam.Ecs {
|
||||
/// <summary>
|
||||
/// Ecs data context.
|
||||
/// </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
|
||||
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
||||
public class EcsWorld {
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
protected EcsEntityData[] Entities = new EcsEntityData[1024];
|
||||
protected int EntitiesCount;
|
||||
protected readonly EcsGrowList<int> FreeEntities = new EcsGrowList<int> (1024);
|
||||
protected readonly EcsGrowList<EcsFilter> Filters = new EcsGrowList<EcsFilter> (128);
|
||||
protected readonly Dictionary<int, EcsGrowList<EcsFilter>> FilterByIncludedComponents = new Dictionary<int, EcsGrowList<EcsFilter>> (64);
|
||||
protected readonly Dictionary<int, EcsGrowList<EcsFilter>> FilterByExcludedComponents = new Dictionary<int, EcsGrowList<EcsFilter>> (64);
|
||||
int _usedComponentsCount;
|
||||
|
||||
/// <summary>
|
||||
/// Component pools cache.
|
||||
/// </summary>
|
||||
// ReSharper restore MemberCanBePrivate.Global
|
||||
public EcsComponentPool[] ComponentPools = new EcsComponentPool[512];
|
||||
#if DEBUG
|
||||
internal readonly List<IEcsWorldDebugListener> DebugListeners = new List<IEcsWorldDebugListener> (4);
|
||||
readonly EcsGrowList<EcsEntity> _leakedEntities = new EcsGrowList<EcsEntity> (256);
|
||||
bool _isDestroyed;
|
||||
bool _inDestroying;
|
||||
|
||||
/// <summary>
|
||||
/// Adds external event listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">Event listener.</param>
|
||||
public void AddDebugListener (IEcsWorldDebugListener listener) {
|
||||
if (listener == null) { throw new Exception ("Listener is null."); }
|
||||
DebugListeners.Add (listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes external event listener.
|
||||
/// </summary>
|
||||
/// <param name="listener">Event listener.</param>
|
||||
public void RemoveDebugListener (IEcsWorldDebugListener listener) {
|
||||
if (listener == null) { throw new Exception ("Listener is null."); }
|
||||
DebugListeners.Remove (listener);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Destroys world and exist entities.
|
||||
/// </summary>
|
||||
public virtual void Destroy () {
|
||||
#if DEBUG
|
||||
if (_isDestroyed || _inDestroying) { throw new Exception ("EcsWorld already destroyed."); }
|
||||
_inDestroying = true;
|
||||
CheckForLeakedEntities ("Destroy");
|
||||
#endif
|
||||
EcsEntity entity;
|
||||
entity.Owner = this;
|
||||
for (var i = EntitiesCount - 1; i >= 0; i--) {
|
||||
ref var entityData = ref Entities[i];
|
||||
if (entityData.ComponentsCountX2 > 0) {
|
||||
entity.Id = i;
|
||||
entity.Gen = entityData.Gen;
|
||||
entity.Destroy ();
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
_isDestroyed = true;
|
||||
for (var i = DebugListeners.Count - 1; i >= 0; i--) {
|
||||
DebugListeners[i].OnWorldDestroyed ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new entity.
|
||||
/// </summary>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity NewEntity () {
|
||||
#if DEBUG
|
||||
if (_isDestroyed) { throw new Exception ("EcsWorld already destroyed."); }
|
||||
#endif
|
||||
EcsEntity entity;
|
||||
entity.Owner = this;
|
||||
// try to reuse entity from pool.
|
||||
if (FreeEntities.Count > 0) {
|
||||
entity.Id = FreeEntities.Items[--FreeEntities.Count];
|
||||
ref var entityData = ref Entities[entity.Id];
|
||||
entity.Gen = entityData.Gen;
|
||||
entityData.ComponentsCountX2 = 0;
|
||||
} else {
|
||||
// create new entity.
|
||||
if (EntitiesCount == Entities.Length) {
|
||||
Array.Resize (ref Entities, EntitiesCount << 1);
|
||||
}
|
||||
entity.Id = EntitiesCount++;
|
||||
ref var entityData = ref Entities[entity.Id];
|
||||
entityData.Components = new int[EcsHelpers.EntityComponentsCountX2];
|
||||
entityData.Gen = 1;
|
||||
entity.Gen = entityData.Gen;
|
||||
entityData.ComponentsCountX2 = 0;
|
||||
}
|
||||
#if DEBUG
|
||||
_leakedEntities.Add (entity);
|
||||
for (var ii = 0; ii < DebugListeners.Count; ii++) {
|
||||
DebugListeners[ii].OnEntityCreated (entity);
|
||||
}
|
||||
#endif
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates entity and attaches component.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of component1.</typeparam>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity NewEntityWith<T1> (out T1 c1) where T1 : class {
|
||||
var entity = NewEntity ();
|
||||
c1 = entity.Set<T1> ();
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates entity and attaches components.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of component1.</typeparam>
|
||||
/// <typeparam name="T2">Type of component2.</typeparam>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity NewEntityWith<T1, T2> (out T1 c1, out T2 c2) where T1 : class where T2 : class {
|
||||
var entity = NewEntity ();
|
||||
c1 = entity.Set<T1> ();
|
||||
c2 = entity.Set<T2> ();
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates entity and attaches components.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of component1.</typeparam>
|
||||
/// <typeparam name="T2">Type of component2.</typeparam>
|
||||
/// <typeparam name="T3">Type of component3.</typeparam>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity NewEntityWith<T1, T2, T3> (out T1 c1, out T2 c2, out T3 c3) where T1 : class where T2 : class where T3 : class {
|
||||
var entity = NewEntity ();
|
||||
c1 = entity.Set<T1> ();
|
||||
c2 = entity.Set<T2> ();
|
||||
c3 = entity.Set<T3> ();
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates entity and attaches components.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of component1.</typeparam>
|
||||
/// <typeparam name="T2">Type of component2.</typeparam>
|
||||
/// <typeparam name="T3">Type of component3.</typeparam>
|
||||
/// <typeparam name="T4">Type of component4.</typeparam>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity NewEntityWith<T1, T2, T3, T4> (out T1 c1, out T2 c2, out T3 c3, out T4 c4) where T1 : class where T2 : class where T3 : class where T4 : class {
|
||||
var entity = NewEntity ();
|
||||
c1 = entity.Set<T1> ();
|
||||
c2 = entity.Set<T2> ();
|
||||
c3 = entity.Set<T3> ();
|
||||
c4 = entity.Set<T4> ();
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores EcsEntity from internal id. For internal use only!
|
||||
/// </summary>
|
||||
/// <param name="id">Internal id.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity RestoreEntityFromInternalId (int id) {
|
||||
EcsEntity entity;
|
||||
entity.Owner = this;
|
||||
entity.Id = id;
|
||||
entity.Gen = 0;
|
||||
ref var entityData = ref GetEntityData (entity);
|
||||
entity.Gen = entityData.Gen;
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request exist filter or create new one. For internal use only!
|
||||
/// </summary>
|
||||
/// <param name="filterType">Filter type.</param>
|
||||
public EcsFilter GetFilter (Type filterType) {
|
||||
#if DEBUG
|
||||
if (filterType == null) { throw new Exception ("FilterType is null."); }
|
||||
if (!filterType.IsSubclassOf (typeof (EcsFilter))) { throw new Exception ($"Invalid filter type: {filterType}."); }
|
||||
if (_isDestroyed) { throw new Exception ("EcsWorld already destroyed."); }
|
||||
#endif
|
||||
// check already exist filters.
|
||||
for (int i = 0, iMax = Filters.Count; i < iMax; i++) {
|
||||
if (Filters.Items[i].GetType () == filterType) {
|
||||
return Filters.Items[i];
|
||||
}
|
||||
}
|
||||
// create new filter.
|
||||
var filter = (EcsFilter) Activator.CreateInstance (filterType, true);
|
||||
#if DEBUG
|
||||
for (var filterIdx = 0; filterIdx < Filters.Count; filterIdx++) {
|
||||
if (filter.AreComponentsSame (Filters.Items[filterIdx])) {
|
||||
throw new Exception (
|
||||
$"Invalid filter \"{filter.GetType ()}\": Another filter \"{Filters.Items[filterIdx].GetType ()}\" already has same components, but in different order.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Filters.Add (filter);
|
||||
// add to component dictionaries for fast compatibility scan.
|
||||
for (int i = 0, iMax = filter.IncludedTypeIndices.Length; i < iMax; i++) {
|
||||
if (!FilterByIncludedComponents.TryGetValue (filter.IncludedTypeIndices[i], out var filtersList)) {
|
||||
filtersList = new EcsGrowList<EcsFilter> (8);
|
||||
FilterByIncludedComponents[filter.IncludedTypeIndices[i]] = filtersList;
|
||||
}
|
||||
filtersList.Add (filter);
|
||||
}
|
||||
if (filter.ExcludedTypeIndices != null) {
|
||||
for (int i = 0, iMax = filter.ExcludedTypeIndices.Length; i < iMax; i++) {
|
||||
if (!FilterByExcludedComponents.TryGetValue (filter.ExcludedTypeIndices[i], out var filtersList)) {
|
||||
filtersList = new EcsGrowList<EcsFilter> (8);
|
||||
FilterByExcludedComponents[filter.ExcludedTypeIndices[i]] = filtersList;
|
||||
}
|
||||
filtersList.Add (filter);
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
for (var ii = 0; ii < DebugListeners.Count; ii++) {
|
||||
DebugListeners[ii].OnFilterCreated (filter);
|
||||
}
|
||||
#endif
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets stats of internal data.
|
||||
/// </summary>
|
||||
public EcsWorldStats GetStats () {
|
||||
var stats = new EcsWorldStats () {
|
||||
ActiveEntities = EntitiesCount - FreeEntities.Count,
|
||||
ReservedEntities = FreeEntities.Count,
|
||||
Filters = Filters.Count,
|
||||
Components = _usedComponentsCount
|
||||
};
|
||||
return stats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles internal entity data to pool.
|
||||
/// </summary>
|
||||
/// <param name="id">Entity id.</param>
|
||||
/// <param name="entityData">Entity internal data.</param>
|
||||
protected internal void RecycleEntityData (int id, ref EcsEntityData entityData) {
|
||||
#if DEBUG
|
||||
if (entityData.ComponentsCountX2 != 0) { throw new Exception ("Cant recycle invalid entity."); }
|
||||
#endif
|
||||
entityData.ComponentsCountX2 = -2;
|
||||
entityData.Gen = (ushort) ((entityData.Gen + 1) % ushort.MaxValue);
|
||||
FreeEntities.Add (id);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Checks exist entities but without components.
|
||||
/// </summary>
|
||||
/// <param name="errorMsg">Prefix for error message.</param>
|
||||
public bool CheckForLeakedEntities (string errorMsg) {
|
||||
if (_leakedEntities.Count > 0) {
|
||||
for (int i = 0, iMax = _leakedEntities.Count; i < iMax; i++) {
|
||||
if (GetEntityData (_leakedEntities.Items[i]).ComponentsCountX2 == 0) {
|
||||
if (errorMsg != null) {
|
||||
throw new Exception ($"{errorMsg}: Empty entity detected, possible memory leak.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_leakedEntities.Count = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Updates filters.
|
||||
/// </summary>
|
||||
/// <param name="typeIdx">Component type index.abstract Positive for add operation, negative for remove operation.</param>
|
||||
/// <param name="entity">Target entity.</param>
|
||||
/// <param name="entityData">Target entity data.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
protected internal void UpdateFilters (int typeIdx, in EcsEntity entity, in EcsEntityData entityData) {
|
||||
#if DEBUG
|
||||
if (_isDestroyed) { throw new Exception ("EcsWorld already destroyed."); }
|
||||
#endif
|
||||
EcsGrowList<EcsFilter> filters;
|
||||
if (typeIdx < 0) {
|
||||
// remove component.
|
||||
if (FilterByIncludedComponents.TryGetValue (-typeIdx, out filters)) {
|
||||
for (int i = 0, iMax = filters.Count; i < iMax; i++) {
|
||||
if (filters.Items[i].IsCompatible (entityData, 0)) {
|
||||
#if DEBUG
|
||||
var isValid = false;
|
||||
foreach (var idx in filters.Items[i]) {
|
||||
if (filters.Items[i].Entities[idx].Id == entity.Id) {
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid) { throw new Exception ($"{filters.Items[i]}Entity not in filter."); }
|
||||
#endif
|
||||
filters.Items[i].OnRemoveEntity (entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FilterByExcludedComponents.TryGetValue (-typeIdx, out filters)) {
|
||||
for (int i = 0, iMax = filters.Count; i < iMax; i++) {
|
||||
if (filters.Items[i].IsCompatible (entityData, typeIdx)) {
|
||||
#if DEBUG
|
||||
var isValid = true;
|
||||
foreach (var idx in filters.Items[i]) {
|
||||
if (filters.Items[i].Entities[idx].Id == entity.Id) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid) { throw new Exception ($"{filters.Items[i]}Entity already in filter."); }
|
||||
#endif
|
||||
filters.Items[i].OnAddEntity (entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// add component.
|
||||
if (FilterByIncludedComponents.TryGetValue (typeIdx, out filters)) {
|
||||
for (int i = 0, iMax = filters.Count; i < iMax; i++) {
|
||||
if (filters.Items[i].IsCompatible (entityData, 0)) {
|
||||
#if DEBUG
|
||||
var isValid = true;
|
||||
foreach (var idx in filters.Items[i]) {
|
||||
if (filters.Items[i].Entities[idx].Id == entity.Id) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid) { throw new Exception ($"{filters.Items[i]}Entity already in filter."); }
|
||||
#endif
|
||||
filters.Items[i].OnAddEntity (entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FilterByExcludedComponents.TryGetValue (typeIdx, out filters)) {
|
||||
for (int i = 0, iMax = filters.Count; i < iMax; i++) {
|
||||
if (filters.Items[i].IsCompatible (entityData, -typeIdx)) {
|
||||
#if DEBUG
|
||||
var isValid = false;
|
||||
foreach (var idx in filters.Items[i]) {
|
||||
if (filters.Items[i].Entities[idx].Id == entity.Id) {
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid) { throw new Exception ($"{filters.Items[i]}Entity not in filter."); }
|
||||
#endif
|
||||
filters.Items[i].OnRemoveEntity (entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns internal state of entity. For internal use!
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity.</param>
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public ref EcsEntityData GetEntityData (in EcsEntity entity) {
|
||||
#if DEBUG
|
||||
if (_isDestroyed) { throw new Exception ("EcsWorld already destroyed."); }
|
||||
if (entity.Id < 0 || entity.Id > EntitiesCount) { throw new Exception ($"Invalid entity {entity.Id}"); }
|
||||
#endif
|
||||
return ref Entities[entity.Id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal state of entity.
|
||||
/// </summary>
|
||||
[StructLayout (LayoutKind.Sequential, Pack = 2)]
|
||||
public struct EcsEntityData {
|
||||
public ushort Gen;
|
||||
public short ComponentsCountX2;
|
||||
public int[] Components;
|
||||
}
|
||||
|
||||
[MethodImpl (MethodImplOptions.AggressiveInlining)]
|
||||
public EcsComponentPool GetPool<T> () where T : class {
|
||||
var typeIdx = EcsComponentType<T>.TypeIndex;
|
||||
if (ComponentPools.Length < typeIdx) {
|
||||
var len = ComponentPools.Length << 1;
|
||||
while (len <= typeIdx) {
|
||||
len <<= 1;
|
||||
}
|
||||
Array.Resize (ref ComponentPools, len);
|
||||
}
|
||||
var pool = ComponentPools[typeIdx];
|
||||
if (pool == null) {
|
||||
pool = new EcsComponentPool (EcsComponentType<T>.Type, EcsComponentType<T>.IsAutoReset);
|
||||
ComponentPools[typeIdx] = pool;
|
||||
_usedComponentsCount++;
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stats of EcsWorld instance.
|
||||
/// </summary>
|
||||
public struct EcsWorldStats {
|
||||
/// <summary>
|
||||
/// Amount of active entities.
|
||||
/// </summary>
|
||||
public int ActiveEntities;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of cached (not in use) entities.
|
||||
/// </summary>
|
||||
public int ReservedEntities;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of registered filters.
|
||||
/// </summary>
|
||||
public int Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of registered component types.
|
||||
/// </summary>
|
||||
public int Components;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Debug interface for world events processing.
|
||||
/// </summary>
|
||||
public interface IEcsWorldDebugListener {
|
||||
void OnEntityCreated (EcsEntity entity);
|
||||
void OnEntityDestroyed (EcsEntity entity);
|
||||
void OnFilterCreated (EcsFilter filter);
|
||||
void OnComponentListChanged (EcsEntity entity);
|
||||
void OnWorldDestroyed ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsWorld.cs.meta
Normal file
11
Assets/Asset Cleaner/com.leopotam.ecs/src/EcsWorld.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d506ff5dff038d547959e91aad707a8f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user