mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 01:44:35 +08:00
global update
rework queries update remove entities add sanitize checks fixes etс
This commit is contained in:
parent
ec4f8325b6
commit
c0c089ec01
@ -2,28 +2,28 @@
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public interface IEcsPreInitSystem : IEcsSystem
|
||||
public interface IEcsPreInitProcess : IEcsSystem
|
||||
{
|
||||
public void PreInit(EcsPipeline pipeline);
|
||||
}
|
||||
public interface IEcsInitSystem : IEcsSystem
|
||||
public interface IEcsInitProcess : IEcsSystem
|
||||
{
|
||||
public void Init(EcsPipeline pipeline);
|
||||
}
|
||||
public interface IEcsRunSystem : IEcsSystem
|
||||
public interface IEcsRunProcess : IEcsSystem
|
||||
{
|
||||
public void Run(EcsPipeline pipeline);
|
||||
}
|
||||
public interface IEcsDestroySystem : IEcsSystem
|
||||
public interface IEcsDestroyProcess : IEcsSystem
|
||||
{
|
||||
public void Destroy(EcsPipeline pipeline);
|
||||
}
|
||||
public interface IEcsBaseSystem : IEcsInitSystem, IEcsRunSystem, IEcsDestroySystem { }
|
||||
public interface IEcsBaseSystem : IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess { }
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
public sealed class EcsPreInitRunner : EcsRunner<IEcsPreInitSystem>, IEcsPreInitSystem
|
||||
public sealed class EcsPreInitProcessRunner : EcsRunner<IEcsPreInitProcess>, IEcsPreInitProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
private EcsProfilerMarker[] _markers;
|
||||
@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
public sealed class EcsInitRunner : EcsRunner<IEcsInitSystem>, IEcsInitSystem
|
||||
public sealed class EcsInitProcessRunner : EcsRunner<IEcsInitProcess>, IEcsInitProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
private EcsProfilerMarker[] _markers;
|
||||
@ -83,7 +83,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
public sealed class EcsRunRunner : EcsRunner<IEcsRunSystem>, IEcsRunSystem
|
||||
public sealed class EcsRunProcessRunner : EcsRunner<IEcsRunProcess>, IEcsRunProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
private EcsProfilerMarker[] _markers;
|
||||
@ -114,7 +114,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
public sealed class EcsDestroyRunner : EcsRunner<IEcsDestroySystem>, IEcsDestroySystem
|
||||
public sealed class EcsDestroyProcessRunner : EcsRunner<IEcsDestroyProcess>, IEcsDestroyProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
private EcsProfilerMarker[] _markers;
|
||||
|
@ -1,12 +1,14 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public struct Parent : IEcsAttachComponent
|
||||
{
|
||||
public EcsEntity entity;
|
||||
public entlong entity;
|
||||
|
||||
public EcsEntity Target
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public entlong Target
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => entity;
|
||||
@ -14,4 +16,36 @@ namespace DCFApixels.DragonECS
|
||||
set => entity = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ParentUtility
|
||||
{
|
||||
// public static int GetRootOrSelf(this HierarchySubject s, int entityID) => s.parents.GetRootOrSelf(entityID);
|
||||
public static int GetRootOrSelf(this EcsAttachPool<Parent> parents, int entityID)
|
||||
{
|
||||
while (parents.Has(entityID) && parents.Read(entityID).entity.TryGetID(out int child))
|
||||
entityID = child;
|
||||
return entityID;
|
||||
}
|
||||
// public static bool IsRoot(this HierarchySubject s, int entityID) => s.parents.IsRoot(entityID);
|
||||
public static bool IsRoot(this EcsAttachPool<Parent> parents, int entityID)
|
||||
{
|
||||
return !(parents.Has(entityID) && parents.Read(entityID).entity.IsAlive);
|
||||
}
|
||||
|
||||
// public static bool TryGetRoot(this HierarchySubject s, int entityID, out int rootEntityID) => TryGetRoot(s.parents, entityID, out rootEntityID);
|
||||
public static bool TryGetRoot(this EcsAttachPool<Parent> parents, EcsSubject conditionSubject, int entityID, out int rootEntityID)
|
||||
{
|
||||
rootEntityID = entityID;
|
||||
while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && conditionSubject.IsMatches(child))
|
||||
rootEntityID = child;
|
||||
return rootEntityID != entityID;
|
||||
}
|
||||
public static bool TryGetRoot(this EcsAttachPool<Parent> parents, int entityID, out int rootEntityID)
|
||||
{
|
||||
rootEntityID = entityID;
|
||||
while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child))
|
||||
rootEntityID = child;
|
||||
return rootEntityID != entityID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public void Inject(T obj);
|
||||
}
|
||||
public interface IEcsPreInitInjectCallbacks : IEcsSystem
|
||||
public interface IEcsPreInitInjectProcess : IEcsSystem
|
||||
{
|
||||
public void OnPreInitInjectionBefore();
|
||||
public void OnPreInitInjectionAfter();
|
||||
@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS
|
||||
namespace Internal
|
||||
{
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
public sealed class PreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
|
||||
public sealed class EcsPreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
|
||||
{
|
||||
void IEcsPreInject.PreInject(object obj)
|
||||
{
|
||||
@ -61,7 +61,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
public sealed class InjectRunner<T> : EcsRunner<IEcsInject<T>>, IEcsInject<T>
|
||||
public sealed class EcsInjectRunner<T> : EcsRunner<IEcsInject<T>>, IEcsInject<T>
|
||||
{
|
||||
private IEcsPreInject _preInjectchache;
|
||||
void IEcsInject<T>.Inject(T obj)
|
||||
@ -75,7 +75,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
public sealed class InjectCallbacksRunner : EcsRunner<IEcsPreInitInjectCallbacks>, IEcsPreInitInjectCallbacks
|
||||
public sealed class EcsPreInitInjectProcessRunner : EcsRunner<IEcsPreInitInjectProcess>, IEcsPreInitInjectProcess
|
||||
{
|
||||
public void OnPreInitInjectionAfter()
|
||||
{
|
||||
@ -91,7 +91,7 @@ namespace DCFApixels.DragonECS
|
||||
public class InjectSystemBase { }
|
||||
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitSystem, IEcsInject<PreInitInjectController>, IEcsPreInitInjectCallbacks
|
||||
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
|
||||
{
|
||||
private T _injectedData;
|
||||
|
||||
@ -105,12 +105,14 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
public void PreInit(EcsPipeline pipeline)
|
||||
{
|
||||
if (_injectedData == null)
|
||||
return;
|
||||
|
||||
if (_injectController == null)
|
||||
{
|
||||
_injectController = new PreInitInjectController(pipeline);
|
||||
var injectMapRunner = pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
|
||||
pipeline.GetRunner<IEcsPreInitInjectCallbacks>().OnPreInitInjectionBefore();
|
||||
pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
|
||||
injectMapRunner.Inject(_injectController);
|
||||
}
|
||||
|
||||
@ -120,7 +122,7 @@ namespace DCFApixels.DragonECS
|
||||
if (_injectController.OnInject())
|
||||
{
|
||||
_injectController.Destroy();
|
||||
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectCallbacks>();
|
||||
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectProcess>();
|
||||
injectCallbacksRunner.OnPreInitInjectionAfter();
|
||||
EcsRunner.Destroy(injectCallbacksRunner);
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
|
||||
public sealed class EmptyQuery : EcsQueryBase
|
||||
{
|
||||
private long _whereVersion;
|
||||
|
||||
public override long WhereVersion => _whereVersion;
|
||||
|
||||
public EmptyQuery(Builder b) { }
|
||||
|
||||
public sealed override WhereResult Where()
|
||||
{
|
||||
groupFilter = source.Entities.GetGroupInternal();
|
||||
return new WhereResult(this, ++_whereVersion);
|
||||
}
|
||||
|
||||
protected sealed override void OnBuild(Builder b) { }
|
||||
}
|
||||
public static partial class EcsWorldExtensions
|
||||
{
|
||||
public static WhereResult WhereAll(this EcsWorld self) => self.Select<EmptyQuery>().Where();
|
||||
}
|
||||
|
||||
public sealed class HierarchyQuery : EcsJoinAttachQuery<Parent>
|
||||
{
|
||||
public HierarchyQuery(Builder b) { }
|
||||
}
|
||||
}
|
11
src/Builtin/Subjects.cs
Normal file
11
src/Builtin/Subjects.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class HierarchySubject : EcsSubject
|
||||
{
|
||||
public readonly EcsAttachPool<Parent> parents;
|
||||
public HierarchySubject(Builder b)
|
||||
{
|
||||
parents = b.Include<Parent>();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[DebugColor(DebugColor.Black)]
|
||||
[DebugHide, DebugColor(DebugColor.Black)]
|
||||
public class SystemsBlockMarkerSystem : IEcsSystem
|
||||
{
|
||||
public readonly string name;
|
||||
@ -13,7 +13,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Grey)]
|
||||
public class DeleteEmptyEntitesSsytem : IEcsRunSystem, IEcsPreInject
|
||||
public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject
|
||||
{
|
||||
private List<EcsWorld> _worlds = new List<EcsWorld>();
|
||||
public void PreInject(object obj)
|
||||
@ -30,42 +30,45 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
|
||||
[DebugHide, DebugColor(DebugColor.Grey)]
|
||||
public class DeleteOneFrameComponentSystem<TWorld, TComponent> : IEcsRunSystem, IEcsInject<TWorld>
|
||||
public class DeleteOneFrameComponentSystem<TWorld, TComponent> : IEcsRunProcess, IEcsInject<TWorld>
|
||||
where TWorld : EcsWorld<TWorld>
|
||||
where TComponent : struct, IEcsComponent
|
||||
{
|
||||
private TWorld _world;
|
||||
public void Inject(TWorld obj) => _world = obj;
|
||||
|
||||
private sealed class Query : EcsQuery
|
||||
private sealed class Subject : EcsSubject
|
||||
{
|
||||
public EcsPool<TComponent> pool;
|
||||
public Query(Builder b)
|
||||
public Subject(Builder b)
|
||||
{
|
||||
pool = b.Include<TComponent>();
|
||||
}
|
||||
}
|
||||
public void Run(EcsPipeline pipeline)
|
||||
{
|
||||
foreach (var e in _world.Where(out Query q))
|
||||
q.pool.Del(e);
|
||||
foreach (var e in _world.Where(out Subject s))
|
||||
s.pool.Del(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeleteOneFrameComponentSystemExt
|
||||
{
|
||||
private const string AUTO_DEL_LAYER = nameof(AUTO_DEL_LAYER);
|
||||
public static EcsPipeline.Builder AutoDel<TWorld, TComponent>(this EcsPipeline.Builder b)
|
||||
where TWorld : EcsWorld<TWorld>
|
||||
where TComponent : struct, IEcsComponent
|
||||
{
|
||||
b.Add(new DeleteOneFrameComponentSystem<TWorld, TComponent>());
|
||||
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER);
|
||||
b.AddUnique(new DeleteOneFrameComponentSystem<TWorld, TComponent>(), AUTO_DEL_LAYER);
|
||||
return b;
|
||||
}
|
||||
/// <summary> for EcsDefaultWorld </summary>
|
||||
public static EcsPipeline.Builder AutoDel<TComponent>(this EcsPipeline.Builder b)
|
||||
where TComponent : struct, IEcsComponent
|
||||
{
|
||||
b.Add(new DeleteOneFrameComponentSystem<EcsDefaultWorld, TComponent>());
|
||||
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER);
|
||||
b.AddUnique(new DeleteOneFrameComponentSystem<EcsDefaultWorld, TComponent>(), AUTO_DEL_LAYER);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
public interface IEcsEntityCreate : IEcsSystem
|
||||
{
|
||||
public void OnEntityCreate(EcsEntity entity);
|
||||
public void OnEntityCreate(int entityID);
|
||||
}
|
||||
public interface IEcsEntityDestroy : IEcsSystem
|
||||
{
|
||||
@ -59,9 +59,9 @@ namespace DCFApixels.DragonECS
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
public sealed class EcsEntityCreateRunner : EcsRunner<IEcsEntityCreate>, IEcsEntityCreate
|
||||
{
|
||||
public void OnEntityCreate(EcsEntity entity)
|
||||
public void OnEntityCreate(int entityID)
|
||||
{
|
||||
foreach (var item in targets) item.OnEntityCreate(entity);
|
||||
foreach (var item in targets) item.OnEntityCreate(entityID);
|
||||
}
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
|
@ -1,17 +1,15 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
//TODO использовать этот мир для холостых вызовов. чтоб все нулевые ентити стучались в него, так можно попробовать сократить число проверок
|
||||
//internal sealed class EcsDeathWrold : EcsWorld<EcsDefaultWorld>
|
||||
//{
|
||||
// public EcsDeathWrold(EcsPipeline pipeline) : base(pipeline) { }
|
||||
//}
|
||||
|
||||
|
||||
public sealed class EcsDefaultWorld : EcsWorld<EcsDefaultWorld>
|
||||
{
|
||||
public EcsDefaultWorld() : base(null) { }
|
||||
public EcsDefaultWorld(EcsPipeline pipeline) : base(pipeline) { }
|
||||
}
|
||||
public sealed class EcsPhysicsEventsWorld : EcsWorld<EcsPhysicsEventsWorld>
|
||||
{
|
||||
public EcsPhysicsEventsWorld() : base(null) { }
|
||||
public EcsPhysicsEventsWorld(EcsPipeline pipeline) : base(pipeline) { }
|
||||
}
|
||||
public sealed class EcsEventWorld : EcsWorld<EcsEventWorld>
|
||||
{
|
||||
public EcsEventWorld() : base(null) { }
|
||||
|
@ -2,16 +2,18 @@
|
||||
{
|
||||
public class EcsConsts
|
||||
{
|
||||
public const string EXCEPTION_MESSAGE_PREFIX = "[DragonECS] ";
|
||||
public const string FRAMEWORK_NAME = "DragonECS";
|
||||
|
||||
public const string EXCEPTION_MESSAGE_PREFIX = "["+ FRAMEWORK_NAME + "] ";
|
||||
public const string DEBUG_PREFIX = "[DEBUG] ";
|
||||
public const string DEBUG_WARNING_TAG = "WARNING";
|
||||
public const string DEBUG_ERROR_TAG = "ERROR";
|
||||
|
||||
|
||||
public const string PRE_BEGIN_SYSTEMS_BLOCK = nameof(PRE_BEGIN_SYSTEMS_BLOCK);
|
||||
public const string BEGIN_SYSTEMS_BLOCK = nameof(BEGIN_SYSTEMS_BLOCK);
|
||||
public const string BASIC_SYSTEMS_BLOCK = nameof(BASIC_SYSTEMS_BLOCK);
|
||||
public const string END_SYSTEMS_BLOCK = nameof(END_SYSTEMS_BLOCK);
|
||||
public const string POST_END_SYSTEMS_BLOCK = nameof(POST_END_SYSTEMS_BLOCK);
|
||||
public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER);
|
||||
public const string BEGIN_LAYER = nameof(BEGIN_LAYER);
|
||||
public const string BASIC_LAYER = nameof(BASIC_LAYER);
|
||||
public const string END_LAYER = nameof(END_LAYER);
|
||||
public const string POST_END_LAYER = nameof(POST_END_LAYER);
|
||||
}
|
||||
}
|
||||
|
14
src/Debug/Attributes/DebugDescriptionAttribute.cs
Normal file
14
src/Debug/Attributes/DebugDescriptionAttribute.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugDescriptionAttribute : Attribute
|
||||
{
|
||||
public readonly string description;
|
||||
public DebugDescriptionAttribute(string description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugHideAttribute : Attribute { }
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugNameAttribute : Attribute
|
||||
{
|
||||
public readonly string name;
|
||||
|
@ -1,16 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public class EcsComponentMask
|
||||
{
|
||||
internal Type WorldArchetype;
|
||||
internal int[] Inc;
|
||||
internal int[] Exc;
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,10 @@ using delayedOp = System.Int32;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
using static EcsGroup.ThrowHalper;
|
||||
#endif
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
|
||||
public readonly ref struct EcsReadonlyGroup
|
||||
{
|
||||
@ -17,6 +21,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public bool IsNull => _source == null;
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -42,15 +47,24 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.IsReleazed;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source[index];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Contains(int entityID) => _source.Has(entityID);
|
||||
public bool Has(int entityID) => _source.Has(entityID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup Clone() => _source.Clone();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() => _source.First();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() => _source.Last();
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
@ -98,7 +112,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
private int _lockCount;
|
||||
|
||||
private bool _isReleazed = true;
|
||||
private bool _isReleazed = true;
|
||||
|
||||
#region Properties
|
||||
public EcsWorld World => _source;
|
||||
@ -127,6 +141,17 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _isReleazed;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (index < 0 || index >= Count) ThrowArgumentOutOfRange();
|
||||
#endif
|
||||
return _dense[index];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constrcutors/Finalizer
|
||||
@ -196,6 +221,9 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AggressiveAdd(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (Has(entityID)) ThrowAlreadyContains(entityID);
|
||||
#endif
|
||||
if (++_count >= _dense.Length)
|
||||
Array.Resize(ref _dense, _dense.Length << 1);
|
||||
_dense[_count] = entityID;
|
||||
@ -221,6 +249,9 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AggressiveRemove(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowDoesNotContain(entityID);
|
||||
#endif
|
||||
_dense[_sparse[entityID]] = _dense[_count];
|
||||
_sparse[_dense[_count--]] = _sparse[entityID];
|
||||
_sparse[entityID] = 0;
|
||||
@ -247,18 +278,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Sort/Clear
|
||||
public void Sort()
|
||||
{
|
||||
int increment = 1;
|
||||
for (int i = 0; i < _dense.Length; i++)
|
||||
{
|
||||
if (_sparse[i] > 0)
|
||||
{
|
||||
_sparse[i] = increment;
|
||||
_dense[increment++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
//public void Sort() { } //TODO прошлай реализация сортировки не удачная, так как в dense могут храниться занчения больше чем dense.Length
|
||||
public void Clear()
|
||||
{
|
||||
_count = 0;
|
||||
@ -398,7 +418,7 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Unlock()
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_lockCount <= 0)
|
||||
{
|
||||
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
|
||||
@ -481,6 +501,14 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OtherMethods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() => this[0];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() => this[_count - 1];
|
||||
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
private static bool StaticEquals(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b.GetGroupInternal());
|
||||
private static bool StaticEquals(EcsGroup a, EcsGroup b)
|
||||
@ -514,6 +542,20 @@ namespace DCFApixels.DragonECS
|
||||
_source.ReleaseGroup(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ThrowHalper
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
internal static class ThrowHalper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowAlreadyContains(int entityID) => throw new EcsFrameworkException($"This group already contains entity {entityID}.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowDoesNotContain(int entityID) => throw new EcsFrameworkException($"This group does not contain entity {entityID}.");
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class EcsGroupExtensions
|
||||
|
@ -1,329 +0,0 @@
|
||||
using System;
|
||||
using System.Xml.Schema;
|
||||
using Unity.Profiling;
|
||||
using UnityEditor.Search;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsJoinAttachQueryBase : EcsQueryBase
|
||||
{
|
||||
public abstract void Join(WhereResult targetWorldWhereQuery);
|
||||
}
|
||||
public abstract class EcsJoinAttachQuery<TAttachComponent> : EcsJoinAttachQueryBase
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
private EcsWorld _targetWorld;
|
||||
private EcsAttachPool<TAttachComponent> _targetPool;
|
||||
|
||||
private int _targetWorldCapacity = -1;
|
||||
private int _targetPoolCapacity = -1;
|
||||
|
||||
private int[] _mapping;
|
||||
private int[] _counts;
|
||||
private EntityLinkedList _linkedBasket;
|
||||
|
||||
private bool _isInitTargetWorld = false;
|
||||
|
||||
private long _executeWhereVersion = 0;
|
||||
private long _executeJoinVersion = 0;
|
||||
|
||||
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
|
||||
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
|
||||
|
||||
#region Properties
|
||||
public EcsWorld AttachWorld => _targetWorld;
|
||||
public EcsAttachPool<TAttachComponent> Attach => _targetPool;
|
||||
|
||||
public sealed override long WhereVersion => _executeWhereVersion;
|
||||
public long JoinVersion => _executeJoinVersion;
|
||||
#endregion
|
||||
|
||||
protected sealed override void OnBuild(Builder b)
|
||||
{
|
||||
_targetPool = b.Include<TAttachComponent>();
|
||||
}
|
||||
public sealed override WhereResult Where()
|
||||
{
|
||||
using (_executeWhere.Auto())
|
||||
{
|
||||
_executeWhereVersion++;
|
||||
ExecuteWhere(_targetPool.Entities, groupFilter);
|
||||
return new WhereResult(this, WhereVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void Join(WhereResult targetWorldWhereQuery)
|
||||
{
|
||||
_executeJoin.Begin();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (targetWorldWhereQuery.IsNull)
|
||||
throw new ArgumentNullException();//TODO составить текст исключения.
|
||||
#endif
|
||||
if (!_isInitTargetWorld)
|
||||
InitTargetWorlds(targetWorldWhereQuery.World);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
else
|
||||
if (_targetWorld != targetWorldWhereQuery.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
|
||||
#endif
|
||||
|
||||
//Подготовка массивов
|
||||
if (_targetWorldCapacity < _targetWorld.Capacity)
|
||||
{
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayUtility.Fill(_counts, 0);
|
||||
ArrayUtility.Fill(_mapping, 0);
|
||||
}
|
||||
if (_targetPoolCapacity < _targetPool.Capacity)
|
||||
{
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket.Resize(_targetPoolCapacity);
|
||||
}
|
||||
_linkedBasket.Clear();
|
||||
//Конец подготовки массивов
|
||||
|
||||
Where();
|
||||
foreach (var attachID in groupFilter)
|
||||
{
|
||||
EcsEntity attachTarget = _targetPool.Read(attachID).Target;
|
||||
if (!attachTarget.IsAlive)
|
||||
{
|
||||
//_targetPool.Del(attachID);
|
||||
continue;
|
||||
}
|
||||
int attachTargetID = attachTarget.id;
|
||||
|
||||
ref int nodeIndex = ref _mapping[attachTargetID];
|
||||
if (nodeIndex <= 0)
|
||||
nodeIndex = _linkedBasket.Add(attachID);
|
||||
else
|
||||
_linkedBasket.Insert(nodeIndex, attachID);
|
||||
_counts[attachTargetID]++;
|
||||
}
|
||||
|
||||
_executeJoinVersion++;
|
||||
_executeJoin.End();
|
||||
}
|
||||
private void InitTargetWorlds(EcsWorld targetWorld)
|
||||
{
|
||||
_targetWorld = targetWorld;
|
||||
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
|
||||
_isInitTargetWorld = true;
|
||||
}
|
||||
|
||||
public bool Has(int attachedEnttiyID) => groupFilter.Has(attachedEnttiyID);
|
||||
public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
|
||||
public int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
|
||||
public int GetNodesCount(int entityID) => _counts[entityID];
|
||||
public bool HasNode(int entityID, int attachedEntityID) => groupFilter.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
|
||||
}
|
||||
/*
|
||||
public abstract class EcsJoinRelationQuery<TRelationComponent> : EcsQueryBase
|
||||
where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
private EcsWorld _firstWorld;
|
||||
private EcsWorld _secondWorld;
|
||||
private EcsRelationPool<TRelationComponent> _targetPool;
|
||||
|
||||
internal int _targetPoolCapacity = -1;
|
||||
|
||||
private bool _isInitTargetWorlds = false;
|
||||
private bool _isJoinExecuted = false;
|
||||
|
||||
private long _executeWhereVersion = 0;
|
||||
private long _executeJoinVersion = 0;
|
||||
|
||||
public readonly Orientation ToSecond = new Orientation();
|
||||
public readonly Orientation ToFirst = new Orientation();
|
||||
|
||||
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinRelationQuery.Where");
|
||||
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinRelationQuery.Join");
|
||||
|
||||
#region Properties
|
||||
public EcsWorld RelationFirstWorld => _firstWorld;
|
||||
public EcsWorld RelationSecondWorld => _secondWorld;
|
||||
public EcsRelationPool<TRelationComponent> Relation => _targetPool;
|
||||
public bool IsMonoWorldRelation => _firstWorld == _secondWorld;
|
||||
|
||||
public sealed override long WhereVersion => _executeWhereVersion;
|
||||
public long JoinVersion => _executeJoinVersion;
|
||||
#endregion
|
||||
|
||||
protected sealed override void OnBuild(Builder b)
|
||||
{
|
||||
_targetPool = b.Include<TRelationComponent>();
|
||||
}
|
||||
public sealed override WhereResult Where()
|
||||
{
|
||||
using (_executeWhere.Auto())
|
||||
{
|
||||
_executeWhereVersion++;
|
||||
ExecuteWhere(_targetPool.Entities, groupFilter);
|
||||
return new WhereResult(this, WhereVersion);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
// реализовать возможность получить список всех связей между двумя сущьностями одной напрваленности, и сделать метод для получения одной такой связи
|
||||
//
|
||||
|
||||
public void Join(WhereResult firstWorldWhereQuery, WhereResult secondWorldWhereQuery)
|
||||
{
|
||||
_executeJoin.Begin();
|
||||
_isJoinExecuted = false;
|
||||
if (_isInitTargetWorlds == false)
|
||||
{
|
||||
InitTargetWorlds();
|
||||
if (_isInitTargetWorlds == false)
|
||||
return;
|
||||
};
|
||||
|
||||
// //Подготовка массивов
|
||||
// if (_targetWorldCapacity < _targetWorld.Capacity)
|
||||
// {
|
||||
// _targetWorldCapacity = _targetWorld.Capacity;
|
||||
// _mapping = new int[_targetWorldCapacity];
|
||||
// _counts = new int[_targetWorldCapacity];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ArrayUtility.Fill(_counts, 0);
|
||||
// ArrayUtility.Fill(_mapping, 0);
|
||||
// }
|
||||
// if (_targetPoolCapacity < _targetPool.Capacity)
|
||||
// {
|
||||
// _targetPoolCapacity = _targetPool.Capacity;
|
||||
// _linkedBasket.Resize(_targetPoolCapacity);
|
||||
// }
|
||||
// _linkedBasket.Clear();
|
||||
// //Конец подготовки массивов
|
||||
|
||||
Where();
|
||||
foreach (var attachID in groupFilter)
|
||||
{
|
||||
EcsEntity attachTarget = _targetPool.Read(attachID).First;
|
||||
if (!attachTarget.IsAlive)
|
||||
{
|
||||
//_targetPool.Del(attachID);
|
||||
continue;
|
||||
}
|
||||
int attachTargetID = attachTarget.id;
|
||||
|
||||
ref int nodeIndex = ref _mapping[attachTargetID];
|
||||
if (nodeIndex <= 0)
|
||||
nodeIndex = _linkedBasket.Add(attachID);
|
||||
else
|
||||
_linkedBasket.Insert(nodeIndex, attachID);
|
||||
_counts[attachTargetID]++;
|
||||
}
|
||||
|
||||
_isJoinExecuted = true;
|
||||
_executeJoinVersion++;
|
||||
_executeJoin.End();
|
||||
|
||||
_executeJoinVersion++;
|
||||
_isJoinExecuted = true;
|
||||
_executeJoin.End();
|
||||
}
|
||||
|
||||
private void InitTargetWorlds()
|
||||
{
|
||||
foreach (var e in groupFilter)
|
||||
{
|
||||
ref readonly var rel = ref _targetPool.Read(e);
|
||||
if (rel.First.IsNotNull)
|
||||
_firstWorld = EcsWorld.Worlds[rel.First.world];
|
||||
if (rel.Second.IsNotNull)
|
||||
_secondWorld = EcsWorld.Worlds[rel.Second.world];
|
||||
if (_firstWorld != null && _secondWorld != null)
|
||||
{
|
||||
_isInitTargetWorlds = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_isInitTargetWorlds)
|
||||
{
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool Has(int relationEntityID) => groupFilter.Has(relationEntityID);
|
||||
|
||||
|
||||
public class Orientation
|
||||
{
|
||||
internal int targetWorldCapacity = -1;
|
||||
|
||||
internal int[] mapping;
|
||||
internal int[] counts;
|
||||
internal EntityLinkedList linkedBasket;
|
||||
|
||||
public bool HasRelation(int fromEntityID, int toEntityID)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public bool HasNode(int fromEntityID, int toEntityID)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public EntityLinkedList.EnumerableSpan GetRelations(int fromEntityID) => linkedBasket.Span(mapping[fromEntityID], counts[fromEntityID]);
|
||||
public int GetRelation(int fromEntityID) => counts[fromEntityID] > 0 ? linkedBasket.Get(mapping[fromEntityID]) : 0;
|
||||
public int GetRelationsCount(int fromEntityID) => counts[fromEntityID];
|
||||
}
|
||||
}*/
|
||||
|
||||
#region Extensions
|
||||
public static class EcsJoinQueryBaseExtensions
|
||||
{
|
||||
public static void Join(this EcsJoinAttachQueryBase self, EcsWorld targetWorld)
|
||||
{
|
||||
self.Join(targetWorld.Where<EmptyQuery>());
|
||||
}
|
||||
|
||||
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld, out TQuery query) where TQuery : EcsJoinAttachQueryBase
|
||||
{
|
||||
return self.Join(targetWorld.WhereAll(), out query);
|
||||
}
|
||||
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld) where TQuery : EcsJoinAttachQueryBase
|
||||
{
|
||||
return self.Join<TQuery>(targetWorld.WhereAll());
|
||||
}
|
||||
/* public static class EcsJoinRelationQueryExtensions
|
||||
{
|
||||
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, EcsWorld secondWorld)
|
||||
where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
self.Join(firstWorld.Where<EmptyQuery>(), secondWorld.Where<EmptyQuery>());
|
||||
}
|
||||
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, WhereResult secondWorldWhereQuery)
|
||||
where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
self.Join(firstWorld.Where<EmptyQuery>(), secondWorldWhereQuery);
|
||||
}
|
||||
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, WhereResult firstWorldWhereQuery, EcsWorld secondWorld)
|
||||
where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
self.Join(firstWorldWhereQuery, secondWorld.Where<EmptyQuery>());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
@ -25,7 +27,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
private IEcsSystem[] _allSystems;
|
||||
private Dictionary<Type, IEcsRunner> _runners;
|
||||
private IEcsRunSystem _runRunnerCache;
|
||||
private IEcsRunProcess _runRunnerCache;
|
||||
|
||||
private ReadOnlyCollection<IEcsSystem> _allSystemsSealed;
|
||||
private ReadOnlyDictionary<Type, IEcsRunner> _allRunnersSealed;
|
||||
@ -89,20 +91,20 @@ namespace DCFApixels.DragonECS
|
||||
var ecsPipelineInjectRunner = GetRunner<IEcsInject<EcsPipeline>>();
|
||||
ecsPipelineInjectRunner.Inject(this);
|
||||
EcsRunner.Destroy(ecsPipelineInjectRunner);
|
||||
var preInitRunner = GetRunner<IEcsPreInitSystem>();
|
||||
var preInitRunner = GetRunner<IEcsPreInitProcess>();
|
||||
preInitRunner.PreInit(this);
|
||||
EcsRunner.Destroy(preInitRunner);
|
||||
var initRunner = GetRunner<IEcsInitSystem>();
|
||||
var initRunner = GetRunner<IEcsInitProcess>();
|
||||
initRunner.Init(this);
|
||||
EcsRunner.Destroy(initRunner);
|
||||
|
||||
_runRunnerCache = GetRunner<IEcsRunSystem>();
|
||||
_runRunnerCache = GetRunner<IEcsRunProcess>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Run()
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
CheckBeforeInitForMethod(nameof(Run));
|
||||
CheckAfterDestroyForMethod(nameof(Run));
|
||||
#endif
|
||||
@ -113,7 +115,7 @@ namespace DCFApixels.DragonECS
|
||||
if (_isEmptyDummy)
|
||||
return;
|
||||
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
CheckBeforeInitForMethod(nameof(Run));
|
||||
#endif
|
||||
if (_isDestoryed == true)
|
||||
@ -122,12 +124,12 @@ namespace DCFApixels.DragonECS
|
||||
return;
|
||||
}
|
||||
_isDestoryed = true;
|
||||
GetRunner<IEcsDestroySystem>().Destroy(this);
|
||||
GetRunner<IEcsDestroyProcess>().Destroy(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region StateChecks
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
private void CheckBeforeInitForMethod(string methodName)
|
||||
{
|
||||
if (!_isInit)
|
||||
@ -155,48 +157,51 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
private const int KEYS_CAPACITY = 4;
|
||||
private HashSet<Type> _uniqueTypes;
|
||||
private readonly List<object> _blockExecutionOrder;
|
||||
private readonly Dictionary<object, List<IEcsSystem>> _systems;
|
||||
private readonly string _basicBlocKey;
|
||||
private bool _isBasicBlockDeclared;
|
||||
private bool _isOnlyBasicBlock;
|
||||
private readonly Dictionary<string, List<IEcsSystem>> _systems;
|
||||
private readonly string _basicLayer;
|
||||
|
||||
public readonly LayerList Layers;
|
||||
public Builder()
|
||||
{
|
||||
_basicBlocKey = EcsConsts.BASIC_SYSTEMS_BLOCK;
|
||||
_basicLayer = EcsConsts.BASIC_LAYER;
|
||||
|
||||
Layers = new LayerList(this, _basicLayer);
|
||||
|
||||
Layers.Insert(EcsConsts.BASIC_LAYER, EcsConsts.PRE_BEGIN_LAYER, EcsConsts.BEGIN_LAYER);
|
||||
Layers.InsertAfter(EcsConsts.BASIC_LAYER, EcsConsts.END_LAYER, EcsConsts.POST_END_LAYER);
|
||||
|
||||
_uniqueTypes = new HashSet<Type>();
|
||||
_blockExecutionOrder = new List<object>(KEYS_CAPACITY);
|
||||
_systems = new Dictionary<object, List<IEcsSystem>>(KEYS_CAPACITY);
|
||||
_isBasicBlockDeclared = false;
|
||||
_isOnlyBasicBlock = true;
|
||||
|
||||
SystemsBlock(EcsConsts.PRE_BEGIN_SYSTEMS_BLOCK);
|
||||
SystemsBlock(EcsConsts.BEGIN_SYSTEMS_BLOCK);
|
||||
_systems = new Dictionary<string, List<IEcsSystem>>(KEYS_CAPACITY);
|
||||
}
|
||||
|
||||
public Builder Add(IEcsSystem system, object blockKey = null)
|
||||
public Builder Add(IEcsSystem system, string layerName = null)
|
||||
{
|
||||
AddInternal(system, blockKey, false);
|
||||
AddInternal(system, layerName, false);
|
||||
return this;
|
||||
}
|
||||
public Builder AddUnique(IEcsSystem system, object blockKey = null)
|
||||
public Builder AddUnique(IEcsSystem system, string layerName = null)
|
||||
{
|
||||
AddInternal(system, blockKey, true);
|
||||
AddInternal(system, layerName, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddInternal(IEcsSystem system, object blockKey, bool isUnique)
|
||||
private void AddInternal(IEcsSystem system, string layerName, bool isUnique)
|
||||
{
|
||||
if (blockKey == null) blockKey = _basicBlocKey;
|
||||
if (layerName == null) layerName = _basicLayer;
|
||||
List<IEcsSystem> list;
|
||||
if (!_systems.TryGetValue(blockKey, out list))
|
||||
if (!_systems.TryGetValue(layerName, out list))
|
||||
{
|
||||
list = new List<IEcsSystem>();
|
||||
list.Add(new SystemsBlockMarkerSystem(blockKey.ToString()));
|
||||
_systems.Add(blockKey, list);
|
||||
list = new List<IEcsSystem>
|
||||
{
|
||||
new SystemsBlockMarkerSystem(layerName.ToString())
|
||||
};
|
||||
_systems.Add(layerName, list);
|
||||
}
|
||||
if ((_uniqueTypes.Add(system.GetType()) == false && isUnique))
|
||||
return;
|
||||
list.Add(system);
|
||||
|
||||
if (system is IEcsModule module)//если система одновременно явялется и системой и модулем то за один Add будет вызван Add и AddModule
|
||||
AddModule(module);
|
||||
}
|
||||
|
||||
public Builder AddModule(IEcsModule module)
|
||||
@ -205,70 +210,133 @@ namespace DCFApixels.DragonECS
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder BasicSystemsBlock()
|
||||
{
|
||||
_isBasicBlockDeclared = true;
|
||||
_blockExecutionOrder.Add(_basicBlocKey);
|
||||
return this;
|
||||
}
|
||||
public Builder SystemsBlock(string blockKey)
|
||||
{
|
||||
if (blockKey == null || blockKey == _basicBlocKey)
|
||||
return BasicSystemsBlock();
|
||||
|
||||
_isOnlyBasicBlock = false;
|
||||
_blockExecutionOrder.Add(blockKey);
|
||||
return this;
|
||||
}
|
||||
public Builder InsertSystemsBlock(string blockKey, string beforeBlockKey)
|
||||
{
|
||||
if (blockKey == null || blockKey == _basicBlocKey)
|
||||
{
|
||||
_isBasicBlockDeclared = true;
|
||||
blockKey = _basicBlocKey;
|
||||
}
|
||||
|
||||
_isOnlyBasicBlock = false;
|
||||
int index = _blockExecutionOrder.IndexOf(beforeBlockKey);
|
||||
index = index < 0 ? _blockExecutionOrder.Count - 1 : index;
|
||||
_blockExecutionOrder.Insert(index, blockKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EcsPipeline Build()
|
||||
{
|
||||
if (_isOnlyBasicBlock)
|
||||
{
|
||||
return new EcsPipeline(_systems[_basicBlocKey].ToArray());
|
||||
}
|
||||
//EcsDebug.Print(string.Join(", ", Layers));
|
||||
|
||||
if(_isBasicBlockDeclared == false)
|
||||
_blockExecutionOrder.Insert(_blockExecutionOrder.IndexOf(EcsConsts.BEGIN_SYSTEMS_BLOCK) + 1, _basicBlocKey);//вставить после BEGIN_SYSTEMS_BLOCK
|
||||
SystemsBlock(EcsConsts.END_SYSTEMS_BLOCK);
|
||||
SystemsBlock(EcsConsts.POST_END_SYSTEMS_BLOCK);
|
||||
Add(new DeleteEmptyEntitesSsytem(), EcsConsts.POST_END_SYSTEMS_BLOCK);
|
||||
Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER);
|
||||
|
||||
List<IEcsSystem> result = new List<IEcsSystem>(32);
|
||||
|
||||
List<IEcsSystem> basicBlockList = _systems[_basicBlocKey];
|
||||
List<IEcsSystem> basicBlockList = _systems[_basicLayer];
|
||||
|
||||
foreach (var item in _systems)
|
||||
{
|
||||
if (!_blockExecutionOrder.Contains(item.Key))
|
||||
{
|
||||
if (!Layers.Has(item.Key))
|
||||
basicBlockList.AddRange(item.Value);
|
||||
}
|
||||
}
|
||||
foreach (var item in _blockExecutionOrder)
|
||||
foreach (var item in Layers)
|
||||
{
|
||||
if(_systems.TryGetValue(item, out var list))
|
||||
{
|
||||
result.AddRange(list);
|
||||
}
|
||||
}
|
||||
|
||||
return new EcsPipeline(result.ToArray());
|
||||
}
|
||||
|
||||
public class LayerList : IEnumerable<string>
|
||||
{
|
||||
private const string ADD_LAYER = nameof(ADD_LAYER); // автоматический слой нужный только для метода Add
|
||||
|
||||
private Builder _source;
|
||||
private List<string> _layers;
|
||||
|
||||
private string _basicLayerName;
|
||||
|
||||
public LayerList(Builder source, string basicLayerName)
|
||||
{
|
||||
_source = source;
|
||||
_layers = new List<string>(16) { basicLayerName, ADD_LAYER };
|
||||
_basicLayerName = basicLayerName;
|
||||
}
|
||||
|
||||
public Builder Add(string newLayer) => Insert(ADD_LAYER, newLayer);
|
||||
public Builder Insert(string targetLayer, string newLayer)
|
||||
{
|
||||
if (Has(newLayer)) return _source;
|
||||
|
||||
int index = _layers.IndexOf(targetLayer);
|
||||
if (index < 0)
|
||||
throw new KeyNotFoundException($"Layer {targetLayer} not found");
|
||||
_layers.Insert(index, newLayer);
|
||||
return _source;
|
||||
}
|
||||
public Builder InsertAfter(string targetLayer, string newLayer)
|
||||
{
|
||||
if (Has(newLayer)) return _source;
|
||||
|
||||
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
|
||||
targetLayer = ADD_LAYER;
|
||||
|
||||
int index = _layers.IndexOf(targetLayer);
|
||||
if (index < 0)
|
||||
throw new KeyNotFoundException($"Layer {targetLayer} not found");
|
||||
|
||||
if (++index >= _layers.Count)
|
||||
_layers.Add(newLayer);
|
||||
else
|
||||
_layers.Insert(index, newLayer);
|
||||
return _source;
|
||||
}
|
||||
public Builder Move(string targetLayer, string movingLayer)
|
||||
{
|
||||
_layers.Remove(movingLayer);
|
||||
return Insert(targetLayer, movingLayer);
|
||||
}
|
||||
public Builder MoveAfter(string targetLayer, string movingLayer)
|
||||
{
|
||||
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
|
||||
targetLayer = ADD_LAYER;
|
||||
|
||||
_layers.Remove(movingLayer);
|
||||
return InsertAfter(targetLayer, movingLayer);
|
||||
}
|
||||
|
||||
public Builder Add(params string[] newLayers) => Insert(ADD_LAYER, newLayers);
|
||||
public Builder Insert(string targetLayer, params string[] newLayers)
|
||||
{
|
||||
int index = _layers.IndexOf(targetLayer);
|
||||
if (index < 0)
|
||||
throw new KeyNotFoundException($"Layer {targetLayer} not found");
|
||||
_layers.InsertRange(index, newLayers.Where(o => !Has(o)));
|
||||
return _source;
|
||||
}
|
||||
public Builder InsertAfter(string targetLayer, params string[] newLayers)
|
||||
{
|
||||
int index = _layers.IndexOf(targetLayer);
|
||||
if (index < 0)
|
||||
throw new KeyNotFoundException($"Layer {targetLayer} not found");
|
||||
|
||||
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
|
||||
targetLayer = ADD_LAYER;
|
||||
|
||||
if (++index >= _layers.Count)
|
||||
_layers.AddRange(newLayers.Where(o => !Has(o)));
|
||||
else
|
||||
_layers.InsertRange(index, newLayers.Where(o => !Has(o)));
|
||||
return _source;
|
||||
}
|
||||
public Builder Move(string targetLayer, params string[] movingLayers)
|
||||
{
|
||||
foreach (var movingLayer in movingLayers)
|
||||
_layers.Remove(movingLayer);
|
||||
return Insert(targetLayer, movingLayers);
|
||||
}
|
||||
public Builder MoveAfter(string targetLayer, params string[] movingLayers)
|
||||
{
|
||||
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
|
||||
targetLayer = ADD_LAYER;
|
||||
|
||||
foreach (var movingLayer in movingLayers)
|
||||
_layers.Remove(movingLayer);
|
||||
return InsertAfter(targetLayer, movingLayers);
|
||||
}
|
||||
|
||||
public bool Has(string layer) => _layers.Contains(layer);
|
||||
|
||||
public List<string>.Enumerator GetEnumerator() => _layers.GetEnumerator();
|
||||
IEnumerator<string> IEnumerable<string>.GetEnumerator() => _layers.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _layers.GetEnumerator();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -281,27 +349,23 @@ namespace DCFApixels.DragonECS
|
||||
#region Extensions
|
||||
public static class EcsPipelineExtensions
|
||||
{
|
||||
public static void GetRunner<T>(this EcsPipeline self, out T runner) where T : IEcsSystem
|
||||
{
|
||||
runner = self.GetRunner<T>();
|
||||
}
|
||||
public static bool IsNullOrDestroyed(this EcsPipeline self)
|
||||
{
|
||||
return self == null || self.IsDestoryed;
|
||||
}
|
||||
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
|
||||
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
|
||||
{
|
||||
foreach (var item in range)
|
||||
{
|
||||
self.Add(item, blockKey);
|
||||
self.Add(item, layerName);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
|
||||
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
|
||||
{
|
||||
foreach (var item in range)
|
||||
{
|
||||
self.AddUnique(item, blockKey);
|
||||
self.AddUnique(item, layerName);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
195
src/EcsQuery.cs
195
src/EcsQuery.cs
@ -1,195 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsQueryBase
|
||||
{
|
||||
internal EcsWorld source;
|
||||
internal EcsGroup groupFilter;
|
||||
internal EcsQueryMask mask;
|
||||
|
||||
private bool _isInit;
|
||||
|
||||
#region Properties
|
||||
public EcsQueryMask Mask => mask;
|
||||
public EcsWorld World => source;
|
||||
public bool IsInit => _isInit;
|
||||
|
||||
public abstract long WhereVersion { get; }
|
||||
#endregion
|
||||
|
||||
#region Builder
|
||||
protected virtual void Init(Builder b) { }
|
||||
protected abstract void OnBuild(Builder b);
|
||||
public sealed class Builder : EcsQueryBuilderBase
|
||||
{
|
||||
private EcsWorld _world;
|
||||
private List<int> _inc;
|
||||
private List<int> _exc;
|
||||
|
||||
public EcsWorld World => _world;
|
||||
|
||||
private Builder(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
_inc = new List<int>(8);
|
||||
_exc = new List<int>(4);
|
||||
}
|
||||
internal static TQuery Build<TQuery>(EcsWorld world) where TQuery : EcsQueryBase
|
||||
{
|
||||
Builder builder = new Builder(world);
|
||||
Type queryType = typeof(TQuery);
|
||||
ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
|
||||
EcsQueryBase newQuery;
|
||||
if (constructorInfo != null)
|
||||
{
|
||||
newQuery = (EcsQueryBase)constructorInfo.Invoke(new object[] { builder });
|
||||
}
|
||||
else
|
||||
{
|
||||
newQuery = (EcsQueryBase)Activator.CreateInstance(typeof(TQuery));
|
||||
newQuery.Init(builder);
|
||||
}
|
||||
newQuery.groupFilter = EcsGroup.New(world);
|
||||
newQuery.source = world;
|
||||
newQuery.OnBuild(builder);
|
||||
builder.End(out newQuery.mask);
|
||||
newQuery._isInit = true;
|
||||
return (TQuery)(object)newQuery;
|
||||
}
|
||||
|
||||
public sealed override TPool Include<TComponent, TPool>()
|
||||
{
|
||||
_inc.Add(_world.GetComponentID<TComponent>());
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
public sealed override TPool Exclude<TComponent, TPool>()
|
||||
{
|
||||
_exc.Add(_world.GetComponentID<TComponent>());
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
public sealed override TPool Optional<TComponent, TPool>()
|
||||
{
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
|
||||
private void End(out EcsQueryMask mask)
|
||||
{
|
||||
_inc.Sort();
|
||||
_exc.Sort();
|
||||
mask = new EcsQueryMask(_world.Archetype, _inc.ToArray(), _exc.ToArray());
|
||||
_world = null;
|
||||
_inc = null;
|
||||
_exc = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
public abstract WhereResult Where();
|
||||
|
||||
protected void ExecuteWhere(EcsReadonlyGroup group, EcsGroup result)
|
||||
{
|
||||
var pools = World.pools;
|
||||
result.Clear();
|
||||
foreach (var e in group)
|
||||
{
|
||||
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
|
||||
{
|
||||
if (!pools[mask.Inc[i]].Has(e))
|
||||
goto next;
|
||||
}
|
||||
for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++)
|
||||
{
|
||||
if (pools[mask.Exc[i]].Has(e))
|
||||
goto next;
|
||||
}
|
||||
result.AggressiveAdd(e);
|
||||
next: continue;
|
||||
}
|
||||
}
|
||||
//protected void IsMaskCompatible
|
||||
protected void ExecuteWhereAndSort(EcsReadonlyGroup group, EcsGroup result)
|
||||
{
|
||||
ExecuteWhere(group, result);
|
||||
result.Sort();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO есть идея проверки того что запросбылвыполнен путем создания ref struct которыйсодержит результат выполнения заапроса и существует только в стеке.
|
||||
//таким образом для каждой системы он будет выполняться единожды, без холостых перезапусков, скорее всего эта система будет работать лучше чем "Версии запросов"
|
||||
public abstract class EcsQuery : EcsQueryBase
|
||||
{
|
||||
private ProfilerMarker _execute = new ProfilerMarker("EcsQuery.Where");
|
||||
|
||||
private long _executeWhereVersion = 0;
|
||||
|
||||
#region Properties
|
||||
//на данный момент бесполное свойство, основная идея, реализовать все методы запроса с аргументом (..., ref long version),
|
||||
//далее в теле метододов сравнивать с текущей версией, и если они отличаются, выполнять запрос и перезаписывать значение version
|
||||
//таким образом можно добиться сокращение "холостых" выполнений запроса, тоесть в рамках одной системы запрос всегда будет выполняться один раз
|
||||
//даже при повторном вызове.
|
||||
//Но нужно добавить метод для принудительного повторения запроса без сравнения вресий.
|
||||
//TODO реализовать описанное выше поведение
|
||||
//TODO проверить что лучше подходит int или long. long делает этот механизм очень надежным, но возможно его использование будет существенно влиять на производительность.
|
||||
public sealed override long WhereVersion => _executeWhereVersion;
|
||||
#endregion
|
||||
|
||||
protected sealed override void OnBuild(Builder b) { }
|
||||
public sealed override WhereResult Where()
|
||||
{
|
||||
using (_execute.Auto())
|
||||
{
|
||||
ExecuteWhereAndSort(World.Entities, groupFilter);
|
||||
return new WhereResult(this, ++_executeWhereVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
return groupFilter.Has(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
public class EcsQueryMask : EcsComponentMask
|
||||
{
|
||||
public EcsQueryMask(Type worldArchetypeType, int[] inc, int[] exc)
|
||||
{
|
||||
WorldArchetype = worldArchetypeType;
|
||||
Inc = inc;
|
||||
Exc = exc;
|
||||
}
|
||||
}
|
||||
public abstract class EcsQueryBuilderBase
|
||||
{
|
||||
public abstract TPool Include<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
|
||||
public abstract TPool Exclude<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
|
||||
public abstract TPool Optional<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 16)]
|
||||
public readonly ref struct WhereResult
|
||||
{
|
||||
public readonly EcsQueryBase query; //ref = 8 byte
|
||||
public readonly long version; //long = 8 byte
|
||||
|
||||
#region Properties
|
||||
public bool IsNull => query == null;
|
||||
public EcsWorld World => query.World;
|
||||
public bool IsActual => query.WhereVersion == version;
|
||||
#endregion
|
||||
|
||||
public WhereResult(EcsQueryBase query, long version)
|
||||
{
|
||||
this.query = query;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup.Enumerator GetEnumerator() => query.groupFilter.GetEnumerator();
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -9,6 +8,9 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
using RunnersCore;
|
||||
using System.ComponentModel;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
sealed class EcsRunnerFilterAttribute : Attribute
|
||||
{
|
||||
@ -26,6 +28,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
namespace RunnersCore
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public interface IEcsRunner
|
||||
{
|
||||
public EcsPipeline Source { get; }
|
||||
@ -55,7 +58,7 @@ namespace DCFApixels.DragonECS
|
||||
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
|
||||
}
|
||||
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
for (int i = 0; i < runnerHandlerTypes.Count; i++)
|
||||
{
|
||||
var e = CheckRunnerValide(runnerHandlerTypes[i]);
|
||||
@ -128,6 +131,7 @@ namespace DCFApixels.DragonECS
|
||||
EcsRunner<TInterface>.Register(runnerType);
|
||||
}
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public abstract class EcsRunner<TInterface> : IEcsSystem, IEcsRunner
|
||||
where TInterface : IEcsSystem
|
||||
{
|
||||
@ -135,7 +139,7 @@ namespace DCFApixels.DragonECS
|
||||
private static Type _subclass;
|
||||
internal static void Register(Type subclass)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_subclass != null)
|
||||
{
|
||||
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");
|
||||
|
236
src/EcsSubject.cs
Normal file
236
src/EcsSubject.cs
Normal file
@ -0,0 +1,236 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsSubject
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
internal EcsWorld source;
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
internal EcsMask mask;
|
||||
|
||||
private bool _isInit;
|
||||
|
||||
#region Properties
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public EcsMask Mask => mask;
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public EcsWorld World => source;
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool IsInit => _isInit;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public bool IsMatches(int entityID) => source.IsMaskCompatible(mask, entityID);
|
||||
#endregion
|
||||
|
||||
#region Builder
|
||||
protected virtual void Init(Builder b) { }
|
||||
public sealed class Builder : EcsSubjectBuilderBase
|
||||
{
|
||||
private EcsWorld _world;
|
||||
private List<int> _inc;
|
||||
private List<int> _exc;
|
||||
|
||||
public EcsWorld World => _world;
|
||||
|
||||
private Builder(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
_inc = new List<int>(8);
|
||||
_exc = new List<int>(4);
|
||||
}
|
||||
internal static TSubject Build<TSubject>(EcsWorld world) where TSubject : EcsSubject
|
||||
{
|
||||
Builder builder = new Builder(world);
|
||||
Type queryType = typeof(TSubject);
|
||||
ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
|
||||
EcsSubject newSubject;
|
||||
if (constructorInfo != null)
|
||||
{
|
||||
newSubject = (EcsSubject)constructorInfo.Invoke(new object[] { builder });
|
||||
}
|
||||
else
|
||||
{
|
||||
newSubject = (EcsSubject)Activator.CreateInstance(typeof(TSubject));
|
||||
newSubject.Init(builder);
|
||||
}
|
||||
newSubject.source = world;
|
||||
builder.End(out newSubject.mask);
|
||||
newSubject._isInit = true;
|
||||
return (TSubject)newSubject;
|
||||
}
|
||||
|
||||
public sealed override TPool Include<TComponent, TPool>()
|
||||
{
|
||||
IncludeImplicit<TComponent>();
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
public sealed override TPool Exclude<TComponent, TPool>()
|
||||
{
|
||||
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
public sealed override TPool Optional<TComponent, TPool>()
|
||||
{
|
||||
return _world.GetPool<TComponent, TPool>();
|
||||
}
|
||||
|
||||
public void IncludeImplicit<TComponent>()
|
||||
{
|
||||
int id = _world.GetComponentID<TComponent>();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
|
||||
#endif
|
||||
_inc.Add(_world.GetComponentID<TComponent>());
|
||||
}
|
||||
public void ExcludeImplicit<TComponent>()
|
||||
{
|
||||
int id = _world.GetComponentID<TComponent>();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
|
||||
#endif
|
||||
_exc.Add(_world.GetComponentID<TComponent>());
|
||||
}
|
||||
|
||||
private void End(out EcsMask mask)
|
||||
{
|
||||
_inc.Sort();
|
||||
_exc.Sort();
|
||||
mask = new EcsMask(_world.Archetype, _inc.ToArray(), _exc.ToArray());
|
||||
_world = null;
|
||||
_inc = null;
|
||||
_exc = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class EcsSubjectExtensions
|
||||
{
|
||||
public static EcsSubjectIterator<TSubject> GetIterator<TSubject>(this TSubject self) where TSubject : EcsSubject
|
||||
{
|
||||
return new EcsSubjectIterator<TSubject>(self, self.World.Entities);
|
||||
}
|
||||
public static EcsSubjectIterator<TSubject> GetIteratorFor<TSubject>(this TSubject self, EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
|
||||
{
|
||||
return new EcsSubjectIterator<TSubject>(self, sourceGroup);
|
||||
}
|
||||
}
|
||||
|
||||
#region BuilderBase
|
||||
public abstract class EcsSubjectBuilderBase
|
||||
{
|
||||
public abstract TPool Include<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
|
||||
public abstract TPool Exclude<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
|
||||
public abstract TPool Optional<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Mask
|
||||
public sealed class EcsMask
|
||||
{
|
||||
internal readonly Type WorldType;
|
||||
internal readonly int[] Inc;
|
||||
internal readonly int[] Exc;
|
||||
|
||||
public EcsMask(Type worldType, int[] inc, int[] exc)
|
||||
{
|
||||
WorldType = worldType;
|
||||
Inc = inc;
|
||||
Exc = exc;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterator
|
||||
public ref struct EcsSubjectIterator<TSubject> where TSubject : EcsSubject
|
||||
{
|
||||
public readonly TSubject s;
|
||||
private EcsReadonlyGroup sourceGroup;
|
||||
private Enumerator enumerator;
|
||||
|
||||
public EcsSubjectIterator(TSubject s, EcsReadonlyGroup sourceGroup)
|
||||
{
|
||||
this.s = s;
|
||||
this.sourceGroup = sourceGroup;
|
||||
enumerator = default;
|
||||
}
|
||||
|
||||
public int Entity
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => enumerator.Current;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Begin() => enumerator = GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Next() => enumerator.MoveNext();
|
||||
public void CopyTo(EcsGroup group)
|
||||
{
|
||||
group.Clear();
|
||||
var enumerator = GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
group.AggressiveAdd(enumerator.Current);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(sourceGroup, s);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (var e in this)
|
||||
{
|
||||
result.Append(e);
|
||||
result.Append(", ");
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private EcsGroup.Enumerator _sourceGroup;
|
||||
private readonly int[] _inc;
|
||||
private readonly int[] _exc;
|
||||
private readonly IEcsPoolImplementation[] _pools;
|
||||
|
||||
public Enumerator(EcsReadonlyGroup sourceGroup, EcsSubject subject)
|
||||
{
|
||||
_sourceGroup = sourceGroup.GetEnumerator();
|
||||
_inc = subject.mask.Inc;
|
||||
_exc = subject.mask.Exc;
|
||||
_pools = subject.World.pools;
|
||||
}
|
||||
public int Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _sourceGroup.Current;
|
||||
}
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_sourceGroup.MoveNext())
|
||||
{
|
||||
int e = _sourceGroup.Current;
|
||||
for (int i = 0, iMax = _inc.Length; i < iMax; i++)
|
||||
if (!_pools[_inc[i]].Has(e)) goto next;
|
||||
for (int i = 0, iMax = _exc.Length; i < iMax; i++)
|
||||
if (_pools[_exc[i]].Has(e)) goto next;
|
||||
return true;
|
||||
next: continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
278
src/EcsWorld.cs
278
src/EcsWorld.cs
@ -5,6 +5,13 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
using Internal;
|
||||
|
||||
internal sealed class EcsNullWorld : EcsWorld<EcsNullWorld>
|
||||
{
|
||||
public EcsNullWorld() : base(null, false) { }
|
||||
}
|
||||
|
||||
public abstract class EcsWorld
|
||||
{
|
||||
private const short GEN_BITS = 0x7fff;
|
||||
@ -15,7 +22,7 @@ namespace DCFApixels.DragonECS
|
||||
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
|
||||
public readonly short uniqueID;
|
||||
|
||||
private int _worldArchetypeID;
|
||||
private int _worldTypeID;
|
||||
|
||||
private IntDispenser _entityDispenser;
|
||||
private int _entitiesCount;
|
||||
@ -30,10 +37,11 @@ namespace DCFApixels.DragonECS
|
||||
private int[] _delEntBuffer;
|
||||
private int _delEntBufferCount;
|
||||
|
||||
internal EcsPoolBase[] pools;
|
||||
internal IEcsPoolImplementation[] pools;
|
||||
private EcsNullPool _nullPool;
|
||||
|
||||
private EcsQueryBase[] _queries;
|
||||
private EcsSubject[] _subjects;
|
||||
private EcsQueryExecutor[] _executors;
|
||||
|
||||
private EcsPipeline _pipeline;
|
||||
|
||||
@ -50,26 +58,35 @@ namespace DCFApixels.DragonECS
|
||||
public int Capacity => _entitesCapacity; //_denseEntities.Length;
|
||||
public EcsPipeline Pipeline => _pipeline;
|
||||
public EcsReadonlyGroup Entities => _allEntites.Readonly;
|
||||
public ReadOnlySpan<EcsPoolBase> AllPools => pools;
|
||||
public ReadOnlySpan<IEcsPoolImplementation> AllPools => pools;
|
||||
#endregion
|
||||
|
||||
#region Constructors/Destroy
|
||||
public EcsWorld(EcsPipeline pipline)
|
||||
static EcsWorld()
|
||||
{
|
||||
EcsNullWorld nullWorld = new EcsNullWorld();
|
||||
Worlds[0] = nullWorld;
|
||||
}
|
||||
public EcsWorld(EcsPipeline pipline) : this(pipline, true) { }
|
||||
internal EcsWorld(EcsPipeline pipline, bool isIndexable)
|
||||
{
|
||||
_entitesCapacity = 512;
|
||||
|
||||
uniqueID = (short)_worldIdDispenser.GetFree();
|
||||
if (uniqueID >= Worlds.Length)
|
||||
Array.Resize(ref Worlds, Worlds.Length << 1);
|
||||
Worlds[uniqueID] = this;
|
||||
if (isIndexable)
|
||||
{
|
||||
uniqueID = (short)_worldIdDispenser.GetFree();
|
||||
if (uniqueID >= Worlds.Length)
|
||||
Array.Resize(ref Worlds, Worlds.Length << 1);
|
||||
Worlds[uniqueID] = this;
|
||||
}
|
||||
|
||||
_worldArchetypeID = WorldMetaStorage.GetWorldId(Archetype);
|
||||
_worldTypeID = WorldMetaStorage.GetWorldId(Archetype);
|
||||
|
||||
_pipeline = pipline ?? EcsPipeline.Empty;
|
||||
if (!_pipeline.IsInit) pipline.Init();
|
||||
_entityDispenser = new IntDispenser(0);
|
||||
_nullPool = EcsNullPool.instance;
|
||||
pools = new EcsPoolBase[512];
|
||||
pools = new IEcsPoolImplementation[512];
|
||||
ArrayUtility.Fill(pools, _nullPool);
|
||||
|
||||
_gens = new short[_entitesCapacity];
|
||||
@ -82,13 +99,16 @@ namespace DCFApixels.DragonECS
|
||||
_groups = new List<WeakReference<EcsGroup>>();
|
||||
_allEntites = GetGroupFromPool();
|
||||
|
||||
_queries = new EcsQueryBase[128];
|
||||
_subjects = new EcsSubject[128];
|
||||
_executors = new EcsQueryExecutor[128];
|
||||
|
||||
_entityCreate = _pipeline.GetRunner<IEcsEntityCreate>();
|
||||
_entityDestry = _pipeline.GetRunner<IEcsEntityDestroy>();
|
||||
_pipeline.GetRunner<IEcsInject<EcsWorld>>().Inject(this);
|
||||
_pipeline.GetRunner<IEcsWorldCreate>().OnWorldCreate(this);
|
||||
}
|
||||
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
_entityDispenser = null;
|
||||
@ -96,7 +116,8 @@ namespace DCFApixels.DragonECS
|
||||
_gens = null;
|
||||
pools = null;
|
||||
_nullPool = null;
|
||||
_queries = null;
|
||||
_subjects = null;
|
||||
_executors = null;
|
||||
|
||||
Worlds[uniqueID] = null;
|
||||
_worldIdDispenser.Release(uniqueID);
|
||||
@ -109,14 +130,14 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetComponentID
|
||||
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldArchetypeID);////ComponentType<TWorldArchetype>.uniqueID;
|
||||
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldTypeID);////ComponentType<TWorldArchetype>.uniqueID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetPool
|
||||
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new()
|
||||
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new()
|
||||
{
|
||||
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldArchetypeID);
|
||||
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldTypeID);
|
||||
|
||||
if (uniqueID >= pools.Length)
|
||||
{
|
||||
@ -129,8 +150,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
var pool = new TPool();
|
||||
pools[uniqueID] = pool;
|
||||
pool.PreInitInternal(this, uniqueID);
|
||||
pool.InvokeInit(this);
|
||||
pool.OnInit(this, uniqueID);
|
||||
|
||||
//EcsDebug.Print(pool.GetType().FullName);
|
||||
}
|
||||
@ -140,44 +160,118 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Queries
|
||||
public TQuery Select<TQuery>() where TQuery : EcsQueryBase
|
||||
public TSubject GetSubject<TSubject>() where TSubject : EcsSubject
|
||||
{
|
||||
int uniqueID = WorldMetaStorage.GetQueryId<TQuery>(_worldArchetypeID);
|
||||
if (uniqueID >= _queries.Length)
|
||||
Array.Resize(ref _queries, _queries.Length << 1);
|
||||
if (_queries[uniqueID] == null)
|
||||
_queries[uniqueID] = EcsQueryBase.Builder.Build<TQuery>(this);
|
||||
return (TQuery)_queries[uniqueID];
|
||||
int uniqueID = WorldMetaStorage.GetSubjectId<TSubject>(_worldTypeID);
|
||||
if (uniqueID >= _subjects.Length)
|
||||
Array.Resize(ref _subjects, _subjects.Length << 1);
|
||||
if (_subjects[uniqueID] == null)
|
||||
_subjects[uniqueID] = EcsSubject.Builder.Build<TSubject>(this);
|
||||
return (TSubject)_subjects[uniqueID];
|
||||
}
|
||||
public WhereResult Where<TQuery>(out TQuery query) where TQuery : EcsQueryBase
|
||||
#region Iterate
|
||||
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
|
||||
{
|
||||
query = Select<TQuery>();
|
||||
return query.Where();
|
||||
}
|
||||
public WhereResult Where<TQuery>() where TQuery : EcsQueryBase
|
||||
{
|
||||
return Select<TQuery>().Where();
|
||||
}
|
||||
|
||||
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery, out TQuery query) where TQuery : EcsJoinAttachQueryBase
|
||||
{
|
||||
query = Select<TQuery>();
|
||||
query.Join(targetWorldWhereQuery);
|
||||
return query;
|
||||
subject = GetSubject<TSubject>();
|
||||
return subject.GetIteratorFor(sourceGroup);
|
||||
}
|
||||
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery) where TQuery : EcsJoinAttachQueryBase
|
||||
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
|
||||
{
|
||||
TQuery query = Select<TQuery>();
|
||||
query.Join(targetWorldWhereQuery);
|
||||
return query;
|
||||
return GetSubject<TSubject>().GetIteratorFor(sourceGroup);
|
||||
}
|
||||
public EcsSubjectIterator<TSubject> Iterate<TSubject>(out TSubject subject) where TSubject : EcsSubject
|
||||
{
|
||||
subject = GetSubject<TSubject>();
|
||||
return subject.GetIterator();
|
||||
}
|
||||
public EcsSubjectIterator<TSubject> Iterate<TSubject>() where TSubject : EcsSubject
|
||||
{
|
||||
return GetSubject<TSubject>().GetIterator();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsMaskCompatible
|
||||
public bool IsMaskCompatible(EcsComponentMask mask, int entityID)
|
||||
#region Where
|
||||
private EcsWhereExecutor<TSubject> GetWhereExecutor<TSubject>() where TSubject : EcsSubject
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (mask.WorldArchetype != Archetype)
|
||||
int id = WorldMetaStorage.GetExecutorId<EcsWhereExecutor<TSubject>>(_worldTypeID);
|
||||
if (id >= _executors.Length)
|
||||
Array.Resize(ref _executors, _executors.Length << 1);
|
||||
if (_executors[id] == null)
|
||||
_executors[id] = new EcsWhereExecutor<TSubject>(GetSubject<TSubject>());
|
||||
return (EcsWhereExecutor<TSubject>)_executors[id];
|
||||
}
|
||||
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
|
||||
{
|
||||
var executor = GetWhereExecutor<TSubject>();
|
||||
subject = executor.Subject;
|
||||
return executor.ExecuteFor(sourceGroup);
|
||||
}
|
||||
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
|
||||
{
|
||||
return GetWhereExecutor<TSubject>().ExecuteFor(sourceGroup);
|
||||
}
|
||||
public EcsWhereResult<TSubject> Where<TSubject>(out TSubject subject) where TSubject : EcsSubject
|
||||
{
|
||||
var executor = GetWhereExecutor<TSubject>();
|
||||
subject = executor.Subject;
|
||||
return executor.Execute();
|
||||
}
|
||||
public EcsWhereResult<TSubject> Where<TSubject>() where TSubject : EcsSubject
|
||||
{
|
||||
return GetWhereExecutor<TSubject>().Execute();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Join
|
||||
private EcsJoinAttachExecutor<TSubject, TAttachComponent> GetJoinAttachExecutor<TSubject, TAttachComponent>()
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
int id = WorldMetaStorage.GetExecutorId<EcsJoinAttachExecutor<TSubject, TAttachComponent>>(_worldTypeID);
|
||||
if (id >= _executors.Length)
|
||||
Array.Resize(ref _executors, _executors.Length << 1);
|
||||
if (_executors[id] == null)
|
||||
_executors[id] = new EcsJoinAttachExecutor<TSubject, TAttachComponent>(GetSubject<TSubject>());
|
||||
return (EcsJoinAttachExecutor<TSubject, TAttachComponent>)_executors[id];
|
||||
}
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> JoinFor<TSubject, TAttachComponent>(EcsReadonlyGroup sourceGroup, out TSubject subject)
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
var executor = GetJoinAttachExecutor<TSubject, TAttachComponent>();
|
||||
subject = executor.Subject;
|
||||
return executor.ExecuteFor(sourceGroup);
|
||||
}
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> JoinFor<TSubject, TAttachComponent>(EcsReadonlyGroup sourceGroup)
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
return GetJoinAttachExecutor<TSubject, TAttachComponent>().ExecuteFor(sourceGroup);
|
||||
}
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> Join<TSubject, TAttachComponent>(out TSubject subject)
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
var executor = GetJoinAttachExecutor<TSubject, TAttachComponent>();
|
||||
subject = executor.Subject;
|
||||
return executor.Execute();
|
||||
}
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> Join<TSubject, TAttachComponent>()
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
return GetJoinAttachExecutor<TSubject, TAttachComponent>().Execute();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsMaskCompatible
|
||||
public bool IsMaskCompatible(EcsMask mask, int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (mask.WorldType != Archetype)
|
||||
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TTableArhetype)");
|
||||
#endif
|
||||
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
|
||||
@ -195,7 +289,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Entity
|
||||
public EcsEntity NewEntity()
|
||||
public int NewEntity()
|
||||
{
|
||||
int entityID = _entityDispenser.GetFree();
|
||||
_entitiesCount++;
|
||||
@ -221,13 +315,17 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
foreach (var item in pools)
|
||||
item.InvokeOnWorldResize(_gens.Length);
|
||||
item.OnWorldResize(_gens.Length);
|
||||
}
|
||||
_gens[entityID] &= GEN_BITS;
|
||||
EcsEntity entity = new EcsEntity(entityID, ++_gens[entityID], uniqueID);
|
||||
_entityCreate.OnEntityCreate(entity);
|
||||
_entityCreate.OnEntityCreate(entityID);
|
||||
_allEntites.Add(entityID);
|
||||
return entity;
|
||||
return entityID;
|
||||
}
|
||||
public entlong NewEntityLong()
|
||||
{
|
||||
int e = NewEntity();
|
||||
return GetEntityLong(e);
|
||||
}
|
||||
|
||||
public void DelEntity(int entityID)
|
||||
@ -242,13 +340,16 @@ namespace DCFApixels.DragonECS
|
||||
ReleaseDelEntityBuffer();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity GetEcsEntity(int entityID) => new EcsEntity(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода
|
||||
public entlong GetEntityLong(int entityID) => new entlong(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsUsed(int entityID) => (_gens[entityID] | DEATH_GEN_BIT) == 0;
|
||||
public bool IsUsed(int entityID) => _gens[entityID] >= 0;
|
||||
public void ReleaseDelEntityBuffer()
|
||||
{
|
||||
ReadOnlySpan<int> buffser = new ReadOnlySpan<int>(_delEntBuffer, 0, _delEntBufferCount);
|
||||
foreach (var pool in pools)
|
||||
pool.OnReleaseDelEntityBuffer(buffser);
|
||||
for (int i = 0; i < _delEntBufferCount; i++)
|
||||
_entityDispenser.Release(_delEntBuffer[i]);
|
||||
_delEntBufferCount = 0;
|
||||
@ -264,8 +365,6 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void IncrementEntityComponentCount(int entityID)
|
||||
{
|
||||
@ -278,7 +377,7 @@ namespace DCFApixels.DragonECS
|
||||
if(count == 0)
|
||||
DelEntity(entityID);
|
||||
|
||||
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента.декремента компонентов");
|
||||
#endif
|
||||
}
|
||||
@ -305,6 +404,42 @@ namespace DCFApixels.DragonECS
|
||||
_groupsPool.Push(group);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Debug
|
||||
// public int GetComponents(int entity, ref object[] list)
|
||||
// {
|
||||
// var entityOffset = GetRawEntityOffset(entity);
|
||||
// var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount];
|
||||
// if (itemsCount == 0) { return 0; }
|
||||
// if (list == null || list.Length < itemsCount)
|
||||
// {
|
||||
// list = new object[_pools.Length];
|
||||
// }
|
||||
// var dataOffset = entityOffset + RawEntityOffsets.Components;
|
||||
// for (var i = 0; i < itemsCount; i++)
|
||||
// {
|
||||
// list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity);
|
||||
// }
|
||||
// return itemsCount;
|
||||
// }
|
||||
//
|
||||
// public int GetComponentTypes(int entity, ref Type[] list)
|
||||
// {
|
||||
// var entityOffset = GetRawEntityOffset(entity);
|
||||
// var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount];
|
||||
// if (itemsCount == 0) { return 0; }
|
||||
// if (list == null || list.Length < itemsCount)
|
||||
// {
|
||||
// list = new Type[_pools.Length];
|
||||
// }
|
||||
// var dataOffset = entityOffset + RawEntityOffsets.Components;
|
||||
// for (var i = 0; i < itemsCount; i++)
|
||||
// {
|
||||
// list[i] = _pools[_entities[dataOffset + i]].GetComponentType();
|
||||
// }
|
||||
// return itemsCount;
|
||||
// }
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class EcsWorld<TWorldArchetype> : EcsWorld
|
||||
@ -312,6 +447,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public override Type Archetype => typeof(TWorldArchetype);
|
||||
public EcsWorld(EcsPipeline pipline) : base(pipline) { }
|
||||
internal EcsWorld(EcsPipeline pipline, bool isIndexable) : base(pipline, isIndexable) { }
|
||||
}
|
||||
|
||||
#region Utils
|
||||
@ -366,7 +502,9 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetComponentId<T>(int worldID) => Component<T>.Get(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetQueryId<T>(int worldID) => Query<T>.Get(worldID);
|
||||
public static int GetSubjectId<T>(int worldID) => Subject<T>.Get(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetExecutorId<T>(int worldID) => Executor<T>.Get(worldID);
|
||||
private abstract class Resizer
|
||||
{
|
||||
public abstract void Resize(int size);
|
||||
@ -376,7 +514,8 @@ namespace DCFApixels.DragonECS
|
||||
public override void Resize(int size)
|
||||
{
|
||||
Array.Resize(ref Component<T>.ids, size);
|
||||
Array.Resize(ref Query<T>.ids, size);
|
||||
Array.Resize(ref Subject<T>.ids, size);
|
||||
Array.Resize(ref Executor<T>.ids, size);
|
||||
}
|
||||
}
|
||||
private static class Component<T>
|
||||
@ -398,10 +537,29 @@ namespace DCFApixels.DragonECS
|
||||
return id;
|
||||
}
|
||||
}
|
||||
private static class Query<T>
|
||||
private static class Subject<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static Query()
|
||||
static Subject()
|
||||
{
|
||||
ids = new int[tokenCount];
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
ids[i] = -1;
|
||||
resizer.Add(new Resizer<T>());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
id = queryCounts[token]++;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
private static class Executor<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static Executor()
|
||||
{
|
||||
ids = new int[tokenCount];
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
|
@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
// uniqueID - 32 bits
|
||||
// gen - 16 bits
|
||||
// world - 16 bits
|
||||
/// <summary>Strong identifier/Permanent entity identifier</summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
|
||||
public readonly partial struct EcsEntity : IEquatable<long>, IEquatable<EcsEntity>
|
||||
{
|
||||
public static readonly EcsEntity NULL = default;
|
||||
[FieldOffset(0)]
|
||||
internal readonly long full; //Union
|
||||
[FieldOffset(0)]
|
||||
public readonly int id;
|
||||
[FieldOffset(4)]
|
||||
public readonly short gen;
|
||||
[FieldOffset(6)]
|
||||
public readonly short world;
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public ent ToEnt() => EcsWorld.Worlds[world].IsAlive(id, gen) ? new ent(id) : default;
|
||||
|
||||
public bool IsAlive => EcsWorld.Worlds[world].IsAlive(id, gen);
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsEntity(int id, short gen, short world) : this()
|
||||
{
|
||||
this.id = id;
|
||||
this.gen = gen;
|
||||
this.world = world;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsEntity(long full) : this()
|
||||
{
|
||||
this.full = full;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(EcsEntity other) => full == other.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(long other) => full == other;
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override string ToString() => $"Entity(uniqueID:{id} gen:{gen} world:{world})";
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj) => obj is EcsEntity other && full == other.full;
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(in EcsEntity a, in EcsEntity b) => a.full == b.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(in EcsEntity a, in EcsEntity b) => a.full != b.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(in EcsEntity a) => a.full;
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static explicit operator ent(in EcsEntity a) => a.ToEnt();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator EcsEntity(in long a) => new EcsEntity(a);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public readonly partial struct EcsEntity
|
||||
{
|
||||
private static EcsProfilerMarker _IsNullMarker = new EcsProfilerMarker("EcsEntity.IsNull");
|
||||
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
//using (_IsNullMarker.Auto())
|
||||
return this == NULL;
|
||||
}
|
||||
}
|
||||
public bool IsNotNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
//using (_IsNullMarker.Auto())
|
||||
return this != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
public EcsWorld GetWorld() => EcsWorld.Worlds[world];
|
||||
}
|
||||
|
||||
public static partial class entExtensions
|
||||
{
|
||||
private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("EcsEntity.IsAlive");
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static bool IsAlive(this ref EcsEntity self)
|
||||
// {
|
||||
// //using (_IsAliveMarker.Auto())
|
||||
// //{
|
||||
// bool result = EcsWorld.Worlds[self.world].IsAlive(self.id, self.gen);
|
||||
// if (!result) self = EcsEntity.NULL;
|
||||
// return result;
|
||||
// //}
|
||||
// }
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[Serializable]
|
||||
public class EcsRelationException : Exception
|
||||
public class EcsRelationException : EcsFrameworkException
|
||||
{
|
||||
public EcsRelationException() { }
|
||||
public EcsRelationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { }
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[Serializable]
|
||||
public class EcsRunnerImplementationException : Exception
|
||||
public class EcsRunnerImplementationException : EcsFrameworkException
|
||||
{
|
||||
public EcsRunnerImplementationException() { }
|
||||
public EcsRunnerImplementationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { }
|
||||
|
189
src/Executors/EcsJoinAttachExecutor.cs
Normal file
189
src/Executors/EcsJoinAttachExecutor.cs
Normal file
@ -0,0 +1,189 @@
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsJoinAttachExecutor<TSubject, TAttachComponent> : EcsQueryExecutor
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
private readonly TSubject _subject;
|
||||
internal readonly EcsGroup _filteredGroup;
|
||||
private EcsWorld _targetWorld;
|
||||
|
||||
private EcsAttachPool<TAttachComponent> _targetPool;
|
||||
|
||||
private int _targetWorldCapacity = -1;
|
||||
private int _targetPoolCapacity = -1;
|
||||
|
||||
private int[] _mapping;
|
||||
private int[] _counts;
|
||||
private EntityLinkedList _linkedBasket;
|
||||
|
||||
private bool _isInitTargetWorld = false;
|
||||
|
||||
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
|
||||
|
||||
private long _executeVersion;
|
||||
|
||||
#region Properties
|
||||
public TSubject Subject => _subject;
|
||||
internal long ExecuteVersion => _executeVersion;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public EcsJoinAttachExecutor(TSubject subject)
|
||||
{
|
||||
_subject = subject;
|
||||
_filteredGroup = EcsGroup.New(subject.World);
|
||||
_targetPool = subject.World.GetPool<TAttachComponent>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> Execute() => ExecuteFor(_targetPool.Entities);
|
||||
public EcsJoinAttachResult<TSubject, TAttachComponent> ExecuteFor(EcsReadonlyGroup sourceGroup)
|
||||
{
|
||||
_executeJoin.Begin();
|
||||
var world = _subject.World;
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
|
||||
#endif
|
||||
if (!_isInitTargetWorld)
|
||||
InitTargetWorlds(sourceGroup.World);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
else
|
||||
if (_targetWorld != sourceGroup.World) throw new System.ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
|
||||
#endif
|
||||
|
||||
//Подготовка массивов
|
||||
if (_targetWorldCapacity < _targetWorld.Capacity)
|
||||
{
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayUtility.Fill(_counts, 0);
|
||||
ArrayUtility.Fill(_mapping, 0);
|
||||
}
|
||||
if (_targetPoolCapacity < _targetPool.Capacity)
|
||||
{
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket.Resize(_targetPoolCapacity);
|
||||
}
|
||||
_linkedBasket.Clear();
|
||||
//Конец подготовки массивов
|
||||
|
||||
var iterator = new EcsSubjectIterator<TSubject>(_subject, sourceGroup);
|
||||
foreach (var attachID in iterator)
|
||||
{
|
||||
entlong attachTarget = _targetPool.Read(attachID).Target;
|
||||
if (!attachTarget.IsAlive)
|
||||
{
|
||||
//_targetPool.Del(attachID);
|
||||
continue;
|
||||
}
|
||||
int attachTargetID = attachTarget.id;
|
||||
//if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery
|
||||
|
||||
ref int nodeIndex = ref _mapping[attachTargetID];
|
||||
if (nodeIndex <= 0)
|
||||
nodeIndex = _linkedBasket.Add(attachID);
|
||||
else
|
||||
_linkedBasket.Insert(nodeIndex, attachID);
|
||||
_counts[attachTargetID]++;
|
||||
}
|
||||
|
||||
_executeVersion++;
|
||||
_executeJoin.End();
|
||||
|
||||
return new EcsJoinAttachResult<TSubject, TAttachComponent>(_subject, this , _executeVersion);
|
||||
}
|
||||
private void InitTargetWorlds(EcsWorld targetWorld)
|
||||
{
|
||||
_targetWorld = targetWorld;
|
||||
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
|
||||
_isInitTargetWorld = true;
|
||||
}
|
||||
internal sealed override void Destroy()
|
||||
{
|
||||
_filteredGroup.Release();
|
||||
_targetWorld = null;
|
||||
_mapping = null;
|
||||
_counts = null;
|
||||
_linkedBasket = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal result methods
|
||||
internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID);
|
||||
internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
|
||||
internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
|
||||
internal int GetNodesCount(int entityID) => _counts[entityID];
|
||||
internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region JoinAttachExecuter Results
|
||||
public readonly ref struct EcsJoinAttachResult<TSubject, TAttachComponent>
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
public readonly TSubject s;
|
||||
private readonly EcsJoinAttachExecutor<TSubject, TAttachComponent> _executer;
|
||||
private readonly long _verison;
|
||||
|
||||
public bool IsRelevant => _verison == _executer.ExecuteVersion;
|
||||
|
||||
public EcsJoinAttachResult(TSubject s, EcsJoinAttachExecutor<TSubject, TAttachComponent> executer, long version)
|
||||
{
|
||||
this.s = s;
|
||||
_executer = executer;
|
||||
_verison = version;
|
||||
}
|
||||
|
||||
public bool Has(int attachedEnttiyID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.Has(attachedEnttiyID);
|
||||
}
|
||||
public EntityLinkedList.EnumerableSpan GetNodes(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNodes(entityID);
|
||||
}
|
||||
public int GetNode(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNode(entityID);
|
||||
}
|
||||
public int GetNodesCount(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNodesCount(entityID);
|
||||
}
|
||||
public bool HasNode(int entityID, int attachedEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.HasNode(entityID, attachedEntityID);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
198
src/Executors/EcsJoinHierarchyExecutor.cs
Normal file
198
src/Executors/EcsJoinHierarchyExecutor.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsJoinHierarchyExecutor<TSubject, TAttachComponent> : EcsQueryExecutor
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
private readonly TSubject _subject;
|
||||
internal readonly EcsGroup _filteredGroup;
|
||||
private EcsWorld _targetWorld;
|
||||
|
||||
private EcsAttachPool<TAttachComponent> _targetPool;
|
||||
|
||||
private int _targetWorldCapacity = -1;
|
||||
private int _targetPoolCapacity = -1;
|
||||
|
||||
private int[] _mapping;
|
||||
private int[] _counts;
|
||||
private EntityLinkedList _linkedBasket;
|
||||
|
||||
private bool _isInitTargetWorld = false;
|
||||
|
||||
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
|
||||
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
|
||||
|
||||
private long _executeVersion;
|
||||
|
||||
#region Properties
|
||||
public TSubject Subject => _subject;
|
||||
internal long ExecuteVersion => _executeVersion;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public EcsJoinHierarchyExecutor(TSubject subject)
|
||||
{
|
||||
_subject = subject;
|
||||
_filteredGroup = EcsGroup.New(subject.World);
|
||||
_targetPool = subject.World.GetPool<TAttachComponent>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public EcsJoinHierarchyResult<TSubject, TAttachComponent> Execute() => ExecuteFor(_targetPool.Entities);
|
||||
public EcsJoinHierarchyResult<TSubject, TAttachComponent> ExecuteFor(EcsReadonlyGroup sourceGroup)
|
||||
{
|
||||
var world = _subject.World;
|
||||
_executeJoin.Begin();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (sourceGroup.IsNull) throw new ArgumentNullException();//TODO составить текст исключения.
|
||||
#endif
|
||||
if (!_isInitTargetWorld)
|
||||
InitTargetWorlds(sourceGroup.World);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
else
|
||||
if (_targetWorld != sourceGroup.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
|
||||
#endif
|
||||
|
||||
//Подготовка массивов
|
||||
if (_targetWorldCapacity < _targetWorld.Capacity)
|
||||
{
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayUtility.Fill(_counts, 0);
|
||||
ArrayUtility.Fill(_mapping, 0);
|
||||
}
|
||||
if (_targetPoolCapacity < _targetPool.Capacity)
|
||||
{
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket.Resize(_targetPoolCapacity);
|
||||
}
|
||||
_linkedBasket.Clear();
|
||||
//Конец подготовки массивов
|
||||
|
||||
var iterator = new EcsSubjectIterator<TSubject>(_subject, sourceGroup);
|
||||
foreach (var attachID in iterator)
|
||||
{
|
||||
entlong attachTarget = _targetPool.Read(attachID).Target;
|
||||
if (!attachTarget.IsAlive)
|
||||
{
|
||||
//_targetPool.Del(attachID);
|
||||
continue;
|
||||
}
|
||||
int attachTargetID = attachTarget.id;
|
||||
//if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery
|
||||
|
||||
ref int nodeIndex = ref _mapping[attachTargetID];
|
||||
if (nodeIndex <= 0)
|
||||
nodeIndex = _linkedBasket.Add(attachID);
|
||||
else
|
||||
_linkedBasket.Insert(nodeIndex, attachID);
|
||||
_counts[attachTargetID]++;
|
||||
}
|
||||
|
||||
_executeVersion++;
|
||||
_executeJoin.End();
|
||||
|
||||
return new EcsJoinHierarchyResult<TSubject, TAttachComponent>(_subject, this , _executeVersion);
|
||||
}
|
||||
private void InitTargetWorlds(EcsWorld targetWorld)
|
||||
{
|
||||
_targetWorld = targetWorld;
|
||||
|
||||
_targetWorldCapacity = _targetWorld.Capacity;
|
||||
_mapping = new int[_targetWorldCapacity];
|
||||
_counts = new int[_targetWorldCapacity];
|
||||
|
||||
_targetPoolCapacity = _targetPool.Capacity;
|
||||
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
|
||||
_isInitTargetWorld = true;
|
||||
}
|
||||
internal sealed override void Destroy()
|
||||
{
|
||||
_filteredGroup.Release();
|
||||
_targetWorld = null;
|
||||
_mapping = null;
|
||||
_counts = null;
|
||||
_linkedBasket = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal result methods
|
||||
internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID);
|
||||
|
||||
internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
|
||||
internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
|
||||
internal int GetNodesCount(int entityID) => _counts[entityID];
|
||||
internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
|
||||
|
||||
|
||||
internal EntityLinkedList.EnumerableSpan GetSubNodes(int entityID) => throw new NotImplementedException();
|
||||
internal int GetSubNode(int entityID) => throw new NotImplementedException();
|
||||
internal bool GetSubNodesCount(int entityID, int attachedEntityID) => throw new NotImplementedException();
|
||||
internal bool HasSubNode(int entityID, int attachedEntityID) => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region JoinAttachExecuter Results
|
||||
public readonly ref struct EcsJoinHierarchyResult<TSubject, TAttachComponent>
|
||||
where TSubject : EcsSubject
|
||||
where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
public readonly TSubject s;
|
||||
private readonly EcsJoinHierarchyExecutor<TSubject, TAttachComponent> _executer;
|
||||
private readonly long _verison;
|
||||
|
||||
public bool IsRelevant => _verison == _executer.ExecuteVersion;
|
||||
|
||||
public EcsJoinHierarchyResult(TSubject s, EcsJoinHierarchyExecutor<TSubject, TAttachComponent> executer, long version)
|
||||
{
|
||||
this.s = s;
|
||||
_executer = executer;
|
||||
_verison = version;
|
||||
}
|
||||
|
||||
public bool Has(int attachedEnttiyID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.Has(attachedEnttiyID);
|
||||
}
|
||||
public EntityLinkedList.EnumerableSpan GetNodes(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNodes(entityID);
|
||||
}
|
||||
public int GetNode(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNode(entityID);
|
||||
}
|
||||
public int GetNodesCount(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.GetNodesCount(entityID);
|
||||
}
|
||||
public bool HasNode(int entityID, int attachedEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return _executer.HasNode(entityID, attachedEntityID);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
7
src/Executors/EcsQueryExecutor.cs
Normal file
7
src/Executors/EcsQueryExecutor.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsQueryExecutor
|
||||
{
|
||||
internal abstract void Destroy();
|
||||
}
|
||||
}
|
72
src/Executors/EcsWhereExecutor.cs
Normal file
72
src/Executors/EcsWhereExecutor.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsWhereExecutor<TSubject> : EcsQueryExecutor where TSubject : EcsSubject
|
||||
{
|
||||
private readonly TSubject _subject;
|
||||
private readonly EcsGroup _filteredGroup;
|
||||
|
||||
private long _executeVersion;
|
||||
|
||||
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
|
||||
|
||||
#region Properties
|
||||
public TSubject Subject => _subject;
|
||||
internal long ExecuteVersion => _executeVersion;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public EcsWhereExecutor(TSubject subject)
|
||||
{
|
||||
_subject = subject;
|
||||
_filteredGroup = EcsGroup.New(subject.World);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public EcsWhereResult<TSubject> Execute() => ExecuteFor(_subject.World.Entities);
|
||||
public EcsWhereResult<TSubject> ExecuteFor(EcsReadonlyGroup sourceGroup)
|
||||
{
|
||||
using (_executeWhere.Auto())
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
|
||||
#endif
|
||||
_subject.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup);
|
||||
return new EcsWhereResult<TSubject>(this, _filteredGroup.Readonly);
|
||||
}
|
||||
}
|
||||
internal sealed override void Destroy()
|
||||
{
|
||||
_filteredGroup.Release();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region WhereExecuter Results
|
||||
public readonly ref struct EcsWhereResult<TSubject> where TSubject : EcsSubject
|
||||
{
|
||||
public readonly TSubject s;
|
||||
private readonly EcsWhereExecutor<TSubject> _executer;
|
||||
public readonly EcsReadonlyGroup group;
|
||||
private readonly long _version;
|
||||
public bool IsRelevant => _version == _executer.ExecuteVersion;
|
||||
|
||||
public EcsWhereResult(EcsWhereExecutor<TSubject> executer, EcsReadonlyGroup group)
|
||||
{
|
||||
_executer = executer;
|
||||
_version = executer.ExecuteVersion;
|
||||
s = executer.Subject;
|
||||
this.group = group;
|
||||
}
|
||||
public EcsGroup.Enumerator GetEnumerator()
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
|
||||
#endif
|
||||
return group.GetEnumerator();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public interface IEcsComponentReset<T>
|
||||
{
|
||||
public void Reset(ref T component);
|
||||
}
|
||||
public static class EcsComponentResetHandler<T>
|
||||
{
|
||||
public static readonly IEcsComponentReset<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsComponentResetHandler()
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
|
||||
{
|
||||
instance = (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
|
||||
isHasHandler = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = new ComponentResetDummyHandler<T>();
|
||||
isHasHandler = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal sealed class ComponentResetDummyHandler<T> : IEcsComponentReset<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset(ref T component) => component = default;
|
||||
}
|
||||
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
|
||||
where T : IEcsComponentReset<T>
|
||||
{
|
||||
private T _fakeInstnace = default;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
|
||||
}
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
using static EcsPoolThrowHalper;
|
||||
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
|
||||
public sealed class EcsAttachPool<T> : EcsPoolBase<T>
|
||||
/// <summary>Pool for IEcsAttachComponent components</summary>
|
||||
public sealed class EcsAttachPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
|
||||
where T : struct, IEcsAttachComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
|
||||
private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID
|
||||
private T[] _items; //sparse
|
||||
private int _count;
|
||||
@ -30,11 +36,17 @@ namespace DCFApixels.DragonECS
|
||||
#region Properites
|
||||
public int Count => _count;
|
||||
public int Capacity => _items.Length;
|
||||
public int ComponentID => _id;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
_poolRunners = new PoolRunners(world.Pipeline);
|
||||
|
||||
_entities = EcsGroup.New(world);
|
||||
@ -45,23 +57,14 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
public void Add(int entityID, EcsEntity target)
|
||||
#region Methods
|
||||
public void Add(int entityID, entlong target)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld)
|
||||
{
|
||||
throw new EcsRelationException();
|
||||
}
|
||||
if (_sanitizeTargetWorld > 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
|
||||
_sanitizeTargetWorld = target.world;
|
||||
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
ref bool entityFlag = ref _entityFlags[entityID];
|
||||
if (entityFlag == false)
|
||||
{
|
||||
@ -72,59 +75,107 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
_items[entityID].Target = target;
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Set(int entityID, EcsEntity target)
|
||||
public void Set(int entityID, entlong target)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld)
|
||||
{
|
||||
throw new EcsRelationException();
|
||||
}
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
|
||||
_sanitizeTargetWorld = target.world;
|
||||
#endif
|
||||
// using (_writeMark.Auto())
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
_items[entityID].Target = target;
|
||||
}
|
||||
public void AddOrSet(int entityID, entlong target)
|
||||
{
|
||||
if (Has(entityID))
|
||||
Set(entityID, target);
|
||||
else
|
||||
Add(entityID, target);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _items[entityID];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _entityFlags[entityID];
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_entities.Remove(entityID);
|
||||
_entityFlags[entityID] = false;
|
||||
_count--;
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID)) Del(entityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
if (Has(toEntityID))
|
||||
Set(toEntityID, Read(fromEntityID).Target);
|
||||
else
|
||||
Add(toEntityID, Read(fromEntityID).Target);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _entityFlags, newSize);
|
||||
Array.Resize(ref _items, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var item in buffer)
|
||||
TryDel(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
ref T IEcsPool<T>.Add(int entityID)
|
||||
{
|
||||
if (!Has(entityID))
|
||||
Add(entityID, entlong.NULL);
|
||||
return ref _items[entityID];
|
||||
}
|
||||
ref T IEcsPool<T>.Write(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _items[entityID];
|
||||
}
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => ((IEcsPool<T>)this).Add(entityID) = (T)dataRaw;
|
||||
object IEcsPool.GetRaw(int entityID) => Read(entityID);
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => ((IEcsPool<T>)this).Write(entityID) = (T)dataRaw;
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IEcsAttachComponent
|
||||
{
|
||||
public EcsEntity Target { get; set; }
|
||||
public entlong Target { get; set; }
|
||||
}
|
||||
public static class EcsAttachComponentPoolExt
|
||||
{
|
||||
@ -133,15 +184,15 @@ namespace DCFApixels.DragonECS
|
||||
return self.GetPool<TAttachComponent, EcsAttachPool<TAttachComponent>>();
|
||||
}
|
||||
|
||||
public static EcsAttachPool<TAttachComponent> Include<TAttachComponent>(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
public static EcsAttachPool<TAttachComponent> Include<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
return self.Include<TAttachComponent, EcsAttachPool<TAttachComponent>>();
|
||||
}
|
||||
public static EcsAttachPool<TAttachComponent> Exclude<TAttachComponent>(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
public static EcsAttachPool<TAttachComponent> Exclude<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
return self.Exclude<TAttachComponent, EcsAttachPool<TAttachComponent>>();
|
||||
}
|
||||
public static EcsAttachPool<TAttachComponent> Optional<TAttachComponent>(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
public static EcsAttachPool<TAttachComponent> Optional<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
|
||||
{
|
||||
return self.Optional<TAttachComponent, EcsAttachPool<TAttachComponent>>();
|
||||
}
|
||||
|
@ -1,27 +1,39 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
|
||||
public sealed class EcsNotNullPool<T> : EcsPoolBase<T>
|
||||
where T : struct, INotNullComponent
|
||||
/// <summary>Pool for IEcsNotNullComponent components</summary>
|
||||
public sealed class EcsNotNullPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
|
||||
where T : struct, IEcsNotNullComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
|
||||
private T[] _items; //sparse
|
||||
private int _count;
|
||||
|
||||
private IEcsComponentReset<T> _componentResetHandler;
|
||||
private IEcsComponentCopy<T> _componentCopyHandler;
|
||||
private PoolRunners _poolRunners;
|
||||
|
||||
#region Properites
|
||||
public int Count => _count;
|
||||
public int Capacity => _items.Length;
|
||||
public int ComponentID => _id;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
_items = new T[world.Capacity];
|
||||
_count = 0;
|
||||
|
||||
@ -30,59 +42,77 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Write(int entityID)
|
||||
{
|
||||
// using (_writeMark.Auto())
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[entityID];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
return ref _items[entityID];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return true;
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _items, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var entityID in buffer)
|
||||
_componentResetHandler.Reset(ref _items[entityID]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
ref T IEcsPool<T>.Add(int entityID) => ref Write(entityID);
|
||||
ref readonly T IEcsPool<T>.Read(int entityID) => ref Read(entityID);
|
||||
ref T IEcsPool<T>.Write(int entityID) => ref Write(entityID);
|
||||
void IEcsPool.Del(int entityID) { }
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
|
||||
object IEcsPool.GetRaw(int entityID) => Write(entityID);
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface INotNullComponent { }
|
||||
/// <summary>
|
||||
/// Not null component. Is present on all entities, without explicit addition and cannot be deleted
|
||||
/// </summary>
|
||||
public interface IEcsNotNullComponent { }
|
||||
public static class EcsNotNullPoolExt
|
||||
{
|
||||
public static EcsNotNullPool<TNotNullComponent> GetPool<TNotNullComponent>(this EcsWorld self) where TNotNullComponent : struct, INotNullComponent
|
||||
public static EcsNotNullPool<TNotNullComponent> GetPool<TNotNullComponent>(this EcsWorld self) where TNotNullComponent : struct, IEcsNotNullComponent
|
||||
{
|
||||
return self.GetPool<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
|
||||
}
|
||||
|
||||
public static EcsNotNullPool<TNotNullComponent> Include<TNotNullComponent>(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
|
||||
public static EcsNotNullPool<TNotNullComponent> Include<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
|
||||
{
|
||||
return self.Include<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
|
||||
}
|
||||
public static EcsNotNullPool<TNotNullComponent> Exclude<TNotNullComponent>(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
|
||||
public static EcsNotNullPool<TNotNullComponent> Exclude<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
|
||||
{
|
||||
return self.Exclude<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
|
||||
}
|
||||
public static EcsNotNullPool<TNotNullComponent> Optional<TNotNullComponent>(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
|
||||
public static EcsNotNullPool<TNotNullComponent> Optional<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
|
||||
{
|
||||
return self.Optional<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsPool<T> : EcsPoolBase<T>
|
||||
using static EcsPoolThrowHalper;
|
||||
/// <summary>Pool for IEcsComponent components</summary>
|
||||
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
|
||||
where T : struct, IEcsComponent
|
||||
{
|
||||
public static string name = typeof(T).Name;
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
@ -18,16 +20,23 @@ namespace DCFApixels.DragonECS
|
||||
private int _recycledItemsCount;
|
||||
|
||||
private IEcsComponentReset<T> _componentResetHandler;
|
||||
private IEcsComponentCopy<T> _componentCopyHandler;
|
||||
private PoolRunners _poolRunners;
|
||||
|
||||
#region Properites
|
||||
public int Count => _itemsCount;
|
||||
public int Capacity => _items.Length;
|
||||
public int ComponentID => _id;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
@ -37,21 +46,59 @@ namespace DCFApixels.DragonECS
|
||||
_itemsCount = 0;
|
||||
|
||||
_componentResetHandler = EcsComponentResetHandler<T>.instance;
|
||||
_componentCopyHandler = EcsComponentCopyHandler<T>.instance;
|
||||
_poolRunners = new PoolRunners(world.Pipeline);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
#region Methods
|
||||
public ref T Add(int entityID)
|
||||
{
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (itemIndex > 0) ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
_itemsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_itemsCount;
|
||||
if (itemIndex >= _items.Length)
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[itemIndex];
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Write(int entityID)
|
||||
{
|
||||
// using (_writeMark.Auto())
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
public ref T TryAddOrWrite(int entityID)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
if (itemIndex <= 0)
|
||||
{
|
||||
if (_recycledItemsCount > 0)
|
||||
@ -65,62 +112,75 @@ namespace DCFApixels.DragonECS
|
||||
if (itemIndex >= _items.Length)
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
|
||||
//_mapping[entityID] = itemIndex; TODO ïðîâåðèòü ÷òî ýòî ëèøíåå äåéñâèå
|
||||
IncrementEntityComponentCount(entityID);
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[itemIndex];
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Write(int entityID)
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
// using (_writeMark.Auto())
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _mapping[entityID] > 0;
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
_componentResetHandler.Reset(ref _items[itemIndex]);
|
||||
if (_recycledItemsCount >= _recycledItems.Length)
|
||||
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
itemIndex = 0;
|
||||
_mapping[entityID] = 0;
|
||||
_itemsCount--;
|
||||
DecrementEntityComponentCount(entityID);
|
||||
this.DecrementEntityComponentCount(entityID);
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID)) Del(entityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
if (Has(toEntityID))
|
||||
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID));
|
||||
else
|
||||
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Add(toEntityID));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var entityID in buffer)
|
||||
TryDel(entityID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw;
|
||||
object IEcsPool.GetRaw(int entityID) => Read(entityID);
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
|
||||
ref readonly T IEcsPool<T>.Read(int entityID) => ref Read(entityID);
|
||||
ref T IEcsPool<T>.Write(int entityID) => ref Write(entityID);
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>Standard component</summary>
|
||||
public interface IEcsComponent { }
|
||||
public static class EcsPoolExt
|
||||
{
|
||||
@ -129,15 +189,15 @@ namespace DCFApixels.DragonECS
|
||||
return self.GetPool<TComponent, EcsPool<TComponent>>();
|
||||
}
|
||||
|
||||
public static EcsPool<TComponent> Include<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
public static EcsPool<TComponent> Include<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Include<TComponent, EcsPool<TComponent>>();
|
||||
}
|
||||
public static EcsPool<TComponent> Exclude<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
public static EcsPool<TComponent> Exclude<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Exclude<TComponent, EcsPool<TComponent>>();
|
||||
}
|
||||
public static EcsPool<TComponent> Optional<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
public static EcsPool<TComponent> Optional<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Optional<TComponent, EcsPool<TComponent>>();
|
||||
}
|
||||
|
@ -1,77 +1,194 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsPoolBase
|
||||
public interface IEcsPool
|
||||
{
|
||||
private int _id = -1;
|
||||
private EcsWorld _world;
|
||||
internal void PreInitInternal(EcsWorld world, int id)
|
||||
{
|
||||
_id = id;
|
||||
_world = world;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void IncrementEntityComponentCount(int entityID)
|
||||
{
|
||||
_world.IncrementEntityComponentCount(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void DecrementEntityComponentCount(int entityID)
|
||||
{
|
||||
_world.DecrementEntityComponentCount(entityID);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public abstract Type ComponentType { get; }
|
||||
public EcsWorld World => _world;
|
||||
public int ComponentID { get; }
|
||||
public Type ComponentType { get; }
|
||||
public EcsWorld World { get; }
|
||||
public int Count { get; }
|
||||
public int Capacity { get; }
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public abstract bool Has(int entityID);
|
||||
|
||||
protected abstract void Init(EcsWorld world);
|
||||
protected abstract void OnWorldResize(int newSize);
|
||||
protected abstract void OnDestroy();
|
||||
#endregion
|
||||
|
||||
#region Internal
|
||||
internal void InvokeInit(EcsWorld world) => Init(world);
|
||||
internal void InvokeOnWorldResize(int newSize) => OnWorldResize(newSize);
|
||||
internal void InvokeOnDestroy() => OnDestroy();
|
||||
bool Has(int entityID);
|
||||
void Del(int entityID);
|
||||
void AddRaw(int entityID, object dataRaw);
|
||||
object GetRaw(int entityID);
|
||||
void SetRaw(int entityID, object dataRaw);
|
||||
void Copy(int fromEntityID, int toEntityID);
|
||||
#endregion
|
||||
}
|
||||
public abstract class EcsPoolBase<T> : EcsPoolBase, IEnumerable<T>
|
||||
public interface IEcsPool<T>
|
||||
{
|
||||
public sealed override Type ComponentType => typeof(T);
|
||||
//Релазиация интерфейса IEnumerator не работает, нужно только чтобы IntelliSense предлагала названия на основе T. Не нашел другого способа
|
||||
#region IEnumerable
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
ref T Add(int entityID);
|
||||
ref readonly T Read(int entityID);
|
||||
ref T Write(int entityID);
|
||||
}
|
||||
|
||||
public struct NullComponent { }
|
||||
public sealed class EcsNullPool : EcsPoolBase
|
||||
/// <summary>Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>.</summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
public interface IEcsPoolImplementation : IEcsPool
|
||||
{
|
||||
public static EcsNullPool instance => new EcsNullPool();
|
||||
|
||||
#region Properties
|
||||
public sealed override Type ComponentType => typeof(NullComponent);
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public sealed override bool Has(int index) => false;
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
protected override void Init(EcsWorld world) { }
|
||||
protected override void OnWorldResize(int newSize) { }
|
||||
protected override void OnDestroy() { }
|
||||
#endregion
|
||||
void OnInit(EcsWorld world, int componentID);
|
||||
void OnWorldResize(int newSize);
|
||||
void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer);
|
||||
void OnWorldDestroy();
|
||||
}
|
||||
/// <summary>Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>.</summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
public interface IEcsPoolImplementation<T> : IEcsPool<T>, IEcsPoolImplementation { }
|
||||
public static class EcsPoolThrowHalper
|
||||
{
|
||||
public static void ThrowAlreadyHasComponent<T>(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"Entity({entityID}) already has component {typeof(T).Name}.");
|
||||
}
|
||||
public static void ThrowNotHaveComponent<T>(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"Entity({entityID}) has no component {typeof(T).Name}.");
|
||||
}
|
||||
public static void ThrowInvalidComponentType<T>(Type invalidType)
|
||||
{
|
||||
throw new EcsFrameworkException($"Invalid component type, {typeof(T).Name} required.");
|
||||
}
|
||||
public static void ThrowWorldDifferent<T>(int entityID)
|
||||
{
|
||||
throw new EcsRelationException($"The world of the target entity({entityID}) in {typeof(T).Name} is not the same as the world of the other entities. ");
|
||||
}
|
||||
}
|
||||
public static class IEcsPoolImplementationExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void IncrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
|
||||
{
|
||||
self.World.IncrementEntityComponentCount(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void DecrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
|
||||
{
|
||||
self.World.DecrementEntityComponentCount(entityID);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrDummy(this IEcsPool self)
|
||||
{
|
||||
return self == null || self is Internal.EcsNullPool;
|
||||
}
|
||||
}
|
||||
|
||||
#region Dummy
|
||||
namespace Internal
|
||||
{
|
||||
public struct NullComponent { }
|
||||
public sealed class EcsNullPool : IEcsPoolImplementation<NullComponent>
|
||||
{
|
||||
public static EcsNullPool instance => new EcsNullPool();
|
||||
|
||||
#region Properties
|
||||
int IEcsPool.ComponentID => throw new NotImplementedException();
|
||||
Type IEcsPool.ComponentType => typeof(NullComponent);
|
||||
EcsWorld IEcsPool.World => throw new NotImplementedException();
|
||||
public int Count => 0;
|
||||
public int Capacity => 0;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
bool IEcsPool.Has(int index) => false;
|
||||
void IEcsPool.Del(int entityID) => throw new NotImplementedException();
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => throw new NotImplementedException();
|
||||
object IEcsPool.GetRaw(int entityID) => throw new NotImplementedException();
|
||||
void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException();
|
||||
void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException();
|
||||
ref NullComponent IEcsPool<NullComponent>.Add(int entityID) => throw new NotImplementedException();
|
||||
ref readonly NullComponent IEcsPool<NullComponent>.Read(int entityID) => throw new NotImplementedException();
|
||||
ref NullComponent IEcsPool<NullComponent>.Write(int entityID) => throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize) { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Reset/Copy interfaces
|
||||
public interface IEcsComponentReset<T>
|
||||
{
|
||||
public void Reset(ref T component);
|
||||
}
|
||||
public static class EcsComponentResetHandler<T>
|
||||
{
|
||||
public static readonly IEcsComponentReset<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsComponentResetHandler()
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
|
||||
{
|
||||
instance = (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
|
||||
isHasHandler = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = new ComponentResetDummyHandler<T>();
|
||||
isHasHandler = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal sealed class ComponentResetDummyHandler<T> : IEcsComponentReset<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset(ref T component) => component = default;
|
||||
}
|
||||
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
|
||||
where T : IEcsComponentReset<T>
|
||||
{
|
||||
private T _fakeInstnace = default;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
|
||||
}
|
||||
|
||||
public interface IEcsComponentCopy<T>
|
||||
{
|
||||
public void Copy(ref T from, ref T to);
|
||||
}
|
||||
public static class EcsComponentCopyHandler<T>
|
||||
{
|
||||
public static readonly IEcsComponentCopy<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsComponentCopyHandler()
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentCopy<>).MakeGenericType(targetType)))
|
||||
{
|
||||
instance = (IEcsComponentCopy<T>)Activator.CreateInstance(typeof(ComponentCopyHandler<>).MakeGenericType(targetType));
|
||||
isHasHandler = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = new ComponentCopyDummyHandler<T>();
|
||||
isHasHandler = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal sealed class ComponentCopyDummyHandler<T> : IEcsComponentCopy<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Copy(ref T from, ref T to) => to = from;
|
||||
}
|
||||
internal sealed class ComponentCopyHandler<T> : IEcsComponentCopy<T>
|
||||
where T : IEcsComponentCopy<T>
|
||||
{
|
||||
private T _fakeInstnace = default;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Copy(ref T from, ref T to) => _fakeInstnace.Copy(ref from, ref to);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
@ -1,139 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsReadonlyPool<T> : EcsPoolBase<T>
|
||||
where T : struct, IEcsReadonlyComponent
|
||||
{
|
||||
public static string name = typeof(T).Name;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
private int _itemsCount;
|
||||
private int[] _recycledItems;
|
||||
private int _recycledItemsCount;
|
||||
|
||||
private IEcsComponentReset<T> _componentResetHandler;
|
||||
private PoolRunners _poolRunners;
|
||||
|
||||
#region Properites
|
||||
public int Count => _itemsCount;
|
||||
public int Capacity => _items.Length;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
{
|
||||
const int capacity = 512;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
_recycledItems = new int[128];
|
||||
_recycledItemsCount = 0;
|
||||
_items = new T[capacity];
|
||||
_itemsCount = 0;
|
||||
|
||||
_componentResetHandler = EcsComponentResetHandler<T>.instance;
|
||||
_poolRunners = new PoolRunners(world.Pipeline);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
public ref T Add(int entityID)
|
||||
{
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
if (itemIndex <= 0)
|
||||
{
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
_itemsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_itemsCount;
|
||||
if (itemIndex >= _items.Length)
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
|
||||
//_mapping[entityID] = itemIndex; TODO проверить что это лишнее дейсвие
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
else
|
||||
{
|
||||
_componentResetHandler.Reset(ref _items[itemIndex]);
|
||||
}
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _items[itemIndex];
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _mapping[entityID] > 0;
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
_componentResetHandler.Reset(ref _items[itemIndex]);
|
||||
if (_recycledItemsCount >= _recycledItems.Length)
|
||||
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
itemIndex = 0;
|
||||
_itemsCount--;
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IEcsReadonlyComponent { }
|
||||
public static class EcsReadonlyPoolExt
|
||||
{
|
||||
public static EcsReadonlyPool<TReadolnyComponent> GetPool<TReadolnyComponent>(this EcsWorld self) where TReadolnyComponent : struct, IEcsReadonlyComponent
|
||||
{
|
||||
return self.GetPool<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
|
||||
}
|
||||
|
||||
public static EcsReadonlyPool<TReadolnyComponent> Include<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
|
||||
{
|
||||
return self.Include<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
|
||||
}
|
||||
public static EcsReadonlyPool<TReadolnyComponent> Exclude<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
|
||||
{
|
||||
return self.Exclude<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
|
||||
}
|
||||
public static EcsReadonlyPool<TReadolnyComponent> Optional<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
|
||||
{
|
||||
return self.Optional<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
|
||||
public sealed class EcsRelationPool<T> : EcsPoolBase<T>
|
||||
where T : struct, IEcsRelationComponent
|
||||
{
|
||||
private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID
|
||||
private T[] _items; //sparse
|
||||
private int _count;
|
||||
private PoolRunners _poolRunners;
|
||||
|
||||
private EcsGroup _entities;
|
||||
public EcsReadonlyGroup Entities => _entities.Readonly;
|
||||
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
private short _sanitizeFirstWorld = -1;
|
||||
private short _sanitizeSecondWorld = -1;
|
||||
#endif
|
||||
|
||||
#region Properites
|
||||
public int Count => _count;
|
||||
public int Capacity => _items.Length;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
{
|
||||
_poolRunners = new PoolRunners(world.Pipeline);
|
||||
|
||||
_entityFlags = new bool[world.Capacity];
|
||||
_items = new T[world.Capacity];
|
||||
_count = 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
public void Add(int entityID, EcsEntity first, EcsEntity second)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) &&
|
||||
(_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld))
|
||||
{
|
||||
throw new EcsRelationException();
|
||||
}
|
||||
#endif
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
ref bool entityFlag = ref _entityFlags[entityID];
|
||||
if (entityFlag == false)
|
||||
{
|
||||
entityFlag = true;
|
||||
_count++;
|
||||
_entities.Add(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
_items[entityID].Set(first, second);
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Set(int entityID, EcsEntity first, EcsEntity second)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if ((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) &&
|
||||
(_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld))
|
||||
{
|
||||
throw new EcsRelationException();
|
||||
}
|
||||
_sanitizeFirstWorld = first.world;
|
||||
_sanitizeSecondWorld = second.world;
|
||||
#endif
|
||||
// using (_writeMark.Auto())
|
||||
//{
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
_items[entityID].Set(first, second);
|
||||
//}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
return ref _items[entityID];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _entityFlags[entityID];
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
_entities.Remove(entityID);
|
||||
_entityFlags[entityID] = false;
|
||||
_count--;
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _entityFlags, newSize);
|
||||
Array.Resize(ref _items, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IEcsRelationComponent
|
||||
{
|
||||
public EcsEntity First { get; set; }
|
||||
public EcsEntity Second { get; set; }
|
||||
}
|
||||
public static class IEcsRelationComponentExt
|
||||
{
|
||||
public static void Set<T>(this ref T self, EcsEntity first, EcsEntity second) where T : struct, IEcsRelationComponent
|
||||
{
|
||||
self.First = first;
|
||||
self.Second = second;
|
||||
}
|
||||
}
|
||||
public static class EcsRelationPoolExt
|
||||
{
|
||||
public static EcsRelationPool<TRelationComponent> GetPool<TRelationComponent>(this EcsWorld self) where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
return self.GetPool<TRelationComponent, EcsRelationPool<TRelationComponent>>();
|
||||
}
|
||||
|
||||
public static EcsRelationPool<TRelationComponent> Include<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
return self.Include<TRelationComponent, EcsRelationPool<TRelationComponent>>();
|
||||
}
|
||||
public static EcsRelationPool<TRelationComponent> Exclude<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
return self.Exclude<TRelationComponent, EcsRelationPool<TRelationComponent>>();
|
||||
}
|
||||
public static EcsRelationPool<TRelationComponent> Optional<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
|
||||
{
|
||||
return self.Optional<TRelationComponent, EcsRelationPool<TRelationComponent>>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,17 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsSinglePool<T> : EcsPoolBase<T>
|
||||
using static EcsPoolThrowHalper;
|
||||
public sealed class EcsSinglePool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
|
||||
where T : struct, IEcsSingleComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
|
||||
private int[] _mapping;
|
||||
|
||||
private int _count;
|
||||
@ -21,77 +26,112 @@ namespace DCFApixels.DragonECS
|
||||
get => ref _component;
|
||||
}
|
||||
public int Count => _count;
|
||||
int IEcsPool.Capacity => -1;
|
||||
public int ComponentID => _id;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
_count = 0;
|
||||
_poolRunners = new PoolRunners(world.Pipeline);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write/Read/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
|
||||
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
#region Methods
|
||||
public ref T Add(int entityID)
|
||||
{
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
if (_mapping[entityID] <= 0)
|
||||
{
|
||||
_mapping[entityID] = ++_count;
|
||||
IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
_mapping[entityID] = ++_count;
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
return ref _component;
|
||||
// }
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Write(int entityID)
|
||||
{
|
||||
// using (_writeMark.Auto())
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_poolRunners.write.OnComponentWrite<T>(entityID);
|
||||
return ref _component;
|
||||
}
|
||||
public ref T TryAddOrWrite(int entityID)
|
||||
{
|
||||
if (!Has(entityID))
|
||||
return ref Add(entityID);
|
||||
return ref Write(entityID);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
// using (_readMark.Auto())
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _component;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _mapping[entityID] > 0;
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_mapping[entityID] = 0;
|
||||
_count--;
|
||||
DecrementEntityComponentCount(entityID);
|
||||
this.DecrementEntityComponentCount(entityID);
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID)) Del(entityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
TryAddOrWrite(toEntityID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var entityID in buffer)
|
||||
TryDel(entityID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Instance = (T)dataRaw;
|
||||
object IEcsPool.GetRaw(int entityID) => Instance;
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => Instance = (T)dataRaw;
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
/// <summary> Singleton component </summary>
|
||||
/// <summary>Singleton component</summary>
|
||||
public interface IEcsSingleComponent { }
|
||||
public static class EcsSinglePoolExt
|
||||
{
|
||||
@ -101,15 +141,15 @@ namespace DCFApixels.DragonECS
|
||||
return self.GetPool<TSingleComponent, EcsSinglePool<TSingleComponent>>();
|
||||
}
|
||||
|
||||
public static EcsSinglePool<TSingleComponent> Include<TSingleComponent>(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
public static EcsSinglePool<TSingleComponent> Include<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
{
|
||||
return self.Include<TSingleComponent, EcsSinglePool<TSingleComponent>>();
|
||||
}
|
||||
public static EcsSinglePool<TSingleComponent> Exclude<TSingleComponent>(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
public static EcsSinglePool<TSingleComponent> Exclude<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
{
|
||||
return self.Exclude<TSingleComponent, EcsSinglePool<TSingleComponent>>();
|
||||
}
|
||||
public static EcsSinglePool<TSingleComponent> Optional<TSingleComponent>(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
public static EcsSinglePool<TSingleComponent> Optional<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
|
||||
{
|
||||
return self.Optional<TSingleComponent, EcsSinglePool<TSingleComponent>>();
|
||||
}
|
||||
|
@ -1,24 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsTagPool<T> : EcsPoolBase<T>
|
||||
using static EcsPoolThrowHalper;
|
||||
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
|
||||
where T : struct, IEcsTagComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
|
||||
private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private int _count;
|
||||
|
||||
private PoolRunners _poolRunners;
|
||||
|
||||
private T _fakeComponent;
|
||||
|
||||
#region Properites
|
||||
public int Count => _count;
|
||||
int IEcsPool.Capacity => -1;
|
||||
public int ComponentID => _id;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
protected override void Init(EcsWorld world)
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
_mapping = new bool[world.Capacity];
|
||||
_count = 0;
|
||||
|
||||
@ -26,50 +40,125 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add/Has/Del
|
||||
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
|
||||
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
|
||||
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
|
||||
#region Method
|
||||
public void Add(int entityID)
|
||||
{
|
||||
// using (_addMark.Auto())
|
||||
// {
|
||||
if (_mapping[entityID] == false)
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
_count++;
|
||||
_mapping[entityID] = true;
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
public void TryAdd(int entityID)
|
||||
{
|
||||
if (!_mapping[entityID])
|
||||
{
|
||||
_count++;
|
||||
_mapping[entityID] = true;
|
||||
IncrementEntityComponentCount(entityID);
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_poolRunners.add.OnComponentAdd<T>(entityID);
|
||||
}
|
||||
// }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sealed override bool Has(int entityID)
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
// using (_hasMark.Auto())
|
||||
return _mapping[entityID];
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
// using (_delMark.Auto())
|
||||
// {
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_mapping[entityID] = false;
|
||||
_count--;
|
||||
DecrementEntityComponentCount(entityID);
|
||||
this.DecrementEntityComponentCount(entityID);
|
||||
_poolRunners.del.OnComponentDel<T>(entityID);
|
||||
// }
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID)) Del(entityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
TryAdd(toEntityID);
|
||||
}
|
||||
public void Set(int entityID, bool isHas)
|
||||
{
|
||||
if (isHas)
|
||||
{
|
||||
if (!Has(entityID))
|
||||
Add(entityID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Has(entityID))
|
||||
Del(entityID);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WorldCallbacks
|
||||
protected override void OnWorldResize(int newSize)
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
}
|
||||
protected override void OnDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var entityID in buffer)
|
||||
TryDel(entityID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
ref T IEcsPool<T>.Add(int entityID)
|
||||
{
|
||||
Add(entityID);
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
ref readonly T IEcsPool<T>.Read(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
ref T IEcsPool<T>.Write(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID);
|
||||
object IEcsPool.GetRaw(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return _fakeComponent;
|
||||
}
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>Component without data</summary>
|
||||
public interface IEcsTagComponent { }
|
||||
public static class EcsTagPoolExt
|
||||
{
|
||||
@ -78,15 +167,15 @@ namespace DCFApixels.DragonECS
|
||||
return self.GetPool<TTagComponent, EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Include<TTagComponent, EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Exclude<TTagComponent, EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Optional<TTagComponent, EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS.Internal
|
||||
{
|
||||
public abstract class EcsRelationTableArchetypeBase
|
||||
{
|
||||
public EcsRelationTableArchetypeBase()
|
||||
{
|
||||
throw new TypeAccessException("Сreating instances of EcsRelationTableArchetype class is not available.");
|
||||
}
|
||||
}
|
||||
public sealed class EcsRelationTableArchetype<TLeftWorld, TRightWorld> : EcsRelationTableArchetypeBase
|
||||
where TLeftWorld : EcsWorld<TLeftWorld>
|
||||
where TRightWorld : EcsWorld<TRightWorld>
|
||||
{ }
|
||||
}
|
@ -46,10 +46,10 @@ namespace DCFApixels.DragonECS
|
||||
/// <returns> new node index</returns>
|
||||
public int Insert(int nodeIndex, int entityID)
|
||||
{
|
||||
_nodes[_count].Set(entityID, _nodes[nodeIndex].next);
|
||||
_nodes[++_count].Set(entityID, _nodes[nodeIndex].next);
|
||||
_nodes[nodeIndex].next = _count;
|
||||
_lastNodeIndex = _count;
|
||||
return _count++;
|
||||
return _count;
|
||||
}
|
||||
|
||||
public int Add(int entityID) => Insert(_lastNodeIndex, entityID);
|
||||
|
169
src/entlong.cs
Normal file
169
src/entlong.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
using static entlong.ThrowHalper;
|
||||
// uniqueID - 32 bits
|
||||
// gen - 16 bits
|
||||
// world - 16 bits
|
||||
/// <summary>Strong identifier/Permanent entity identifier</summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
|
||||
public readonly struct entlong : IEquatable<long>, IEquatable<entlong>
|
||||
{
|
||||
public static readonly entlong NULL = default;
|
||||
[FieldOffset(0)]
|
||||
internal readonly long full; //Union
|
||||
[FieldOffset(0)]
|
||||
internal readonly int id;
|
||||
[FieldOffset(4)]
|
||||
internal readonly short gen;
|
||||
[FieldOffset(6)]
|
||||
internal readonly short world;
|
||||
|
||||
#region Properties
|
||||
public bool IsAlive
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => EcsWorld.Worlds[world].IsAlive(id, gen);
|
||||
}
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this == NULL;
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool IsNotNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this != NULL;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int ID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return id;
|
||||
}
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public short Gen
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return gen;
|
||||
}
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return EcsWorld.Worlds[world];
|
||||
}
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public short WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return world;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(int id, short gen, short world) : this()
|
||||
{
|
||||
this.id = id;
|
||||
this.gen = gen;
|
||||
this.world = world;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal entlong(long full) : this()
|
||||
{
|
||||
this.full = full;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryGetters
|
||||
public bool TryGetID(out int id)
|
||||
{
|
||||
id = this.id;
|
||||
return IsAlive;
|
||||
}
|
||||
public bool TryGetWorld(out EcsWorld world)
|
||||
{
|
||||
world = EcsWorld.Worlds[this.world];
|
||||
return IsAlive;
|
||||
}
|
||||
public bool TryUnpack(out EcsWorld world, out int id)
|
||||
{
|
||||
world = EcsWorld.Worlds[this.world];
|
||||
id = this.id;
|
||||
return IsAlive;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(entlong other) => full == other.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(long other) => full == other;
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsAlive ? "alive" : "not alive")})";
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj) => obj is entlong other && full == other.full;
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(in entlong a, in entlong b) => a.full == b.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(in entlong a, in entlong b) => a.full != b.full;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(in entlong a) => a.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator entlong(in long a) => new entlong(a);
|
||||
#endregion
|
||||
|
||||
#region ThrowHalper
|
||||
internal static class ThrowHalper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsNotAlive(entlong entity)
|
||||
{
|
||||
if (entity.IsNull)
|
||||
throw new EcsFrameworkException("The entity identifier is null.");
|
||||
else
|
||||
throw new EcsFrameworkException("The entity is not alive.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user