682 lines
24 KiB
C#
682 lines
24 KiB
C#
|
|
using System.Runtime.CompilerServices;
|
||
|
|
|
||
|
|
namespace OECS;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Provides zero-allocation ref struct iterators and entity-finding
|
||
|
|
/// extension methods for querying a <see cref="World"/>.
|
||
|
|
/// </summary>
|
||
|
|
public static class WorldQueryExtensions
|
||
|
|
{
|
||
|
|
// ── Select (ref struct enumerators) ──────────────────────────────
|
||
|
|
|
||
|
|
public static Select1<T1> Select<T1>(this World world, Query<T1> query = default)
|
||
|
|
where T1 : struct
|
||
|
|
{
|
||
|
|
return new Select1<T1>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Select2<T1, T2> Select<T1, T2>(this World world, Query<T1, T2> query = default)
|
||
|
|
where T1 : struct where T2 : struct
|
||
|
|
{
|
||
|
|
return new Select2<T1, T2>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Select3<T1, T2, T3> Select<T1, T2, T3>(this World world, Query<T1, T2, T3> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct
|
||
|
|
{
|
||
|
|
return new Select3<T1, T2, T3>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Select4<T1, T2, T3, T4> Select<T1, T2, T3, T4>(this World world, Query<T1, T2, T3, T4> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct where T4 : struct
|
||
|
|
{
|
||
|
|
return new Select4<T1, T2, T3, T4>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Select5<T1, T2, T3, T4, T5> Select<T1, T2, T3, T4, T5>(this World world, Query<T1, T2, T3, T4, T5> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct
|
||
|
|
{
|
||
|
|
return new Select5<T1, T2, T3, T4, T5>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Select6<T1, T2, T3, T4, T5, T6> Select<T1, T2, T3, T4, T5, T6>(this World world, Query<T1, T2, T3, T4, T5, T6> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct
|
||
|
|
{
|
||
|
|
return new Select6<T1, T2, T3, T4, T5, T6>(world, query);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── FindEntity / FindEntities ────────────────────────────────────
|
||
|
|
|
||
|
|
public static Entity FindEntity<T1>(this World world, Query<T1> query = default)
|
||
|
|
where T1 : struct
|
||
|
|
{
|
||
|
|
using var iter = new Select1<T1>(world, query);
|
||
|
|
return iter.MoveNext() ? iter.Entity : Entity.Null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Entity FindEntity<T1, T2>(this World world, Query<T1, T2> query = default)
|
||
|
|
where T1 : struct where T2 : struct
|
||
|
|
{
|
||
|
|
using var iter = new Select2<T1, T2>(world, query);
|
||
|
|
return iter.MoveNext() ? iter.Entity : Entity.Null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static Entity FindEntity<T1, T2, T3>(this World world, Query<T1, T2, T3> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct
|
||
|
|
{
|
||
|
|
using var iter = new Select3<T1, T2, T3>(world, query);
|
||
|
|
return iter.MoveNext() ? iter.Entity : Entity.Null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static List<Entity> FindEntities<T1>(this World world, Query<T1> query = default)
|
||
|
|
where T1 : struct
|
||
|
|
{
|
||
|
|
var list = new List<Entity>();
|
||
|
|
using var iter = new Select1<T1>(world, query);
|
||
|
|
while (iter.MoveNext()) list.Add(iter.Entity);
|
||
|
|
return list;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static List<Entity> FindEntities<T1, T2>(this World world, Query<T1, T2> query = default)
|
||
|
|
where T1 : struct where T2 : struct
|
||
|
|
{
|
||
|
|
var list = new List<Entity>();
|
||
|
|
using var iter = new Select2<T1, T2>(world, query);
|
||
|
|
while (iter.MoveNext()) list.Add(iter.Entity);
|
||
|
|
return list;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static List<Entity> FindEntities<T1, T2, T3>(this World world, Query<T1, T2, T3> query = default)
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct
|
||
|
|
{
|
||
|
|
var list = new List<Entity>();
|
||
|
|
using var iter = new Select3<T1, T2, T3>(world, query);
|
||
|
|
while (iter.MoveNext()) list.Add(iter.Entity);
|
||
|
|
return list;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Ref struct enumerators ───────────────────────────────────────────
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with one component type.</summary>
|
||
|
|
public ref struct Select1<T1> where T1 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
|
||
|
|
internal Select1(World world, Query<T1> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_count = _set?.Count ?? 0;
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>The current entity handle.</summary>
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
|
||
|
|
/// <summary>The current enumerator (required for foreach duck-typing).</summary>
|
||
|
|
public Select1<T1> Current => this;
|
||
|
|
|
||
|
|
/// <summary>Read-write reference to the current entity's component of type <typeparamref name="T1"/>.</summary>
|
||
|
|
public ref T1 Item1 => ref _set!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
Entity = _set.DenseEntities[_index];
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select1<T1> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
_world.EndIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with two component types. Drives from the smaller set.</summary>
|
||
|
|
public ref struct Select2<T1, T2> where T1 : struct where T2 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set1;
|
||
|
|
private readonly SparseSet<T2>? _set2;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
private readonly bool _swapped;
|
||
|
|
|
||
|
|
internal Select2(World world, Query<T1, T2> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set1 = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_set2 = _store.GetSet(typeof(T2)) as SparseSet<T2>;
|
||
|
|
|
||
|
|
int c1 = _set1?.Count ?? 0;
|
||
|
|
int c2 = _set2?.Count ?? 0;
|
||
|
|
_swapped = c2 < c1;
|
||
|
|
_count = _swapped ? c2 : c1;
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
public Select2<T1, T2> Current => this;
|
||
|
|
public ref T1 Item1 => ref _set1!.Get(Entity);
|
||
|
|
public ref T2 Item2 => ref _set2!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set1 == null || _set2 == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
if (_swapped)
|
||
|
|
{
|
||
|
|
Entity = _set2!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Entity = _set1.DenseEntities[_index];
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
}
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select2<T1, T2> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose() => _world.EndIteration();
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with three component types. Drives from the smallest set.</summary>
|
||
|
|
public ref struct Select3<T1, T2, T3>
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set1;
|
||
|
|
private readonly SparseSet<T2>? _set2;
|
||
|
|
private readonly SparseSet<T3>? _set3;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
private readonly int _driver; // 0 = set1, 1 = set2, 2 = set3
|
||
|
|
|
||
|
|
internal Select3(World world, Query<T1, T2, T3> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set1 = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_set2 = _store.GetSet(typeof(T2)) as SparseSet<T2>;
|
||
|
|
_set3 = _store.GetSet(typeof(T3)) as SparseSet<T3>;
|
||
|
|
|
||
|
|
int c1 = _set1?.Count ?? 0;
|
||
|
|
int c2 = _set2?.Count ?? 0;
|
||
|
|
int c3 = _set3?.Count ?? 0;
|
||
|
|
|
||
|
|
if (c2 <= c1 && c2 <= c3) { _driver = 1; _count = c2; }
|
||
|
|
else if (c3 <= c1 && c3 <= c2) { _driver = 2; _count = c3; }
|
||
|
|
else { _driver = 0; _count = c1; }
|
||
|
|
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
public Select3<T1, T2, T3> Current => this;
|
||
|
|
public ref T1 Item1 => ref _set1!.Get(Entity);
|
||
|
|
public ref T2 Item2 => ref _set2!.Get(Entity);
|
||
|
|
public ref T3 Item3 => ref _set3!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set1 == null || _set2 == null || _set3 == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
switch (_driver)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
Entity = _set1.DenseEntities[_index];
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
Entity = _set2!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
Entity = _set3!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select3<T1, T2, T3> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose() => _world.EndIteration();
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with four component types.</summary>
|
||
|
|
public ref struct Select4<T1, T2, T3, T4>
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct where T4 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set1;
|
||
|
|
private readonly SparseSet<T2>? _set2;
|
||
|
|
private readonly SparseSet<T3>? _set3;
|
||
|
|
private readonly SparseSet<T4>? _set4;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
private readonly int _driver;
|
||
|
|
|
||
|
|
internal Select4(World world, Query<T1, T2, T3, T4> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set1 = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_set2 = _store.GetSet(typeof(T2)) as SparseSet<T2>;
|
||
|
|
_set3 = _store.GetSet(typeof(T3)) as SparseSet<T3>;
|
||
|
|
_set4 = _store.GetSet(typeof(T4)) as SparseSet<T4>;
|
||
|
|
|
||
|
|
int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0;
|
||
|
|
int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0;
|
||
|
|
int min = c1;
|
||
|
|
_driver = 0;
|
||
|
|
if (c2 < min) { min = c2; _driver = 1; }
|
||
|
|
if (c3 < min) { min = c3; _driver = 2; }
|
||
|
|
if (c4 < min) { min = c4; _driver = 3; }
|
||
|
|
_count = min;
|
||
|
|
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
public Select4<T1, T2, T3, T4> Current => this;
|
||
|
|
public ref T1 Item1 => ref _set1!.Get(Entity);
|
||
|
|
public ref T2 Item2 => ref _set2!.Get(Entity);
|
||
|
|
public ref T3 Item3 => ref _set3!.Get(Entity);
|
||
|
|
public ref T4 Item4 => ref _set4!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set1 == null || _set2 == null || _set3 == null || _set4 == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
switch (_driver)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
Entity = _set1.DenseEntities[_index];
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
Entity = _set2!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
Entity = _set3!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
Entity = _set4!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select4<T1, T2, T3, T4> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose() => _world.EndIteration();
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with five component types.</summary>
|
||
|
|
public ref struct Select5<T1, T2, T3, T4, T5>
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set1;
|
||
|
|
private readonly SparseSet<T2>? _set2;
|
||
|
|
private readonly SparseSet<T3>? _set3;
|
||
|
|
private readonly SparseSet<T4>? _set4;
|
||
|
|
private readonly SparseSet<T5>? _set5;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
private readonly int _driver;
|
||
|
|
|
||
|
|
internal Select5(World world, Query<T1, T2, T3, T4, T5> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set1 = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_set2 = _store.GetSet(typeof(T2)) as SparseSet<T2>;
|
||
|
|
_set3 = _store.GetSet(typeof(T3)) as SparseSet<T3>;
|
||
|
|
_set4 = _store.GetSet(typeof(T4)) as SparseSet<T4>;
|
||
|
|
_set5 = _store.GetSet(typeof(T5)) as SparseSet<T5>;
|
||
|
|
|
||
|
|
int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0;
|
||
|
|
int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0;
|
||
|
|
int c5 = _set5?.Count ?? 0;
|
||
|
|
int min = c1;
|
||
|
|
_driver = 0;
|
||
|
|
if (c2 < min) { min = c2; _driver = 1; }
|
||
|
|
if (c3 < min) { min = c3; _driver = 2; }
|
||
|
|
if (c4 < min) { min = c4; _driver = 3; }
|
||
|
|
if (c5 < min) { min = c5; _driver = 4; }
|
||
|
|
_count = min;
|
||
|
|
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
public Select5<T1, T2, T3, T4, T5> Current => this;
|
||
|
|
public ref T1 Item1 => ref _set1!.Get(Entity);
|
||
|
|
public ref T2 Item2 => ref _set2!.Get(Entity);
|
||
|
|
public ref T3 Item3 => ref _set3!.Get(Entity);
|
||
|
|
public ref T4 Item4 => ref _set4!.Get(Entity);
|
||
|
|
public ref T5 Item5 => ref _set5!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set1 == null || _set2 == null || _set3 == null || _set4 == null || _set5 == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
switch (_driver)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
Entity = _set1.DenseEntities[_index];
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
Entity = _set2!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
Entity = _set3!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
Entity = _set4!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
Entity = _set5!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select5<T1, T2, T3, T4, T5> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose() => _world.EndIteration();
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Ref struct iterator for queries with six component types.</summary>
|
||
|
|
public ref struct Select6<T1, T2, T3, T4, T5, T6>
|
||
|
|
where T1 : struct where T2 : struct where T3 : struct
|
||
|
|
where T4 : struct where T5 : struct where T6 : struct
|
||
|
|
{
|
||
|
|
private readonly World _world;
|
||
|
|
private readonly SparseSet<T1>? _set1;
|
||
|
|
private readonly SparseSet<T2>? _set2;
|
||
|
|
private readonly SparseSet<T3>? _set3;
|
||
|
|
private readonly SparseSet<T4>? _set4;
|
||
|
|
private readonly SparseSet<T5>? _set5;
|
||
|
|
private readonly SparseSet<T6>? _set6;
|
||
|
|
private readonly ComponentStore _store;
|
||
|
|
private readonly HashSet<Type>? _without;
|
||
|
|
private int _index;
|
||
|
|
private readonly int _count;
|
||
|
|
private readonly int _driver;
|
||
|
|
|
||
|
|
internal Select6(World world, Query<T1, T2, T3, T4, T5, T6> query)
|
||
|
|
{
|
||
|
|
_world = world;
|
||
|
|
_store = world.Components;
|
||
|
|
_without = query.WithoutTypes;
|
||
|
|
_set1 = _store.GetSet(typeof(T1)) as SparseSet<T1>;
|
||
|
|
_set2 = _store.GetSet(typeof(T2)) as SparseSet<T2>;
|
||
|
|
_set3 = _store.GetSet(typeof(T3)) as SparseSet<T3>;
|
||
|
|
_set4 = _store.GetSet(typeof(T4)) as SparseSet<T4>;
|
||
|
|
_set5 = _store.GetSet(typeof(T5)) as SparseSet<T5>;
|
||
|
|
_set6 = _store.GetSet(typeof(T6)) as SparseSet<T6>;
|
||
|
|
|
||
|
|
int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0;
|
||
|
|
int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0;
|
||
|
|
int c5 = _set5?.Count ?? 0, c6 = _set6?.Count ?? 0;
|
||
|
|
int min = c1;
|
||
|
|
_driver = 0;
|
||
|
|
if (c2 < min) { min = c2; _driver = 1; }
|
||
|
|
if (c3 < min) { min = c3; _driver = 2; }
|
||
|
|
if (c4 < min) { min = c4; _driver = 3; }
|
||
|
|
if (c5 < min) { min = c5; _driver = 4; }
|
||
|
|
if (c6 < min) { min = c6; _driver = 5; }
|
||
|
|
_count = min;
|
||
|
|
|
||
|
|
_index = -1;
|
||
|
|
_world.BeginIteration();
|
||
|
|
}
|
||
|
|
|
||
|
|
public Entity Entity { get; private set; }
|
||
|
|
public Select6<T1, T2, T3, T4, T5, T6> Current => this;
|
||
|
|
public ref T1 Item1 => ref _set1!.Get(Entity);
|
||
|
|
public ref T2 Item2 => ref _set2!.Get(Entity);
|
||
|
|
public ref T3 Item3 => ref _set3!.Get(Entity);
|
||
|
|
public ref T4 Item4 => ref _set4!.Get(Entity);
|
||
|
|
public ref T5 Item5 => ref _set5!.Get(Entity);
|
||
|
|
public ref T6 Item6 => ref _set6!.Get(Entity);
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool MoveNext()
|
||
|
|
{
|
||
|
|
if (_set1 == null || _set2 == null || _set3 == null || _set4 == null || _set5 == null || _set6 == null) return false;
|
||
|
|
while (++_index < _count)
|
||
|
|
{
|
||
|
|
switch (_driver)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
Entity = _set1.DenseEntities[_index];
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
if (!_set6.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
Entity = _set2!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
if (!_set6.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
Entity = _set3!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
if (!_set6.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
Entity = _set4!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
if (!_set6.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
Entity = _set5!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set6.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
Entity = _set6!.DenseEntities[_index];
|
||
|
|
if (!_set1.Contains(Entity)) continue;
|
||
|
|
if (!_set2.Contains(Entity)) continue;
|
||
|
|
if (!_set3.Contains(Entity)) continue;
|
||
|
|
if (!_set4.Contains(Entity)) continue;
|
||
|
|
if (!_set5.Contains(Entity)) continue;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!PassesWithout()) continue;
|
||
|
|
if (_world.IsSingletonEntity(Entity)) continue;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Select6<T1, T2, T3, T4, T5, T6> GetEnumerator() => this;
|
||
|
|
|
||
|
|
public void Dispose() => _world.EndIteration();
|
||
|
|
|
||
|
|
private bool PassesWithout()
|
||
|
|
{
|
||
|
|
if (_without == null) return true;
|
||
|
|
foreach (var type in _without)
|
||
|
|
{
|
||
|
|
var set = _store.GetSet(type);
|
||
|
|
if (set != null && set.Contains(Entity))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|