mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 01:44:35 +08:00
284 lines
10 KiB
C#
284 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace DCFApixels.DragonECS
|
|
{
|
|
public abstract class EcsSubject
|
|
{
|
|
[EditorBrowsable(EditorBrowsableState.Always)]
|
|
internal EcsWorld source;
|
|
[EditorBrowsable(EditorBrowsableState.Always)]
|
|
internal EcsMask mask;
|
|
private bool _isInit;
|
|
|
|
#region Properties
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public EcsMask Mask => mask;
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public EcsWorld World => source;
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool IsInit => _isInit;
|
|
#endregion
|
|
|
|
#region Methods
|
|
public bool IsMatches(int entityID) => source.IsMatchesMask(mask, entityID);
|
|
#endregion
|
|
|
|
#region Builder
|
|
protected virtual void Init(Builder b) { }
|
|
public sealed class Builder : EcsSubjectBuilderBase
|
|
{
|
|
private EcsWorld _world;
|
|
private List<int> _inc;
|
|
private List<int> _exc;
|
|
|
|
public EcsWorld World => _world;
|
|
|
|
private Builder(EcsWorld world)
|
|
{
|
|
_world = world;
|
|
_inc = new List<int>(8);
|
|
_exc = new List<int>(4);
|
|
}
|
|
internal static TSubject Build<TSubject>(EcsWorld world) where TSubject : EcsSubject
|
|
{
|
|
Builder builder = new Builder(world);
|
|
Type queryType = typeof(TSubject);
|
|
ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
|
|
EcsSubject newSubject;
|
|
if (constructorInfo != null)
|
|
{
|
|
newSubject = (EcsSubject)constructorInfo.Invoke(new object[] { builder });
|
|
}
|
|
else
|
|
{
|
|
newSubject = (EcsSubject)Activator.CreateInstance(typeof(TSubject));
|
|
newSubject.Init(builder);
|
|
}
|
|
newSubject.source = world;
|
|
builder.End(out newSubject.mask);
|
|
newSubject._isInit = true;
|
|
return (TSubject)newSubject;
|
|
}
|
|
|
|
#region Include/Exclude/Optional
|
|
public sealed override TPool Include<TComponent, TPool>()
|
|
{
|
|
IncludeImplicit<TComponent>();
|
|
return _world.GetPool<TComponent, TPool>();
|
|
}
|
|
public sealed override TPool Exclude<TComponent, TPool>()
|
|
{
|
|
ExcludeImplicit<TComponent>();
|
|
return _world.GetPool<TComponent, TPool>();
|
|
}
|
|
public sealed override TPool Optional<TComponent, TPool>()
|
|
{
|
|
return _world.GetPool<TComponent, TPool>();
|
|
}
|
|
public void IncludeImplicit<TComponent>()
|
|
{
|
|
int id = _world.GetComponentID<TComponent>();
|
|
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
|
|
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
|
|
#endif
|
|
_inc.Add(_world.GetComponentID<TComponent>());
|
|
}
|
|
public void ExcludeImplicit<TComponent>()
|
|
{
|
|
int id = _world.GetComponentID<TComponent>();
|
|
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
|
|
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
|
|
#endif
|
|
_exc.Add(_world.GetComponentID<TComponent>());
|
|
}
|
|
#endregion
|
|
|
|
private void End(out EcsMask mask)
|
|
{
|
|
_inc.Sort();
|
|
_exc.Sort();
|
|
mask = new EcsMask(_world.Archetype, _inc.ToArray(), _exc.ToArray());
|
|
_world = null;
|
|
_inc = null;
|
|
_exc = null;
|
|
}
|
|
|
|
#region SupportReflectionHack
|
|
#if UNITY_2020_3_OR_NEWER
|
|
[UnityEngine.Scripting.Preserve]
|
|
#endif
|
|
private void SupportReflectionHack<TComponent, TPool>() where TPool : IEcsPoolImplementation<TComponent>, new()
|
|
{
|
|
Include<TComponent, TPool>();
|
|
Exclude<TComponent, TPool>();
|
|
Optional<TComponent, TPool>();
|
|
IncludeImplicit<TComponent>();
|
|
ExcludeImplicit<TComponent>();
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
#region Extensions
|
|
public static class EcsSubjectExtensions
|
|
{
|
|
public static EcsSubjectIterator<TSubject> GetIterator<TSubject>(this TSubject self) where TSubject : EcsSubject
|
|
{
|
|
return new EcsSubjectIterator<TSubject>(self, self.World.Entities);
|
|
}
|
|
public static EcsSubjectIterator<TSubject> GetIteratorFor<TSubject>(this TSubject self, EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
|
|
{
|
|
return new EcsSubjectIterator<TSubject>(self, sourceGroup);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region BuilderBase
|
|
public abstract class EcsSubjectBuilderBase
|
|
{
|
|
public abstract TPool Include<TComponent, TPool>() where TPool : IEcsPoolImplementation<TComponent>, new();
|
|
public abstract TPool Exclude<TComponent, TPool>() where TPool : IEcsPoolImplementation<TComponent>, new();
|
|
public abstract TPool Optional<TComponent, TPool>() where TPool : IEcsPoolImplementation<TComponent>, new();
|
|
}
|
|
#endregion
|
|
|
|
#region Mask
|
|
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
|
public sealed class EcsMask
|
|
{
|
|
internal readonly Type _worldType;
|
|
internal readonly int[] _inc;
|
|
internal readonly int[] _exc;
|
|
public EcsMask(Type worldType, int[] inc, int[] exc)
|
|
{
|
|
_worldType = worldType;
|
|
_inc = inc;
|
|
_exc = exc;
|
|
}
|
|
|
|
public override string ToString() => CreateLogString(_worldType, _inc, _exc);
|
|
|
|
#region Debug utils
|
|
private static string CreateLogString(Type worldType, int[] inc, int[] exc)
|
|
{
|
|
#if DEBUG
|
|
int worldID = WorldMetaStorage.GetWorldID(worldType);
|
|
string converter(int o) => EcsDebugUtility.GetGenericTypeName(WorldMetaStorage.GetComponentType(worldID, o), 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 Type worldType;
|
|
public readonly int[] inc;
|
|
public readonly int[] exc;
|
|
public readonly Type[] incTypes;
|
|
public readonly Type[] excTypes;
|
|
public DebuggerProxy(EcsMask mask)
|
|
{
|
|
worldType = mask._worldType;
|
|
int worldID = WorldMetaStorage.GetWorldID(worldType);
|
|
inc = mask._inc;
|
|
exc = mask._exc;
|
|
Type converter(int o) => WorldMetaStorage.GetComponentType(worldID, o);
|
|
incTypes = inc.Select(converter).ToArray();
|
|
excTypes = exc.Select(converter).ToArray();
|
|
}
|
|
public override string ToString() => CreateLogString(worldType, inc, exc);
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
#region Iterator
|
|
public ref struct EcsSubjectIterator<TSubject> where TSubject : EcsSubject
|
|
{
|
|
public readonly TSubject s;
|
|
private EcsReadonlyGroup _sourceGroup;
|
|
private Enumerator _enumerator;
|
|
|
|
public EcsSubjectIterator(TSubject s, EcsReadonlyGroup sourceGroup)
|
|
{
|
|
this.s = s;
|
|
_sourceGroup = sourceGroup;
|
|
_enumerator = default;
|
|
}
|
|
|
|
public int Entity
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => _enumerator.Current;
|
|
}
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Begin() => _enumerator = GetEnumerator();
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool Next() => _enumerator.MoveNext();
|
|
public void CopyTo(EcsGroup group)
|
|
{
|
|
group.Clear();
|
|
var enumerator = GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
group.AddInternal(enumerator.Current);
|
|
}
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Enumerator GetEnumerator() => new Enumerator(_sourceGroup, s);
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder result = new StringBuilder();
|
|
foreach (var e in this)
|
|
{
|
|
result.Append(e);
|
|
result.Append(", ");
|
|
}
|
|
return result.ToString();
|
|
}
|
|
|
|
public ref struct Enumerator
|
|
{
|
|
private EcsGroup.Enumerator _sourceGroup;
|
|
private readonly int[] _inc;
|
|
private readonly int[] _exc;
|
|
private readonly IEcsPoolImplementation[] _pools;
|
|
|
|
public Enumerator(EcsReadonlyGroup sourceGroup, EcsSubject subject)
|
|
{
|
|
_sourceGroup = sourceGroup.GetEnumerator();
|
|
_inc = subject.mask._inc;
|
|
_exc = subject.mask._exc;
|
|
_pools = subject.World._pools;
|
|
}
|
|
public int Current
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => _sourceGroup.Current;
|
|
}
|
|
public bool MoveNext()
|
|
{
|
|
while (_sourceGroup.MoveNext())
|
|
{
|
|
int e = _sourceGroup.Current;
|
|
for (int i = 0, iMax = _inc.Length; i < iMax; i++)
|
|
if (!_pools[_inc[i]].Has(e)) goto next;
|
|
for (int i = 0, iMax = _exc.Length; i < iMax; i++)
|
|
if (_pools[_exc[i]].Has(e)) goto next;
|
|
return true;
|
|
next: continue;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|