Add main mesh fixes

This commit is contained in:
Toomas Tamm
2021-02-23 02:13:14 +02:00
parent c89f326c37
commit 989424e659
139 changed files with 8101 additions and 2033 deletions

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15b66a8957ba23c418ec0f6922d999e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2813e6db660119c42933cad837cdc77a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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 };
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b90027f88e50ab84091856e4c71b9710
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93f7c1225241e624693cdb11c7f16e19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 622f9ce2b229d5849b5a8f60a1486ddd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d506ff5dff038d547959e91aad707a8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: