mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 18:14:37 +08:00
update sorting & masks
This commit is contained in:
parent
be2ecb8c07
commit
b34f8add00
@ -721,7 +721,7 @@ namespace DCFApixels.DragonECS
|
||||
#region Other
|
||||
public override string ToString()
|
||||
{
|
||||
return EntitiesCollectionUtility.AutoToString(_dense.Skip(1).Take(_count), "group");
|
||||
return CollectionUtility.EntitiesToString(_dense.Skip(1).Take(_count), "group");
|
||||
//return $"group({_count}) {{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}";
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
@ -122,7 +122,7 @@ namespace DCFApixels.DragonECS
|
||||
#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член
|
||||
public override string ToString()
|
||||
{
|
||||
return EntitiesCollectionUtility.AutoToString(_values.ToArray(), "span");
|
||||
return CollectionUtility.EntitiesToString(_values.ToArray(), "span");
|
||||
//return $"span({_values.Length}) {{{string.Join(", ", _values.ToArray().OrderBy(o => o))}}}";
|
||||
}
|
||||
|
||||
|
731
src/EcsAspect.cs
731
src/EcsAspect.cs
@ -1,32 +1,32 @@
|
||||
//#define sort_1
|
||||
#define lockSwapCheck
|
||||
// Зеленый свет, тесты показали идентичную скорость или даже прирост
|
||||
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using DCFApixels.DragonECS.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsAspect
|
||||
{
|
||||
internal EcsWorld source;
|
||||
internal EcsMask mask;
|
||||
internal EcsWorld _source;
|
||||
internal EcsMask _mask;
|
||||
private bool _isInit;
|
||||
|
||||
internal UnsafeArray<int> _sortIncBuffer;
|
||||
internal UnsafeArray<int> _sortExcBuffer;
|
||||
internal UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
||||
internal UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
||||
|
||||
#region Properties
|
||||
public EcsMask Mask => mask;
|
||||
public EcsWorld World => source;
|
||||
public EcsMask Mask => _mask;
|
||||
public EcsWorld World => _source;
|
||||
public bool IsInit => _isInit;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public bool IsMatches(int entityID) => source.IsMatchesMask(mask, entityID);
|
||||
public bool IsMatches(int entityID) => _source.IsMatchesMask(_mask, entityID);
|
||||
#endregion
|
||||
|
||||
#region Builder
|
||||
@ -47,7 +47,7 @@ namespace DCFApixels.DragonECS
|
||||
_inc = new HashSet<int>();
|
||||
_exc = new HashSet<int>();
|
||||
}
|
||||
internal static TAspect Build<TAspect>(EcsWorld world) where TAspect : EcsAspect
|
||||
internal static unsafe TAspect Build<TAspect>(EcsWorld world) where TAspect : EcsAspect
|
||||
{
|
||||
Builder builder = new Builder(world);
|
||||
Type aspectType = typeof(TAspect);
|
||||
@ -62,9 +62,33 @@ namespace DCFApixels.DragonECS
|
||||
newAspect = (EcsAspect)Activator.CreateInstance(typeof(TAspect));
|
||||
newAspect.Init(builder);
|
||||
}
|
||||
newAspect.source = world;
|
||||
builder.End(out newAspect.mask);
|
||||
newAspect._source = world;
|
||||
builder.End(out newAspect._mask);
|
||||
newAspect._isInit = true;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
return (TAspect)newAspect;
|
||||
}
|
||||
|
||||
@ -126,7 +150,7 @@ namespace DCFApixels.DragonECS
|
||||
_combined.Sort((a, b) => a.order - b.order);
|
||||
foreach (var item in _combined)
|
||||
{
|
||||
EcsMask submask = item.aspect.mask;
|
||||
EcsMask submask = item.aspect._mask;
|
||||
maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения
|
||||
maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения
|
||||
maskInc.UnionWith(submask.inc);
|
||||
@ -146,26 +170,26 @@ namespace DCFApixels.DragonECS
|
||||
Dictionary<int, int> r = new Dictionary<int, int>();
|
||||
foreach (var id in maskInc)
|
||||
{
|
||||
var bit = EcsMaskBit.FromID(id);
|
||||
var bit = EcsMaskChunck.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray();
|
||||
EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
|
||||
r.Clear();
|
||||
foreach (var id in maskExc)
|
||||
{
|
||||
var bit = EcsMaskBit.FromID(id);
|
||||
var bit = EcsMaskChunck.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray();
|
||||
EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
|
||||
|
||||
var inc = maskInc.ToArray();
|
||||
Array.Sort(inc);
|
||||
var exc = maskExc.ToArray();
|
||||
Array.Sort(exc);
|
||||
|
||||
mask = new EcsMask(_world.id, inc, exc, incMasks, excMasks);
|
||||
mask = new EcsMask(0, _world.id, inc, exc, incMasks, excMasks);
|
||||
_world = null;
|
||||
_inc = null;
|
||||
_exc = null;
|
||||
@ -187,10 +211,20 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Destructor
|
||||
unsafe ~EcsAspect()
|
||||
{
|
||||
_sortIncBuffer.Dispose();
|
||||
_sortExcBuffer.Dispose();
|
||||
_sortIncChunckBuffer.Dispose();
|
||||
_sortExcChunckBuffer.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterator
|
||||
public EcsAspectIterator GetIterator()
|
||||
{
|
||||
return new EcsAspectIterator(this, source.Entities);
|
||||
return new EcsAspectIterator(this, _source.Entities);
|
||||
}
|
||||
public EcsAspectIterator GetIteratorFor(EcsSpan span)
|
||||
{
|
||||
@ -198,6 +232,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Combined
|
||||
private readonly struct Combined
|
||||
{
|
||||
public readonly EcsAspect aspect;
|
||||
@ -208,6 +243,7 @@ namespace DCFApixels.DragonECS
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region BuilderBase
|
||||
@ -219,168 +255,19 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Mask
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public readonly struct EcsMaskBit
|
||||
{
|
||||
private const int BITS = 32;
|
||||
private const int DIV_SHIFT = 5;
|
||||
private const int MOD_MASK = BITS - 1;
|
||||
|
||||
public readonly int chankIndex;
|
||||
public readonly int mask;
|
||||
public EcsMaskBit(int chankIndex, int mask)
|
||||
{
|
||||
this.chankIndex = chankIndex;
|
||||
this.mask = mask;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsMaskBit FromID(int id)
|
||||
{
|
||||
return new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); //аналогично new EcsMaskBit(id / BITS, 1 << (id % BITS)) но быстрее
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"mask({chankIndex}, {mask}, {BitsUtility.CountBits(mask)})";
|
||||
}
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
public int chunk;
|
||||
public uint mask;
|
||||
public int[] values = Array.Empty<int>();
|
||||
public string bits;
|
||||
public DebuggerProxy(EcsMaskBit maskbits)
|
||||
{
|
||||
chunk = maskbits.chankIndex;
|
||||
mask = (uint)maskbits.mask;
|
||||
BitsUtility.GetBitNumbersNoAlloc(mask, ref values);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] += (chunk) << 5;
|
||||
}
|
||||
bits = BitsUtility.ToBitsString(mask, '_', 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public sealed class EcsMask
|
||||
{
|
||||
internal readonly int worldID;
|
||||
internal readonly EcsMaskBit[] incChunckMasks;
|
||||
internal readonly EcsMaskBit[] excChunckMasks;
|
||||
internal readonly int[] inc;
|
||||
internal readonly int[] exc;
|
||||
public int WorldID => worldID;
|
||||
public EcsWorld World => EcsWorld.GetWorld(worldID);
|
||||
/// <summary>Including constraints</summary>
|
||||
public ReadOnlySpan<int> Inc => inc;
|
||||
/// <summary>Excluding constraints</summary>
|
||||
public ReadOnlySpan<int> Exc => exc;
|
||||
internal EcsMask(int worldID, int[] inc, int[] exc, EcsMaskBit[] incChunckMasks, EcsMaskBit[] excChunckMasks)
|
||||
{
|
||||
#if DEBUG
|
||||
CheckConstraints(inc, exc);
|
||||
#endif
|
||||
this.inc = inc;
|
||||
this.exc = exc;
|
||||
this.worldID = worldID;
|
||||
this.incChunckMasks = incChunckMasks;
|
||||
this.excChunckMasks = excChunckMasks;
|
||||
}
|
||||
|
||||
#region Object
|
||||
public override string ToString() => CreateLogString(worldID, inc, exc);
|
||||
#endregion
|
||||
|
||||
#region Debug utils
|
||||
#if DEBUG
|
||||
private static HashSet<int> _dummyHashSet = new HashSet<int>();
|
||||
private void CheckConstraints(int[] inc, int[] exc)
|
||||
{
|
||||
lock (_dummyHashSet)
|
||||
{
|
||||
if (CheckRepeats(inc)) throw new EcsFrameworkException("The values in the Include constraints are repeated.");
|
||||
if (CheckRepeats(exc)) throw new EcsFrameworkException("The values in the Exclude constraints are repeated.");
|
||||
_dummyHashSet.Clear();
|
||||
_dummyHashSet.UnionWith(inc);
|
||||
if (_dummyHashSet.Overlaps(exc)) throw new EcsFrameworkException("Conflicting Include and Exclude constraints.");
|
||||
}
|
||||
}
|
||||
private bool CheckRepeats(int[] array)
|
||||
{
|
||||
_dummyHashSet.Clear();
|
||||
foreach (var item in array)
|
||||
{
|
||||
if (_dummyHashSet.Contains(item)) return true;
|
||||
_dummyHashSet.Add(item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
private static string CreateLogString(int worldID, int[] inc, int[] exc)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG)
|
||||
string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1);
|
||||
return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})";
|
||||
#else
|
||||
return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization
|
||||
#endif
|
||||
}
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
public readonly EcsWorld world;
|
||||
public readonly int worldID;
|
||||
public readonly EcsMaskBit[] includedChunkMasks;
|
||||
public readonly EcsMaskBit[] excludedChunkMasks;
|
||||
public readonly int[] included;
|
||||
public readonly int[] excluded;
|
||||
public readonly Type[] includedTypes;
|
||||
public readonly Type[] excludedTypes;
|
||||
|
||||
public DebuggerProxy(EcsMask mask)
|
||||
{
|
||||
world = EcsWorld.GetWorld(mask.worldID);
|
||||
worldID = mask.worldID;
|
||||
includedChunkMasks = mask.incChunckMasks;
|
||||
excludedChunkMasks = mask.excChunckMasks;
|
||||
included = mask.inc;
|
||||
excluded = mask.exc;
|
||||
Type converter(int o) => world.GetComponentType(o);
|
||||
includedTypes = included.Select(converter).ToArray();
|
||||
excludedTypes = excluded.Select(converter).ToArray();
|
||||
}
|
||||
public override string ToString() => CreateLogString(worldID, included, excluded);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterator
|
||||
public ref struct EcsAspectIterator
|
||||
{
|
||||
public readonly int worldID;
|
||||
public readonly EcsMask mask;
|
||||
public readonly EcsAspect aspect;
|
||||
private EcsSpan _span;
|
||||
private Enumerator _enumerator;
|
||||
|
||||
public EcsAspectIterator(EcsAspect aspect, EcsSpan span)
|
||||
{
|
||||
worldID = aspect.World.id;
|
||||
mask = aspect.mask;
|
||||
_span = span;
|
||||
_enumerator = default;
|
||||
this.aspect = aspect;
|
||||
}
|
||||
|
||||
public int Current
|
||||
{
|
||||
[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();
|
||||
@ -416,315 +303,237 @@ namespace DCFApixels.DragonECS
|
||||
#region object
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
List<int> ints = new List<int>();
|
||||
foreach (var e in this)
|
||||
{
|
||||
result.Append(e);
|
||||
result.Append(", ");
|
||||
ints.Add(e);
|
||||
}
|
||||
return result.ToString();
|
||||
return CollectionUtility.EntitiesToString(ints, "it");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(_span, mask);
|
||||
|
||||
public Enumerator GetEnumerator() => new Enumerator(_span, aspect);
|
||||
|
||||
public unsafe ref struct Enumerator
|
||||
{
|
||||
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;
|
||||
#if sort_1
|
||||
private readonly EcsMaskBit[] _incChunckMasks;
|
||||
private readonly EcsMaskBit[] _excChunckMasks;
|
||||
#else
|
||||
private readonly EcsMaskBit[] _incChunckMasks;
|
||||
private readonly EcsMaskBit[] _excChunckMasks;
|
||||
#endif
|
||||
private readonly int[][] _entitiesComponentMasks;
|
||||
|
||||
private static EcsMaskChunck* _preSortedIncBuffer;
|
||||
private static EcsMaskChunck* _preSortedExcBuffer;
|
||||
|
||||
private UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
||||
private UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
||||
|
||||
private static EcsMaskBit* _sortedIncBuffer;
|
||||
private static EcsMaskBit* _sortedExcBuffer;
|
||||
private static SparseArray<int> _sp;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe Enumerator(EcsSpan span, EcsMask mask)
|
||||
private EcsAspect aspect;
|
||||
|
||||
public unsafe Enumerator(EcsSpan span, EcsAspect aspect)
|
||||
{
|
||||
this.aspect = aspect;
|
||||
_span = span.GetEnumerator();
|
||||
#if sort_1
|
||||
_incChunckMasks = mask.incChunckMasks;
|
||||
_excChunckMasks = mask.excChunckMasks;
|
||||
#endif
|
||||
_entitiesComponentMasks = span.World._entitiesComponentMasks;
|
||||
|
||||
EcsMask mask = aspect.Mask;
|
||||
|
||||
UnsafeArray<int> _sortIncBuffer = aspect._sortIncBuffer;
|
||||
UnsafeArray<int> _sortExcBuffer = aspect._sortExcBuffer;
|
||||
_sortIncChunckBuffer = aspect._sortIncChunckBuffer;
|
||||
_sortExcChunckBuffer = aspect._sortExcChunckBuffer;
|
||||
|
||||
#region MyRegion
|
||||
#region Trash
|
||||
#if !sort_1
|
||||
int[] inc = mask.inc;
|
||||
int[] exc = mask.exc;
|
||||
int[] counts = mask.World._poolComponentCounts;
|
||||
if (inc.Length > 1)
|
||||
{
|
||||
//if (inc.Length == 2)
|
||||
|
||||
|
||||
|
||||
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 (counts[inc[0]] > counts[inc[1]])
|
||||
// //if (_sortIncBufferLength == 2)
|
||||
// //{
|
||||
// // if (counts[_sortIncBuffer[0]] > counts[_sortIncBuffer[1]])
|
||||
// // {
|
||||
// // int tmp = _sortIncBuffer[0];
|
||||
// // _sortIncBuffer[0] = _sortIncBuffer[1];
|
||||
// // _sortIncBuffer[1] = tmp;
|
||||
// // }
|
||||
// // //...
|
||||
// //}
|
||||
// //else
|
||||
// {
|
||||
// int tmp = inc[0];
|
||||
// inc[0] = inc[1];
|
||||
// inc[1] = tmp;
|
||||
// }
|
||||
// //...
|
||||
//}
|
||||
//else
|
||||
{
|
||||
//for (int i = 0; i < inc.Length; i++)
|
||||
//{
|
||||
// int counti = counts[inc[i]];
|
||||
// if (counti <= 0)
|
||||
// for (int i = 0, n = _sortIncBuffer.Length - 1; i < n; i++)
|
||||
// {
|
||||
// _span = ReadOnlySpan<int>.Empty.GetEnumerator();
|
||||
// goto skip1;
|
||||
// }
|
||||
// for (int j = inc.Length - 1; j >= i; j--)
|
||||
// //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; )
|
||||
// {
|
||||
// if (counts[inc[j]] < counti)
|
||||
// ref int j0 = ref _sortIncBuffer.ptr[j++];
|
||||
// if (counts[j0] > counts[_sortIncBuffer.ptr[j]])
|
||||
// {
|
||||
// int tmp = inc[j];
|
||||
// inc[j] = inc[i];
|
||||
// inc[i] = tmp;
|
||||
// int tmp = _sortIncBuffer.ptr[j];
|
||||
// _sortIncBuffer.ptr[j] = j0;
|
||||
// j0 = tmp;
|
||||
// noSwaped = false;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//...
|
||||
|
||||
for (int i = 0, n = inc.Length; i < n - 1; i++)
|
||||
{
|
||||
//int counti = counts[inc[i]];
|
||||
//if (counti <= 0)
|
||||
//{
|
||||
// _span = ReadOnlySpan<int>.Empty.GetEnumerator();
|
||||
// goto skip1;
|
||||
//}
|
||||
bool noSwaped = true;
|
||||
for (int j = 0; j < n - i - 1; )
|
||||
{
|
||||
ref int j0 = ref inc[j++];
|
||||
if (counts[j0] > counts[inc[j]])
|
||||
{
|
||||
int tmp = inc[j];
|
||||
inc[j] = j0;
|
||||
j0 = tmp;
|
||||
noSwaped = false;
|
||||
}
|
||||
}
|
||||
#if !lockSwapCheck
|
||||
if (noSwaped)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
skip1:;
|
||||
if (exc.Length > 1)
|
||||
{
|
||||
//if (exc.Length == 2)
|
||||
//{
|
||||
// if (counts[exc[0]] < counts[exc[1]])
|
||||
// {
|
||||
// int tmp = exc[0];
|
||||
// exc[0] = inc[1];
|
||||
// exc[1] = tmp;
|
||||
// }
|
||||
// //...
|
||||
//}
|
||||
//else
|
||||
{
|
||||
//for (int i = 0; i < exc.Length; i++)
|
||||
//{
|
||||
// int counti = counts[inc[i]];
|
||||
// if (counti <= 0)
|
||||
// {
|
||||
// _excChunckMasks = ReadOnlySpan<EcsMaskBit>.Empty;
|
||||
// goto skip2;
|
||||
// }
|
||||
// for (int j = exc.Length - 1; j >= i; j--)
|
||||
// {
|
||||
// if (counts[exc[j]] > counti)
|
||||
// {
|
||||
// int tmp = exc[j];
|
||||
// exc[j] = exc[i];
|
||||
// exc[i] = tmp;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//...
|
||||
|
||||
for (int i = 0, n = exc.Length; i < n - 1; i++)
|
||||
{
|
||||
//int counti = counts[inc[i]];
|
||||
//if (counti <= 0)
|
||||
//{
|
||||
// _excChunckMasks = ReadOnlySpan<EcsMaskBit>.Empty;
|
||||
// goto skip2;
|
||||
//}
|
||||
bool noSwaped = true;
|
||||
for (int j = 0; j < n - i - 1;)
|
||||
{
|
||||
ref int j0 = ref exc[j++];
|
||||
if (counts[j0] < counts[exc[j]])
|
||||
{
|
||||
int tmp = exc[j];
|
||||
exc[j] = j0;
|
||||
j0 = tmp;
|
||||
noSwaped = false;
|
||||
}
|
||||
}
|
||||
#if !lockSwapCheck
|
||||
if (noSwaped)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
skip2:;
|
||||
|
||||
if (_sortedIncBuffer == null)
|
||||
{
|
||||
_sortedIncBuffer = UnmanagedArrayUtility.New<EcsMaskBit>(256);
|
||||
_sortedExcBuffer = UnmanagedArrayUtility.New<EcsMaskBit>(256);
|
||||
_sp = new SparseArray<int>(32);
|
||||
}
|
||||
|
||||
_sp.Clear();
|
||||
for (int i = 0, ii = 0; i < inc.Length; i++)
|
||||
{
|
||||
//int id = inc[i];
|
||||
//_sortedIncBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK));
|
||||
_sortedIncBuffer[i] = EcsMaskBit.FromID(inc[i]);
|
||||
}
|
||||
for (int i = 0; i < exc.Length; i++)
|
||||
{
|
||||
//int id = inc[i];
|
||||
//_sortedExcBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK));
|
||||
_sortedExcBuffer[i] = EcsMaskBit.FromID(exc[i]);
|
||||
}
|
||||
|
||||
EcsMaskBit[] incChunckMasks = mask.incChunckMasks;
|
||||
EcsMaskBit[] excChunckMasks = mask.excChunckMasks;
|
||||
|
||||
//_incChunckMasks = new ReadOnlySpan<EcsMaskBit>(_sortedIncBuffer, inc.Length);
|
||||
//_excChunckMasks = new ReadOnlySpan<EcsMaskBit>(_sortedExcBuffer, exc.Length);
|
||||
|
||||
|
||||
int _sortedIncBufferLength = inc.Length;
|
||||
int _sortedExcBufferLength = exc.Length;
|
||||
|
||||
if (_sortedIncBufferLength > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна
|
||||
{
|
||||
for (int i = 0, ii = 0; ii < incChunckMasks.Length; ii++)
|
||||
{
|
||||
EcsMaskBit bas = _sortedIncBuffer[i];
|
||||
int chankIndexX = bas.chankIndex;
|
||||
int maskX = bas.mask;
|
||||
|
||||
for (int j = i + 1; j < _sortedIncBufferLength; j++)
|
||||
{
|
||||
if (_sortedIncBuffer[j].chankIndex == chankIndexX)
|
||||
{
|
||||
maskX |= _sortedIncBuffer[j].mask;
|
||||
}
|
||||
}
|
||||
incChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX);
|
||||
while (++i < _sortedIncBufferLength && _sortedIncBuffer[i].chankIndex == chankIndexX)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_sortedExcBufferLength > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна
|
||||
{
|
||||
for (int i = 0, ii = 0; ii < excChunckMasks.Length; ii++)
|
||||
{
|
||||
EcsMaskBit bas = _sortedExcBuffer[i];
|
||||
int chankIndexX = bas.chankIndex;
|
||||
int maskX = bas.mask;
|
||||
|
||||
for (int j = i + 1; j < _sortedExcBufferLength; j++)
|
||||
{
|
||||
if (_sortedExcBuffer[j].chankIndex == chankIndexX)
|
||||
{
|
||||
maskX |= _sortedExcBuffer[j].mask;
|
||||
}
|
||||
}
|
||||
excChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX);
|
||||
while (++i < _sortedExcBufferLength && _sortedExcBuffer[i].chankIndex == chankIndexX)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_incChunckMasks = incChunckMasks;
|
||||
_excChunckMasks = excChunckMasks;
|
||||
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region MyRegion
|
||||
//_inc = mask.inc;
|
||||
//_exc = mask.exc;
|
||||
//int[] inc = mask.inc;
|
||||
//int[] exc = mask.exc;
|
||||
//int[] counts = mask.World._poolComponentCounts;
|
||||
//if (_inc.Length > 1)
|
||||
//{
|
||||
// for (int i = 0; i < inc.Length; i++)
|
||||
// {
|
||||
// if (counts[inc[i]] <= 0)
|
||||
// {
|
||||
// _span = ReadOnlySpan<int>.Empty.GetEnumerator();
|
||||
// goto skip1;
|
||||
// }
|
||||
// for (int j = 0; j < inc.Length - i - 1; j++)
|
||||
// {
|
||||
// if (counts[inc[i]] > counts[inc[j]])
|
||||
// {
|
||||
// int tmp = inc[j];
|
||||
// inc[j] = inc[i];
|
||||
// inc[i] = tmp;
|
||||
// }
|
||||
// if (noSwaped)
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//skip1:;
|
||||
//if(exc.Length > 1)
|
||||
//if (_sortExcBuffer.Length > 1)
|
||||
//{
|
||||
// for (int i = 0; i < exc.Length; i++)
|
||||
// //if (_sortExcBufferLength == 2)
|
||||
// //{
|
||||
// // if (counts[_sortExcBuffer[0]] < counts[_sortExcBuffer[1]])
|
||||
// // {
|
||||
// // int tmp = _sortExcBuffer[0];
|
||||
// // _sortExcBuffer[0] = _sortExcBuffer[1];
|
||||
// // _sortExcBuffer[1] = tmp;
|
||||
// // }
|
||||
// // //...
|
||||
// //}
|
||||
// //else
|
||||
// {
|
||||
// if (counts[exc[i]] <= 0)
|
||||
// for (int i = 0, n = _sortExcBuffer.Length - 1; i < n; i++)
|
||||
// {
|
||||
// _exc = ReadOnlySpan<int>.Empty;
|
||||
// goto skip2;
|
||||
// //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;
|
||||
// }
|
||||
// for (int j = 0; j < exc.Length - i - 1; j++)
|
||||
// {
|
||||
// if (counts[exc[i]] < counts[exc[j]])
|
||||
// {
|
||||
// int tmp = exc[j];
|
||||
// exc[j] = exc[i];
|
||||
// exc[i] = tmp;
|
||||
// }
|
||||
// if (noSwaped)
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//skip2:;
|
||||
|
||||
|
||||
|
||||
if (_preSortedIncBuffer == null)
|
||||
{
|
||||
_preSortedIncBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
||||
_preSortedExcBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < _sortIncBuffer.Length; i++)
|
||||
{
|
||||
_preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]);
|
||||
}
|
||||
for (int i = 0; i < _sortExcBuffer.Length; i++)
|
||||
{
|
||||
_preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]);
|
||||
}
|
||||
|
||||
//int _sortedIncBufferLength = mask.inc.Length;
|
||||
//int _sortedExcBufferLength = mask.exc.Length;
|
||||
|
||||
//if (_sortIncChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна
|
||||
if (_sortIncBuffer.Length > 1)
|
||||
{
|
||||
for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++)
|
||||
{
|
||||
EcsMaskChunck bas = _preSortedIncBuffer[i];
|
||||
int chankIndexX = bas.chankIndex;
|
||||
int maskX = bas.mask;
|
||||
|
||||
for (int j = i + 1; j < _sortIncBuffer.Length; j++)
|
||||
{
|
||||
if (_preSortedIncBuffer[j].chankIndex == chankIndexX)
|
||||
{
|
||||
maskX |= _preSortedIncBuffer[j].mask;
|
||||
}
|
||||
}
|
||||
_sortIncChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
||||
while (++i < _sortIncBuffer.Length && _preSortedIncBuffer[i].chankIndex == chankIndexX)
|
||||
{
|
||||
// skip
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sortIncChunckBuffer.ptr[0] = _preSortedIncBuffer[0];
|
||||
}
|
||||
|
||||
//if (_sortExcChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна
|
||||
if (_sortExcBuffer.Length > 1)
|
||||
{
|
||||
for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++)
|
||||
{
|
||||
EcsMaskChunck bas = _preSortedExcBuffer[i];
|
||||
int chankIndexX = bas.chankIndex;
|
||||
int maskX = bas.mask;
|
||||
|
||||
for (int j = i + 1; j < _sortExcBuffer.Length; j++)
|
||||
{
|
||||
if (_preSortedExcBuffer[j].chankIndex == chankIndexX)
|
||||
{
|
||||
maskX |= _preSortedExcBuffer[j].mask;
|
||||
}
|
||||
}
|
||||
_sortExcChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
||||
while (++i < _sortExcBuffer.Length && _preSortedExcBuffer[i].chankIndex == chankIndexX)
|
||||
{
|
||||
// skip
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sortExcChunckBuffer.ptr[0] = _preSortedExcBuffer[0];
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public int Current
|
||||
@ -732,55 +541,31 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _span.Current;
|
||||
}
|
||||
public entlong CurrentLong
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => aspect.World.GetEntityLong(_span.Current);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_span.MoveNext())
|
||||
{
|
||||
int e = _span.Current;
|
||||
|
||||
foreach (var bit in _incChunckMasks)
|
||||
for (int i = 0; i < _sortIncChunckBuffer.Length; i++)
|
||||
{
|
||||
var bit = _sortIncChunckBuffer.ptr[i];
|
||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
|
||||
goto skip;
|
||||
}
|
||||
foreach (var bit in _excChunckMasks)
|
||||
for (int i = 0; i < _sortExcChunckBuffer.Length; i++)
|
||||
{
|
||||
var bit = _sortExcChunckBuffer.ptr[i];
|
||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
|
||||
goto skip;
|
||||
}
|
||||
|
||||
//for (int i = 0, iMax = _incChunckMasks.Length; i < iMax; i++)
|
||||
//{
|
||||
// var bit = _incChunckMasks[i];
|
||||
// if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
|
||||
// goto skip;
|
||||
//}
|
||||
//for (int i = 0, iMax = _excChunckMasks.Length; i < iMax; i++)
|
||||
//{
|
||||
// var bit = _excChunckMasks[i];
|
||||
// if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
|
||||
// goto skip;
|
||||
//}
|
||||
return true;
|
||||
skip: continue;
|
||||
|
||||
#region MyRegion
|
||||
//for (int i = 0, iMax = _inc.Length; i < iMax; i++)
|
||||
//{
|
||||
// bit = EcsMaskBit.FromID(_inc[i]);
|
||||
// if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
|
||||
// goto skip;
|
||||
//}
|
||||
//for (int i = 0, iMax = _exc.Length; i < iMax; i++)
|
||||
//{
|
||||
// bit = EcsMaskBit.FromID(_exc[i]);
|
||||
// if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
|
||||
// goto skip;
|
||||
//}
|
||||
//return true;
|
||||
//skip: continue;
|
||||
#endregion
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
346
src/EcsMask.cs
Normal file
346
src/EcsMask.cs
Normal file
@ -0,0 +1,346 @@
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static Leopotam.EcsLite.EcsWorld;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
private Dictionary<EcsMask.BuilderMaskKey, EcsMask> _masks = new Dictionary<EcsMask.BuilderMaskKey, EcsMask>(256);
|
||||
internal EcsMask GetMask_Internal(EcsMask.BuilderMaskKey maskKey)
|
||||
{
|
||||
if(!_masks.TryGetValue(maskKey, out EcsMask result))
|
||||
{
|
||||
int[] combinedInc = maskKey.inc;
|
||||
int[] combinedExc = maskKey.exc;
|
||||
Dictionary<int, int> r = new Dictionary<int, int>();
|
||||
foreach (var id in combinedInc)
|
||||
{
|
||||
var bit = EcsMaskChunck.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
|
||||
r.Clear();
|
||||
foreach (var id in combinedExc)
|
||||
{
|
||||
var bit = EcsMaskChunck.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray();
|
||||
|
||||
result = new EcsMask(_masks.Count, id, combinedInc, combinedExc, incMasks, excMasks);
|
||||
_masks.Add(maskKey, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public sealed class EcsMask
|
||||
{
|
||||
internal readonly int id;
|
||||
internal readonly int worldID;
|
||||
internal readonly EcsMaskChunck[] incChunckMasks;
|
||||
internal readonly EcsMaskChunck[] excChunckMasks;
|
||||
internal readonly int[] inc;
|
||||
internal readonly int[] exc;
|
||||
public int WorldID => worldID;
|
||||
public EcsWorld World => EcsWorld.GetWorld(worldID);
|
||||
/// <summary>Including constraints</summary>
|
||||
public ReadOnlySpan<int> Inc => inc;
|
||||
/// <summary>Excluding constraints</summary>
|
||||
public ReadOnlySpan<int> Exc => exc;
|
||||
internal EcsMask(int id, int worldID, int[] inc, int[] exc, EcsMaskChunck[] incChunckMasks, EcsMaskChunck[] excChunckMasks)
|
||||
{
|
||||
#if DEBUG
|
||||
CheckConstraints(inc, exc);
|
||||
#endif
|
||||
this.id = id;
|
||||
this.inc = inc;
|
||||
this.exc = exc;
|
||||
this.worldID = worldID;
|
||||
this.incChunckMasks = incChunckMasks;
|
||||
this.excChunckMasks = excChunckMasks;
|
||||
}
|
||||
|
||||
#region Object
|
||||
public override string ToString() => CreateLogString(worldID, inc, exc);
|
||||
#endregion
|
||||
|
||||
#region Debug utils
|
||||
#if DEBUG
|
||||
private static HashSet<int> _dummyHashSet = new HashSet<int>();
|
||||
private void CheckConstraints(int[] inc, int[] exc)
|
||||
{
|
||||
lock (_dummyHashSet)
|
||||
{
|
||||
if (CheckRepeats(inc)) throw new EcsFrameworkException("The values in the Include constraints are repeated.");
|
||||
if (CheckRepeats(exc)) throw new EcsFrameworkException("The values in the Exclude constraints are repeated.");
|
||||
_dummyHashSet.Clear();
|
||||
_dummyHashSet.UnionWith(inc);
|
||||
if (_dummyHashSet.Overlaps(exc)) throw new EcsFrameworkException("Conflicting Include and Exclude constraints.");
|
||||
}
|
||||
}
|
||||
private bool CheckRepeats(int[] array)
|
||||
{
|
||||
_dummyHashSet.Clear();
|
||||
foreach (var item in array)
|
||||
{
|
||||
if (_dummyHashSet.Contains(item)) return true;
|
||||
_dummyHashSet.Add(item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
private static string CreateLogString(int worldID, int[] inc, int[] exc)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG)
|
||||
string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1);
|
||||
return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})";
|
||||
#else
|
||||
return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization
|
||||
#endif
|
||||
}
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
public readonly EcsWorld world;
|
||||
public readonly int worldID;
|
||||
public readonly EcsMaskChunck[] includedChunkMasks;
|
||||
public readonly EcsMaskChunck[] excludedChunkMasks;
|
||||
public readonly int[] included;
|
||||
public readonly int[] excluded;
|
||||
public readonly Type[] includedTypes;
|
||||
public readonly Type[] excludedTypes;
|
||||
|
||||
public DebuggerProxy(EcsMask mask)
|
||||
{
|
||||
world = EcsWorld.GetWorld(mask.worldID);
|
||||
worldID = mask.worldID;
|
||||
includedChunkMasks = mask.incChunckMasks;
|
||||
excludedChunkMasks = mask.excChunckMasks;
|
||||
included = mask.inc;
|
||||
excluded = mask.exc;
|
||||
Type converter(int o) => world.GetComponentType(o);
|
||||
includedTypes = included.Select(converter).ToArray();
|
||||
excludedTypes = excluded.Select(converter).ToArray();
|
||||
}
|
||||
public override string ToString() => CreateLogString(worldID, included, excluded);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public readonly struct BuilderMaskKey : IEquatable<BuilderMaskKey>
|
||||
{
|
||||
public readonly int[] inc;
|
||||
public readonly int[] exc;
|
||||
public readonly int hash;
|
||||
public BuilderMaskKey(int[] inc, int[] exc, int hash)
|
||||
{
|
||||
this.inc = inc;
|
||||
this.exc = exc;
|
||||
this.hash = hash;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(BuilderMaskKey other)
|
||||
{
|
||||
if (inc.Length != other.inc.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (exc.Length != other.exc.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < inc.Length; i++)
|
||||
{
|
||||
if (inc[i] != other.inc[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < exc.Length; i++)
|
||||
{
|
||||
if (exc[i] != other.exc[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
if(other.hash != hash)
|
||||
{
|
||||
throw new Exception("other.hash != hash");
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => hash;
|
||||
}
|
||||
|
||||
public class Builder
|
||||
{
|
||||
private EcsWorld _world;
|
||||
private HashSet<int> _inc;
|
||||
private HashSet<int> _exc;
|
||||
private List<Combined> _combined;
|
||||
|
||||
internal Builder(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
_inc = new HashSet<int>();
|
||||
_exc = new HashSet<int>();
|
||||
}
|
||||
|
||||
public void Include<T>()
|
||||
{
|
||||
int id = _world.GetComponentID<T>();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(typeof(T));
|
||||
#endif
|
||||
_inc.Add(id);
|
||||
}
|
||||
public void Exclude<T>()
|
||||
{
|
||||
int id = _world.GetComponentID<T>();
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(typeof(T));
|
||||
#endif
|
||||
_exc.Add(id);
|
||||
}
|
||||
public void Include(Type type)
|
||||
{
|
||||
int id = _world.GetComponentID(type);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
|
||||
#endif
|
||||
_inc.Add(id);
|
||||
}
|
||||
public void Exclude(Type type)
|
||||
{
|
||||
int id = _world.GetComponentID(type);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
|
||||
#endif
|
||||
_exc.Add(id);
|
||||
}
|
||||
|
||||
public void CombineWith(EcsMask mask, int order = 0)
|
||||
{
|
||||
_combined.Add(new Combined(mask, order));
|
||||
}
|
||||
|
||||
public EcsMask Build()
|
||||
{
|
||||
HashSet<int> combinedInc;
|
||||
HashSet<int> combinedExc;
|
||||
if (_combined.Count > 0)
|
||||
{
|
||||
combinedInc = new HashSet<int>();
|
||||
combinedExc = new HashSet<int>();
|
||||
_combined.Sort((a, b) => a.order - b.order);
|
||||
foreach (var item in _combined)
|
||||
{
|
||||
EcsMask submask = item.mask;
|
||||
combinedInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения
|
||||
combinedExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения
|
||||
combinedInc.UnionWith(submask.inc);
|
||||
combinedExc.UnionWith(submask.exc);
|
||||
}
|
||||
combinedInc.ExceptWith(_exc);//удаляю конфликтующие ограничения
|
||||
combinedExc.ExceptWith(_inc);//удаляю конфликтующие ограничения
|
||||
combinedInc.UnionWith(_inc);
|
||||
combinedExc.UnionWith(_exc);
|
||||
}
|
||||
else
|
||||
{
|
||||
combinedInc = _inc;
|
||||
combinedExc = _exc;
|
||||
}
|
||||
|
||||
var inc = combinedInc.ToArray();
|
||||
Array.Sort(inc);
|
||||
var exc = combinedExc.ToArray();
|
||||
Array.Sort(exc);
|
||||
|
||||
int keyHash = inc.Length + exc.Length;
|
||||
for (int i = 0, iMax = inc.Length; i < iMax; i++)
|
||||
{
|
||||
keyHash = unchecked(keyHash * 314159 + inc[i]);
|
||||
}
|
||||
for (int i = 0, iMax = exc.Length; i < iMax; i++)
|
||||
{
|
||||
keyHash = unchecked(keyHash * 314159 - exc[i]);
|
||||
}
|
||||
BuilderMaskKey key = new BuilderMaskKey(inc, exc, keyHash);
|
||||
|
||||
_world = null;
|
||||
_inc = null;
|
||||
_exc = null;
|
||||
_combined = null;
|
||||
|
||||
return _world.GetMask_Internal(key);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct Combined
|
||||
{
|
||||
public readonly EcsMask mask;
|
||||
public readonly int order;
|
||||
public Combined(EcsMask mask, int order)
|
||||
{
|
||||
this.mask = mask;
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public readonly struct EcsMaskChunck
|
||||
{
|
||||
internal const int BITS = 32;
|
||||
internal const int DIV_SHIFT = 5;
|
||||
internal const int MOD_MASK = BITS - 1;
|
||||
|
||||
public readonly int chankIndex;
|
||||
public readonly int mask;
|
||||
public EcsMaskChunck(int chankIndex, int mask)
|
||||
{
|
||||
this.chankIndex = chankIndex;
|
||||
this.mask = mask;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsMaskChunck FromID(int id)
|
||||
{
|
||||
return new EcsMaskChunck(id >> DIV_SHIFT, 1 << (id & MOD_MASK));
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"mask({chankIndex}, {mask}, {BitsUtility.CountBits(mask)})";
|
||||
}
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
public int chunk;
|
||||
public uint mask;
|
||||
public int[] values = Array.Empty<int>();
|
||||
public string bits;
|
||||
public DebuggerProxy(EcsMaskChunck maskbits)
|
||||
{
|
||||
chunk = maskbits.chankIndex;
|
||||
mask = (uint)maskbits.mask;
|
||||
BitsUtility.GetBitNumbersNoAlloc(mask, ref values);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] += (chunk) << 5;
|
||||
}
|
||||
bits = BitsUtility.ToBitsString(mask, '_', 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -125,14 +125,14 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#region Pools mediation
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
{
|
||||
_poolComponentCounts[componentTypeID]++;
|
||||
_componentCounts[entityID]++;
|
||||
_entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
{
|
||||
_poolComponentCounts[componentTypeID]--;
|
||||
var count = --_componentCounts[entityID];
|
||||
@ -145,7 +145,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool HasEntityComponent(int entityID, EcsMaskBit maskBit)
|
||||
private bool HasEntityComponent(int entityID, EcsMaskChunck maskBit)
|
||||
{
|
||||
return (_entitiesComponentMasks[entityID][maskBit.chankIndex] & maskBit.mask) != maskBit.mask;
|
||||
}
|
||||
@ -164,17 +164,17 @@ namespace DCFApixels.DragonECS
|
||||
_world = world;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
public void RegisterComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
{
|
||||
_world.RegisterEntityComponent(entityID, componentTypeID, maskBit);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
{
|
||||
_world.UnregisterEntityComponent(entityID, componentTypeID, maskBit);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasComponent(int entityID, EcsMaskBit maskBit)
|
||||
public bool HasComponent(int entityID, EcsMaskChunck maskBit)
|
||||
{
|
||||
return _world.HasEntityComponent(entityID, maskBit);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
@ -175,7 +175,7 @@ namespace DCFApixels.DragonECS
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
@ -141,7 +141,7 @@ namespace DCFApixels.DragonECS
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private int _count;
|
||||
@ -124,7 +124,7 @@ namespace DCFApixels.DragonECS
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
|
||||
_mapping = new bool[world.Capacity];
|
||||
_count = 0;
|
||||
|
179
src/Utils/ArraySortHalperX.cs
Normal file
179
src/Utils/ArraySortHalperX.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
||||
namespace DCFApixels.DragonECS.Internal
|
||||
{
|
||||
internal interface IComparerX<T>
|
||||
{
|
||||
// a > b = return > 0
|
||||
int Compare(T a, T b);
|
||||
}
|
||||
internal static class ArraySortHalperX<T>
|
||||
{
|
||||
private const int IntrosortSizeThreshold = 16;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwapIfGreater<TComparer>(T[] items, ref TComparer comparer, int i, int j) where TComparer : IComparerX<T>
|
||||
{
|
||||
if (comparer.Compare(items[i], items[j]) > 0)
|
||||
{
|
||||
T key = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = key;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsertionSort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
{
|
||||
for (int i = 0; i < items.Length - 1; i++)
|
||||
{
|
||||
T t = items[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
|
||||
{
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
items[j + 1] = t;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
{
|
||||
int length = items.Length;
|
||||
if (length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (length <= IntrosortSizeThreshold)
|
||||
{
|
||||
|
||||
if (length == 2)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 3)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
SwapIfGreater(items, ref comparer, 0, 2);
|
||||
SwapIfGreater(items, ref comparer, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort(items, ref comparer);
|
||||
return;
|
||||
}
|
||||
IComparerX<T> packed = comparer;
|
||||
Array.Sort(items, comparer.Compare);
|
||||
comparer = (TComparer)packed;
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe class UnsafeArraySortHalperX<T> where T : unmanaged
|
||||
{
|
||||
private const int IntrosortSizeThreshold = 16;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwapIfGreater<TComparer>(T* items, ref TComparer comparer, int i, int j) where TComparer : IComparerX<T>
|
||||
{
|
||||
if (comparer.Compare(items[i], items[j]) > 0)
|
||||
{
|
||||
T key = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = key;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsertionSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
{
|
||||
for (int i = 0; i < length - 1; i++)
|
||||
{
|
||||
T t = items[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
|
||||
{
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
items[j + 1] = t;
|
||||
}
|
||||
}
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static void OptimizedBubbleSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
//{
|
||||
// for (int i = 0, n = length - 1; i < n; i++)
|
||||
// {
|
||||
// bool noSwaped = true;
|
||||
// for (int j = 0; j < n - i;)
|
||||
// {
|
||||
// ref T j0 = ref items[j++];
|
||||
// if(comparer.Compare(j0, items[j]) < 0)
|
||||
// {
|
||||
// T tmp = items[j];
|
||||
// items[j] = j0;
|
||||
// j0 = tmp;
|
||||
// noSwaped = false;
|
||||
// }
|
||||
// }
|
||||
// if (noSwaped)
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
public static void InsertionSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
{
|
||||
if (length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 2)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 3)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
SwapIfGreater(items, ref comparer, 0, 2);
|
||||
SwapIfGreater(items, ref comparer, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort_Unchecked(items, length, ref comparer);
|
||||
}
|
||||
//public static void OptimizedBubbleSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
//{
|
||||
// if (length == 1)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (length == 2)
|
||||
// {
|
||||
// SwapIfGreater(items, ref comparer, 0, 1);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (length == 3)
|
||||
// {
|
||||
// SwapIfGreater(items, ref comparer, 0, 1);
|
||||
// SwapIfGreater(items, ref comparer, 0, 2);
|
||||
// SwapIfGreater(items, ref comparer, 1, 2);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// OptimizedBubbleSort_Unchecked(items, length, ref comparer);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -18,7 +19,46 @@ namespace DCFApixels.DragonECS.Utils
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
internal readonly struct EnumerableInt : IEnumerable<int>
|
||||
{
|
||||
public readonly int start;
|
||||
public readonly int length;
|
||||
private EnumerableInt(int start, int length)
|
||||
{
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
}
|
||||
public static EnumerableInt Range(int start, int length) => new EnumerableInt(start, length);
|
||||
public static EnumerableInt StartEnd(int start, int end) => new EnumerableInt(start, end - start);
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(start, start + length);
|
||||
public struct Enumerator : IEnumerator<int>
|
||||
{
|
||||
private readonly int _max;
|
||||
private int _current;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator(int max, int current)
|
||||
{
|
||||
_max = max;
|
||||
_current = current - 1;
|
||||
}
|
||||
public int Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _current;
|
||||
}
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_current < _max;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset() { }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
internal static unsafe class UnmanagedArrayUtility
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -26,6 +66,11 @@ namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void New<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* NewAndInit<T>(int capacity) where T : unmanaged
|
||||
@ -38,12 +83,39 @@ namespace DCFApixels.DragonECS.Utils
|
||||
|
||||
return (T*)newPointer;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void NewAndInit<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
int newSize = Marshal.SizeOf(typeof(T)) * capacity;
|
||||
byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
|
||||
for (int i = 0; i < newSize; i++)
|
||||
*(newPointer + i) = 0;
|
||||
|
||||
ptr = (T*)newPointer;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Free(void* pointer)
|
||||
{
|
||||
Marshal.FreeHGlobal(new IntPtr(pointer));
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Free<T>(ref T* pointer, ref int length) where T : unmanaged
|
||||
{
|
||||
Marshal.FreeHGlobal(new IntPtr(pointer));
|
||||
pointer = null;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* Clone<T>(T* sourcePtr, int length) where T : unmanaged
|
||||
{
|
||||
T* clone = New<T>(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
clone[i] = sourcePtr[i];
|
||||
return clone;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* Resize<T>(void* oldPointer, int newCount) where T : unmanaged
|
||||
@ -54,11 +126,15 @@ namespace DCFApixels.DragonECS.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntitiesCollectionUtility
|
||||
public static class CollectionUtility
|
||||
{
|
||||
public static string AutoToString(IEnumerable<int> range, string name)
|
||||
public static string EntitiesToString(IEnumerable<int> range, string name)
|
||||
{
|
||||
return $"{name}({range.Count()}) {{{string.Join(", ", range.OrderBy(o => o))}}})";
|
||||
}
|
||||
public static string AutoToString<T>(IEnumerable<T> range, string name)
|
||||
{
|
||||
return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})";
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user