DragonECS/src/EcsAspect.cs

577 lines
22 KiB
C#
Raw Normal View History

2024-01-07 18:52:54 +08:00
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.Utils;
2023-06-26 02:53:55 +08:00
using System;
using System.Collections.Generic;
2023-05-27 22:15:25 +08:00
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
2023-06-22 14:31:13 +08:00
public abstract class EcsAspect
{
2024-01-07 18:52:54 +08:00
internal EcsWorld _source;
internal EcsMask _mask;
private bool _isInit;
2024-01-07 18:52:54 +08:00
internal UnsafeArray<int> _sortIncBuffer;
internal UnsafeArray<int> _sortExcBuffer;
internal UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
internal UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
#region Properties
2024-01-07 18:52:54 +08:00
public EcsMask Mask => _mask;
public EcsWorld World => _source;
public bool IsInit => _isInit;
#endregion
#region Methods
2024-01-07 18:52:54 +08:00
public bool IsMatches(int entityID) => _source.IsMatchesMask(_mask, entityID);
#endregion
#region Builder
protected virtual void Init(Builder b) { }
2023-06-22 14:31:13 +08:00
public sealed class Builder : EcsAspectBuilderBase
{
private EcsWorld _world;
2023-11-22 19:05:00 +08:00
private HashSet<int> _inc;
private HashSet<int> _exc;
2023-06-22 14:31:13 +08:00
private List<Combined> _combined;
public EcsWorld World => _world;
private Builder(EcsWorld world)
{
_world = world;
2023-06-22 14:31:13 +08:00
_combined = new List<Combined>();
2023-11-22 19:05:00 +08:00
_inc = new HashSet<int>();
_exc = new HashSet<int>();
}
2024-01-07 18:52:54 +08:00
internal static unsafe TAspect Build<TAspect>(EcsWorld world) where TAspect : EcsAspect
{
Builder builder = new Builder(world);
2023-06-22 14:31:13 +08:00
Type aspectType = typeof(TAspect);
ConstructorInfo constructorInfo = aspectType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
EcsAspect newAspect;
if (constructorInfo != null)
{
2023-06-22 14:31:13 +08:00
newAspect = (EcsAspect)constructorInfo.Invoke(new object[] { builder });
}
else
{
2023-06-22 14:31:13 +08:00
newAspect = (EcsAspect)Activator.CreateInstance(typeof(TAspect));
newAspect.Init(builder);
}
2024-01-07 18:52:54 +08:00
newAspect._source = world;
builder.End(out newAspect._mask);
2023-06-22 14:31:13 +08:00
newAspect._isInit = true;
2024-01-07 18:52:54 +08:00
newAspect._sortIncBuffer = new UnsafeArray<int>(newAspect._mask.inc.Length, true);
newAspect._sortExcBuffer = new UnsafeArray<int>(newAspect._mask.exc.Length, true);
newAspect._sortIncChunckBuffer = new UnsafeArray<EcsMaskChunck>(newAspect._mask.incChunckMasks.Length, true);
newAspect._sortExcChunckBuffer = new UnsafeArray<EcsMaskChunck>(newAspect._mask.excChunckMasks.Length, true);
for (int i = 0; i < newAspect._sortIncBuffer.Length; i++)
{
newAspect._sortIncBuffer.ptr[i] = newAspect._mask.inc[i];
}
for (int i = 0; i < newAspect._sortExcBuffer.Length; i++)
{
newAspect._sortExcBuffer.ptr[i] = newAspect._mask.exc[i];
}
for (int i = 0; i < newAspect._sortIncChunckBuffer.Length; i++)
{
newAspect._sortIncChunckBuffer.ptr[i] = newAspect._mask.incChunckMasks[i];
}
for (int i = 0; i < newAspect._sortExcChunckBuffer.Length; i++)
{
newAspect._sortExcChunckBuffer.ptr[i] = newAspect._mask.excChunckMasks[i];
}
2023-06-22 14:31:13 +08:00
return (TAspect)newAspect;
}
2023-05-28 05:53:08 +08:00
#region Include/Exclude/Optional
public sealed override TPool Include<TPool>()
{
IncludeImplicit(typeof(TPool).GetGenericArguments()[0]);
return _world.GetPool<TPool>();
}
public sealed override TPool Exclude<TPool>()
{
ExcludeImplicit(typeof(TPool).GetGenericArguments()[0]);
return _world.GetPool<TPool>();
}
public sealed override TPool Optional<TPool>()
{
return _world.GetPool<TPool>();
}
private void IncludeImplicit(Type type)
{
int id = _world.GetComponentID(type);
2023-11-22 19:05:00 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
#endif
_inc.Add(id);
}
private void ExcludeImplicit(Type type)
{
int id = _world.GetComponentID(type);
2023-11-22 19:05:00 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
#endif
_exc.Add(id);
}
2023-05-28 05:53:08 +08:00
#endregion
#region Combine
2023-06-22 14:31:13 +08:00
public TOtherAspect Combine<TOtherAspect>(int order = 0) where TOtherAspect : EcsAspect
2023-06-03 01:58:54 +08:00
{
2023-06-22 14:31:13 +08:00
var result = _world.GetAspect<TOtherAspect>();
_combined.Add(new Combined(result, order));
2023-06-03 01:58:54 +08:00
return result;
}
#endregion
2023-06-22 02:02:43 +08:00
public EcsWorldCmp<T> GetWorldData<T>() where T : struct
{
return new EcsWorldCmp<T>(_world.id);
}
private void End(out EcsMask mask)
{
2023-11-22 19:05:00 +08:00
HashSet<int> maskInc;
HashSet<int> maskExc;
2023-06-22 14:31:13 +08:00
if (_combined.Count > 0)
2023-06-05 22:09:34 +08:00
{
2023-11-22 19:05:00 +08:00
maskInc = new HashSet<int>();
maskExc = new HashSet<int>();
_combined.Sort((a, b) => a.order - b.order);
foreach (var item in _combined)
{
2024-01-07 18:52:54 +08:00
EcsMask submask = item.aspect._mask;
2023-11-22 19:05:00 +08:00
maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения
maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения
maskInc.UnionWith(submask.inc);
maskExc.UnionWith(submask.exc);
}
maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения
maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения
maskInc.UnionWith(_inc);
maskExc.UnionWith(_exc);
2023-06-05 22:59:34 +08:00
}
else
{
maskInc = _inc;
maskExc = _exc;
2023-06-05 22:09:34 +08:00
}
2023-11-22 19:05:00 +08:00
Dictionary<int, int> r = new Dictionary<int, int>();
foreach (var id in maskInc)
{
2024-01-07 18:52:54 +08:00
var bit = EcsMaskChunck.FromID(id);
2023-12-20 23:21:10 +08:00
if (!r.TryAdd(bit.chankIndex, bit.mask))
2023-11-22 19:05:00 +08:00
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
}
2024-01-07 18:52:54 +08:00
EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
2023-11-22 19:05:00 +08:00
r.Clear();
foreach (var id in maskExc)
{
2024-01-07 18:52:54 +08:00
var bit = EcsMaskChunck.FromID(id);
2023-11-22 19:05:00 +08:00
if (!r.TryAdd(bit.chankIndex, bit.mask))
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
}
2024-01-07 18:52:54 +08:00
EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
2023-12-20 23:21:10 +08:00
2023-11-22 19:05:00 +08:00
var inc = maskInc.ToArray();
Array.Sort(inc);
var exc = maskExc.ToArray();
Array.Sort(exc);
2024-01-07 18:52:54 +08:00
mask = new EcsMask(0, _world.id, inc, exc, incMasks, excMasks);
_world = null;
_inc = null;
_exc = null;
}
2023-05-28 05:53:08 +08:00
#region SupportReflectionHack
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
private void SupportReflectionHack<TPool>() where TPool : IEcsPoolImplementation, new()
2023-05-28 05:53:08 +08:00
{
Include<TPool>();
Exclude<TPool>();
Optional<TPool>();
IncludeImplicit(null);
ExcludeImplicit(null);
2023-05-28 05:53:08 +08:00
}
#endregion
}
#endregion
2024-01-07 18:52:54 +08:00
#region Destructor
unsafe ~EcsAspect()
{
_sortIncBuffer.Dispose();
_sortExcBuffer.Dispose();
_sortIncChunckBuffer.Dispose();
_sortExcChunckBuffer.Dispose();
}
#endregion
2023-06-02 04:00:08 +08:00
#region Iterator
2023-06-22 14:31:13 +08:00
public EcsAspectIterator GetIterator()
{
2024-01-07 18:52:54 +08:00
return new EcsAspectIterator(this, _source.Entities);
}
public EcsAspectIterator GetIteratorFor(EcsSpan span)
{
return new EcsAspectIterator(this, span);
}
2023-06-02 04:00:08 +08:00
#endregion
2023-06-05 22:09:34 +08:00
2024-01-07 18:52:54 +08:00
#region Combined
2023-06-25 23:13:51 +08:00
private readonly struct Combined
2023-06-05 22:09:34 +08:00
{
2023-06-25 23:13:51 +08:00
public readonly EcsAspect aspect;
public readonly int order;
2023-06-22 14:31:13 +08:00
public Combined(EcsAspect aspect, int order)
2023-06-05 22:09:34 +08:00
{
2023-06-22 14:31:13 +08:00
this.aspect = aspect;
2023-06-05 22:09:34 +08:00
this.order = order;
}
}
2024-01-07 18:52:54 +08:00
#endregion
}
#region BuilderBase
2023-06-22 14:31:13 +08:00
public abstract class EcsAspectBuilderBase
{
public abstract TPool Include<TPool>() where TPool : IEcsPoolImplementation, new();
public abstract TPool Exclude<TPool>() where TPool : IEcsPoolImplementation, new();
public abstract TPool Optional<TPool>() where TPool : IEcsPoolImplementation, new();
}
#endregion
#region Iterator
2023-06-22 14:31:13 +08:00
public ref struct EcsAspectIterator
{
2023-12-24 15:40:19 +08:00
public readonly int worldID;
2024-01-07 18:52:54 +08:00
public readonly EcsAspect aspect;
private EcsSpan _span;
public EcsAspectIterator(EcsAspect aspect, EcsSpan span)
{
2023-12-24 15:40:19 +08:00
worldID = aspect.World.id;
_span = span;
2024-01-07 18:52:54 +08:00
this.aspect = aspect;
}
public void CopyTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
while (enumerator.MoveNext())
2023-05-26 00:24:38 +08:00
group.AddInternal(enumerator.Current);
}
2023-12-24 15:40:19 +08:00
public int CopyTo(ref int[] array)
{
var enumerator = GetEnumerator();
int count = 0;
while (enumerator.MoveNext())
{
if(array.Length <= count)
Array.Resize(ref array, array.Length << 1);
array[count++] = enumerator.Current;
}
return count;
}
public EcsSpan CopyToSpan(ref int[] array)
{
var enumerator = GetEnumerator();
int count = 0;
while (enumerator.MoveNext())
{
if (array.Length <= count)
Array.Resize(ref array, array.Length << 1);
array[count++] = enumerator.Current;
}
return new EcsSpan(worldID, array, count);
}
2023-06-02 04:00:08 +08:00
#region object
public override string ToString()
{
2024-01-07 18:52:54 +08:00
List<int> ints = new List<int>();
foreach (var e in this)
{
2024-01-07 18:52:54 +08:00
ints.Add(e);
}
2024-01-07 18:52:54 +08:00
return CollectionUtility.EntitiesToString(ints, "it");
}
2023-06-02 04:00:08 +08:00
#endregion
#region Enumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-01-07 18:52:54 +08:00
public Enumerator GetEnumerator() => new Enumerator(_span, aspect);
public unsafe ref struct Enumerator
{
2024-01-07 18:52:54 +08:00
private readonly struct IncCountComparer : IComparerX<int>
{
public readonly int[] counts;
public IncCountComparer(int[] counts)
{
this.counts = counts;
}
public int Compare(int a, int b)
{
return counts[a] - counts[b];
}
}
private readonly struct ExcCountComparer : IComparerX<int>
{
public readonly int[] counts;
public ExcCountComparer(int[] counts)
{
this.counts = counts;
}
public int Compare(int a, int b)
{
return counts[b] - counts[a];
}
}
private ReadOnlySpan<int>.Enumerator _span;
2023-11-22 17:35:03 +08:00
private readonly int[][] _entitiesComponentMasks;
2024-01-07 18:52:54 +08:00
private static EcsMaskChunck* _preSortedIncBuffer;
private static EcsMaskChunck* _preSortedExcBuffer;
2024-01-01 21:44:33 +08:00
2024-01-07 18:52:54 +08:00
private UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
private UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
2024-01-07 18:52:54 +08:00
private EcsAspect aspect;
public unsafe Enumerator(EcsSpan span, EcsAspect aspect)
{
2024-01-07 18:52:54 +08:00
this.aspect = aspect;
_span = span.GetEnumerator();
_entitiesComponentMasks = span.World._entitiesComponentMasks;
2024-01-01 21:44:33 +08:00
2024-01-07 18:52:54 +08:00
EcsMask mask = aspect.Mask;
UnsafeArray<int> _sortIncBuffer = aspect._sortIncBuffer;
UnsafeArray<int> _sortExcBuffer = aspect._sortExcBuffer;
_sortIncChunckBuffer = aspect._sortIncChunckBuffer;
_sortExcChunckBuffer = aspect._sortExcChunckBuffer;
2024-01-01 21:44:33 +08:00
int[] counts = mask.World._poolComponentCounts;
2024-01-07 18:52:54 +08:00
IncCountComparer incComparer = new IncCountComparer(counts);
ExcCountComparer excComparer = new ExcCountComparer(counts);
#region Sort
UnsafeArraySortHalperX<int>.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer);
UnsafeArraySortHalperX<int>.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer);
//if (_sortIncBuffer.Length > 1)
//{
// //if (_sortIncBufferLength == 2)
// //{
// // if (counts[_sortIncBuffer[0]] > counts[_sortIncBuffer[1]])
// // {
// // int tmp = _sortIncBuffer[0];
// // _sortIncBuffer[0] = _sortIncBuffer[1];
// // _sortIncBuffer[1] = tmp;
// // }
// // //...
// //}
// //else
// {
// for (int i = 0, n = _sortIncBuffer.Length - 1; i < n; i++)
// {
// //int counti = counts[_sortIncBuffer[i]];
// //if (counti <= 0)
// //{
// // _span = ReadOnlySpan<int>.Empty.GetEnumerator();
// // goto skip1;
// //}
// bool noSwaped = true;
// for (int j = 0; j < n - i; )
// {
// ref int j0 = ref _sortIncBuffer.ptr[j++];
// if (counts[j0] > counts[_sortIncBuffer.ptr[j]])
// {
// int tmp = _sortIncBuffer.ptr[j];
// _sortIncBuffer.ptr[j] = j0;
// j0 = tmp;
// noSwaped = false;
// }
// }
// if (noSwaped)
// break;
// }
// }
//}
//skip1:;
//if (_sortExcBuffer.Length > 1)
//{
// //if (_sortExcBufferLength == 2)
// //{
// // if (counts[_sortExcBuffer[0]] < counts[_sortExcBuffer[1]])
// // {
// // int tmp = _sortExcBuffer[0];
// // _sortExcBuffer[0] = _sortExcBuffer[1];
// // _sortExcBuffer[1] = tmp;
// // }
// // //...
// //}
// //else
// {
// for (int i = 0, n = _sortExcBuffer.Length - 1; i < n; i++)
// {
// //int counti = counts[_sortExcBuffer[i]];
// //if (counti <= 0)
// //{
// // _excChunckMasks = ReadOnlySpan<EcsMaskBit>.Empty;
// // goto skip2;
// //}
// bool noSwaped = true;
// for (int j = 0; j < n - i;)
// {
// ref int j0 = ref _sortExcBuffer.ptr[j++];
// if (counts[j0] < counts[_sortExcBuffer.ptr[j]])
// {
// int tmp = _sortExcBuffer.ptr[j];
// _sortExcBuffer.ptr[j] = j0;
// j0 = tmp;
// noSwaped = false;
// }
// }
// if (noSwaped)
// break;
// }
// }
//}
//skip2:;
if (_preSortedIncBuffer == null)
{
2024-01-07 18:52:54 +08:00
_preSortedIncBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
_preSortedExcBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
}
2024-01-07 18:52:54 +08:00
for (int i = 0; i < _sortIncBuffer.Length; i++)
{
2024-01-07 18:52:54 +08:00
_preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]);
}
2024-01-07 18:52:54 +08:00
for (int i = 0; i < _sortExcBuffer.Length; i++)
{
2024-01-07 18:52:54 +08:00
_preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]);
}
2024-01-07 18:52:54 +08:00
//int _sortedIncBufferLength = mask.inc.Length;
//int _sortedExcBufferLength = mask.exc.Length;
2024-01-07 18:52:54 +08:00
//if (_sortIncChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна
if (_sortIncBuffer.Length > 1)
{
2024-01-07 18:52:54 +08:00
for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++)
{
2024-01-07 18:52:54 +08:00
EcsMaskChunck bas = _preSortedIncBuffer[i];
int chankIndexX = bas.chankIndex;
int maskX = bas.mask;
2024-01-07 18:52:54 +08:00
for (int j = i + 1; j < _sortIncBuffer.Length; j++)
{
2024-01-07 18:52:54 +08:00
if (_preSortedIncBuffer[j].chankIndex == chankIndexX)
{
2024-01-07 18:52:54 +08:00
maskX |= _preSortedIncBuffer[j].mask;
}
}
2024-01-07 18:52:54 +08:00
_sortIncChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
while (++i < _sortIncBuffer.Length && _preSortedIncBuffer[i].chankIndex == chankIndexX)
{
2024-01-07 18:52:54 +08:00
// skip
}
}
}
2024-01-07 18:52:54 +08:00
else
{
_sortIncChunckBuffer.ptr[0] = _preSortedIncBuffer[0];
}
2024-01-07 18:52:54 +08:00
//if (_sortExcChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна
if (_sortExcBuffer.Length > 1)
{
2024-01-07 18:52:54 +08:00
for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++)
{
2024-01-07 18:52:54 +08:00
EcsMaskChunck bas = _preSortedExcBuffer[i];
int chankIndexX = bas.chankIndex;
int maskX = bas.mask;
2024-01-07 18:52:54 +08:00
for (int j = i + 1; j < _sortExcBuffer.Length; j++)
{
2024-01-07 18:52:54 +08:00
if (_preSortedExcBuffer[j].chankIndex == chankIndexX)
{
2024-01-07 18:52:54 +08:00
maskX |= _preSortedExcBuffer[j].mask;
}
}
2024-01-07 18:52:54 +08:00
_sortExcChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
while (++i < _sortExcBuffer.Length && _preSortedExcBuffer[i].chankIndex == chankIndexX)
{
2024-01-07 18:52:54 +08:00
// skip
}
}
}
2024-01-07 18:52:54 +08:00
else
{
_sortExcChunckBuffer.ptr[0] = _preSortedExcBuffer[0];
}
#endregion
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _span.Current;
}
2024-01-07 18:52:54 +08:00
public entlong CurrentLong
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => aspect.World.GetEntityLong(_span.Current);
}
2023-11-22 17:35:03 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
while (_span.MoveNext())
{
int e = _span.Current;
2024-01-07 18:52:54 +08:00
for (int i = 0; i < _sortIncChunckBuffer.Length; i++)
2023-11-22 17:35:03 +08:00
{
2024-01-07 18:52:54 +08:00
var bit = _sortIncChunckBuffer.ptr[i];
2024-01-01 21:44:33 +08:00
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
goto skip;
2023-11-22 17:35:03 +08:00
}
2024-01-07 18:52:54 +08:00
for (int i = 0; i < _sortExcChunckBuffer.Length; i++)
2023-11-22 17:35:03 +08:00
{
2024-01-07 18:52:54 +08:00
var bit = _sortExcChunckBuffer.ptr[i];
2024-01-01 21:44:33 +08:00
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
goto skip;
2023-11-22 17:35:03 +08:00
}
return true;
2023-11-22 17:35:03 +08:00
skip: continue;
}
return false;
}
}
2023-06-02 04:00:08 +08:00
#endregion
}
#endregion
}