DragonECS/src/EcsAspect.cs

523 lines
17 KiB
C#
Raw Normal View History

2025-03-14 16:53:25 +08:00
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
2024-11-01 12:41:10 +08:00
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.PoolsCore;
2023-06-26 02:53:55 +08:00
using System;
2024-11-01 12:41:10 +08:00
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
2024-11-09 21:10:12 +08:00
public readonly struct Singleton<T> where T : struct
{
public readonly short WorldID;
public Singleton(short worldID)
{
WorldID = worldID;
EcsWorld.GetData<T>(worldID);
}
public EcsWorld World
{
get { return EcsWorld.GetWorld(WorldID); }
}
public ref T Value
{
get { return ref EcsWorld.GetDataUnchecked<T>(WorldID); }
}
public static implicit operator Singleton<T>(SingletonMarker a) { return new Singleton<T>(a.Builder.World.ID); }
}
2025-03-10 13:00:30 +08:00
public interface IEcsAspect
{
EcsMask Mask { get; set; }
}
#region IEcsAspectExtensions tmp
2025-03-10 22:55:34 +08:00
// public static class IEcsAspectExtensions
// {
// public static void Apply(this IEcsAspect aspect, short worldID, int entityID)
// {
// EcsWorld world = EcsWorld.GetWorld(worldID);
// EcsMask mask = aspect.Mask;
// foreach (var incTypeID in mask._incs)
// {
// var pool = world.FindPoolInstance(incTypeID);
// if (pool != null)
// {
// if (pool.Has(entityID) == false)
// {
// pool.AddEmpty(entityID);
// }
// }
//#if DEBUG
// else
// {
// EcsDebug.PrintWarning("Component has not been added because the pool has not been initialized yet.");
// }
//#endif
// }
// foreach (var excTypeID in mask._excs)
// {
// var pool = world.FindPoolInstance(excTypeID);
// if (pool != null && pool.Has(entityID))
// {
// pool.Del(entityID);
// }
// }
// }
// }
2025-03-10 13:00:30 +08:00
#endregion
public static partial class API
{
public static IncludeMarker Inc
{
get { return EcsAspect.CurrentBuilder.Inc; }
}
public static ExcludeMarker Exc
{
get { return EcsAspect.CurrentBuilder.Exc; }
}
public static OptionalMarker Opt
{
get { return EcsAspect.CurrentBuilder.Opt; }
}
}
public abstract class EcsAspect : IEcsAspect, ITemplateNode, IComponentMask
{
2024-08-23 22:31:43 +08:00
#region Initialization Halpers
2024-03-26 16:06:03 +08:00
[ThreadStatic]
2024-10-31 14:46:21 +08:00
private static Builder[] _constructorBuildersStack;
[ThreadStatic]
private static int _constructorBuildersStackIndex;
2024-11-09 21:10:12 +08:00
protected static Builder B
2024-07-05 22:13:17 +08:00
{
get
{
2024-10-31 14:46:21 +08:00
if (_constructorBuildersStack == null || _constructorBuildersStackIndex < 0)
2024-07-05 22:13:17 +08:00
{
Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(CurrentBuilder));
}
2024-10-31 14:46:21 +08:00
return _constructorBuildersStack[_constructorBuildersStackIndex];
2024-07-05 22:13:17 +08:00
}
}
2025-03-10 13:00:30 +08:00
public static Builder CurrentBuilder
2024-11-09 21:10:12 +08:00
{
get { return B; }
}
2024-03-26 16:06:03 +08:00
protected static IncludeMarker Inc
{
2024-11-09 21:10:12 +08:00
get { return B.Inc; }
2024-03-26 16:06:03 +08:00
}
protected static ExcludeMarker Exc
{
2024-11-09 21:10:12 +08:00
get { return B.Exc; }
2024-03-26 16:06:03 +08:00
}
protected static OptionalMarker Opt
{
2024-11-09 21:10:12 +08:00
get { return B.Opt; }
}
protected static SingletonMarker Singleton
{
get { return B.Singleton; }
2024-03-26 16:06:03 +08:00
}
2024-08-23 22:31:43 +08:00
#endregion
2024-01-07 18:52:54 +08:00
2024-11-03 19:02:07 +08:00
//Инициализация аспектов проходит в синхронизированном состоянии, поэтому использование _staticMaskCache потоко безопасно.
2025-03-24 14:26:40 +08:00
private readonly static Dictionary<Type, EcsStaticMask> _staticMaskCache = new Dictionary<Type, EcsStaticMask>();
2024-11-03 19:02:07 +08:00
2024-08-23 22:31:43 +08:00
internal EcsWorld _source;
internal EcsMask _mask;
private bool _isBuilt = false;
2024-11-01 12:41:10 +08:00
#region Properties
2024-03-02 21:45:09 +08:00
public EcsMask Mask
{
get { return _mask; }
2025-03-10 13:00:30 +08:00
set { }
2024-03-02 21:45:09 +08:00
}
public EcsWorld World
{
get { return _source; }
}
public bool IsInit
{
2024-03-26 16:06:03 +08:00
get { return _isBuilt; }
2024-03-02 21:45:09 +08:00
}
//public ReadOnlySpan<IEcsPool> Pools
//{
// get { return _pools; }
//}
2024-11-01 12:41:10 +08:00
/// <summary>
/// Статическая инициализация означет что каждый новый эекземпляр идентичен другому, инициализация стандартным путем создает идентичные экземпляры, поэтому значение по умолчанию true.
/// </summary>
protected virtual bool IsStaticInitialization
{
get { return true; }
}
#endregion
#region Methods
2024-02-13 21:00:01 +08:00
public bool IsMatches(int entityID)
{
return _source.IsMatchesMask(_mask, entityID);
}
#endregion
#region Builder
protected virtual void Init(Builder b) { }
2024-02-11 01:28:18 +08:00
public sealed class Builder
{
private EcsWorld _world;
2024-10-31 14:46:21 +08:00
private EcsStaticMask.Builder _maskBuilder;
2024-07-05 22:13:17 +08:00
2024-08-26 11:08:42 +08:00
#region Properties
2024-03-08 20:40:19 +08:00
public IncludeMarker Inc
{
get { return new IncludeMarker(this); }
}
public ExcludeMarker Exc
{
get { return new ExcludeMarker(this); }
}
public OptionalMarker Opt
{
get { return new OptionalMarker(this); }
}
2024-11-09 21:10:12 +08:00
public SingletonMarker Singleton
{
get { return new SingletonMarker(this); }
}
2024-08-26 11:08:42 +08:00
public EcsWorld World
{
get { return _world; }
}
#endregion
2024-03-08 20:40:19 +08:00
2024-08-26 11:08:42 +08:00
#region Constructors/New
2024-10-31 14:46:21 +08:00
private Builder() { }
2025-03-10 13:00:30 +08:00
internal static unsafe (TAspect aspect, EcsMask mask) New<TAspect>(EcsWorld world) where TAspect : new()
{
2024-11-01 12:41:10 +08:00
//Get Builder
2024-10-31 14:46:21 +08:00
if (_constructorBuildersStack == null)
{
_constructorBuildersStack = new Builder[4];
_constructorBuildersStackIndex = -1;
}
_constructorBuildersStackIndex++;
if (_constructorBuildersStackIndex >= _constructorBuildersStack.Length)
{
Array.Resize(ref _constructorBuildersStack, _constructorBuildersStack.Length << 1);
}
Builder builder = _constructorBuildersStack[_constructorBuildersStackIndex];
if (builder == null)
{
builder = new Builder();
_constructorBuildersStack[_constructorBuildersStackIndex] = builder;
}
2024-06-27 00:26:05 +08:00
2024-11-01 12:41:10 +08:00
//Setup Builder
EcsStaticMask staticMask = null;
if (_staticMaskCache.TryGetValue(typeof(TAspect), out staticMask) == false)
{
builder._maskBuilder = EcsStaticMask.New();
}
builder._world = world;
//Building
2024-10-31 14:46:21 +08:00
TAspect newAspect = new TAspect();
2025-03-13 13:34:45 +08:00
object newAspectObj = newAspect;
2025-03-10 13:00:30 +08:00
EcsAspect builtinAspect = newAspect as EcsAspect;
2025-03-10 22:55:34 +08:00
if (builtinAspect != null)
2025-03-10 13:00:30 +08:00
{
builtinAspect._source = world;
builtinAspect.Init(builder);
}
2025-03-13 13:34:45 +08:00
OnInit(newAspectObj, builder);
2024-08-26 11:08:42 +08:00
2024-11-01 12:41:10 +08:00
//Build Mask
if (staticMask == null)
{
staticMask = builder._maskBuilder.Build();
builder._maskBuilder = default;
2025-03-10 13:00:30 +08:00
if (builtinAspect == null || builtinAspect.IsStaticInitialization)
2024-11-01 12:41:10 +08:00
{
_staticMaskCache.Add(typeof(TAspect), staticMask);
}
}
2025-03-10 13:00:30 +08:00
EcsMask mask = staticMask.ToMask(world);
2025-03-10 22:55:34 +08:00
if (builtinAspect != null)
2025-03-10 13:00:30 +08:00
{
builtinAspect._mask = mask;
//var pools = new IEcsPool[builder._poolsBufferCount];
//Array.Copy(builder._poolsBuffer, pools, pools.Length);
builtinAspect._isBuilt = true;
}
2024-01-07 18:52:54 +08:00
2024-11-01 12:41:10 +08:00
_constructorBuildersStackIndex--;
2025-03-13 11:19:02 +08:00
2025-03-13 13:34:45 +08:00
OnAfterInit(newAspectObj, mask);
2025-03-13 11:19:02 +08:00
2025-03-13 13:34:45 +08:00
return ((TAspect)newAspectObj, mask);
}
2024-08-26 11:08:42 +08:00
#endregion
2024-04-18 22:14:50 +08:00
#region Include/Exclude/Optional/Combine/Except
2024-11-09 21:10:12 +08:00
public Singleton<T> Get<T>() where T : struct
{
return new Singleton<T>(_world.ID);
}
2024-03-07 03:40:06 +08:00
public TPool IncludePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
2024-11-03 19:02:07 +08:00
var pool = CachePool<TPool>();
SetMaskInclude(pool.ComponentType);
return pool;
}
2024-03-07 03:40:06 +08:00
public TPool ExcludePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
2024-11-03 19:02:07 +08:00
var pool = CachePool<TPool>();
SetMaskExclude(pool.ComponentType);
return pool;
}
2024-03-07 03:40:06 +08:00
public TPool OptionalPool<TPool>() where TPool : IEcsPoolImplementation, new()
{
2024-11-03 19:02:07 +08:00
return CachePool<TPool>();
}
2024-10-05 10:19:52 +08:00
2024-11-03 19:02:07 +08:00
private TPool CachePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
var pool = _world.GetPoolInstance<TPool>();
return pool;
}
public void SetMaskInclude(Type type)
{
2024-11-01 12:41:10 +08:00
if (_maskBuilder.IsNull == false)
{
_maskBuilder.Inc(type);
}
}
public void SetMaskExclude(Type type)
{
2024-11-01 12:41:10 +08:00
if (_maskBuilder.IsNull == false)
{
_maskBuilder.Exc(type);
}
}
2024-07-05 22:13:17 +08:00
public TOtherAspect Combine<TOtherAspect>(int order = 0) where TOtherAspect : EcsAspect, new()
2023-06-03 01:58:54 +08:00
{
2023-06-22 14:31:13 +08:00
var result = _world.GetAspect<TOtherAspect>();
2024-11-01 12:41:10 +08:00
if (_maskBuilder.IsNull == false)
{
_maskBuilder.Combine(result.Mask._staticMask);
}
2023-06-03 01:58:54 +08:00
return result;
}
2024-07-05 22:13:17 +08:00
public TOtherAspect Except<TOtherAspect>(int order = 0) where TOtherAspect : EcsAspect, new()
2024-04-18 22:14:50 +08:00
{
var result = _world.GetAspect<TOtherAspect>();
2024-11-01 12:41:10 +08:00
if (_maskBuilder.IsNull == false)
{
_maskBuilder.Except(result.Mask._staticMask);
}
2024-04-18 22:14:50 +08:00
return result;
}
2023-06-03 01:58:54 +08:00
#endregion
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
{
2024-03-07 03:40:06 +08:00
IncludePool<TPool>();
ExcludePool<TPool>();
OptionalPool<TPool>();
SetMaskInclude(null);
SetMaskExclude(null);
2023-05-28 05:53:08 +08:00
}
#endregion
}
#endregion
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
2024-11-01 12:41:10 +08:00
#region Template
public virtual void Apply(short worldID, int entityID)
{
EcsWorld world = EcsWorld.GetWorld(worldID);
foreach (var incTypeID in _mask._incs)
{
var pool = world.FindPoolInstance(incTypeID);
if (pool != null)
{
if (pool.Has(entityID) == false)
{
2024-11-12 16:36:37 +08:00
pool.AddEmpty(entityID);
2024-11-01 12:41:10 +08:00
}
}
#if DEBUG
else
{
EcsDebug.PrintWarning("Component has not been added because the pool has not been initialized yet.");
}
#endif
}
foreach (var excTypeID in _mask._excs)
{
var pool = world.FindPoolInstance(excTypeID);
if (pool != null && pool.Has(entityID))
{
pool.Del(entityID);
}
}
}
#endregion
#region Other
EcsMask IComponentMask.ToMask(EcsWorld world) { return _mask; }
#endregion
#region Obsolete
2024-08-24 12:29:58 +08:00
[Obsolete("Use EcsMask.GetIterator()")]
public Iterator GetIterator()
{
return new Iterator(Mask.GetIterator(), _source.Entities);
}
[Obsolete("Use EcsMask.GetIterator().Iterate(span)")]
public Iterator GetIteratorFor(EcsSpan span)
{
return new Iterator(Mask.GetIterator(), span);
}
[Obsolete("Use EcsMaskIterator")]
public ref struct Iterator
2023-12-24 15:40:19 +08:00
{
2024-04-16 12:46:09 +08:00
public readonly short worldID;
2024-08-24 12:29:58 +08:00
public readonly EcsMaskIterator.Enumerable iterator;
private EcsSpan _span;
2024-08-24 12:29:58 +08:00
public Iterator(EcsMaskIterator iterator, EcsSpan span)
2023-12-24 15:40:19 +08:00
{
2024-10-31 16:27:53 +08:00
worldID = iterator.World.ID;
_span = span;
2024-08-24 12:29:58 +08:00
this.iterator = iterator.Iterate(span);
}
#region CopyTo
public void CopyTo(EcsGroup group)
{
iterator.CopyTo(group);
}
public int CopyTo(ref int[] array)
{
return iterator.CopyTo(ref array);
}
public EcsSpan CopyToSpan(ref int[] array)
{
2024-10-10 19:57:19 +08:00
int count = CopyTo(ref array);
return new EcsSpan(worldID, array, count);
2024-08-24 12:29:58 +08:00
}
#endregion
public EcsMaskIterator.Enumerable.Enumerator GetEnumerator()
{
return iterator.GetEnumerator();
2023-12-24 15:40:19 +08:00
}
}
2023-06-02 04:00:08 +08:00
#endregion
2025-03-13 11:19:02 +08:00
#region Events
public delegate void OnInitApectHandler(object aspect, Builder builder);
public static event OnInitApectHandler OnInit = delegate { };
public delegate void OnBuildApectHandler(object aspect, EcsMask mask);
public static event OnBuildApectHandler OnAfterInit = delegate { };
#endregion
}
2024-08-23 22:31:43 +08:00
2024-12-04 16:10:09 +08:00
#region EcsAspect.Builder.Extensions
public static class EcsAspectBuilderExtensions
{
public static EcsAspect.Builder Inc<TPool>(this EcsAspect.Builder self, ref TPool pool) where TPool : IEcsPoolImplementation, new()
{
pool = self.IncludePool<TPool>();
return self;
}
public static EcsAspect.Builder Exc<TPool>(this EcsAspect.Builder self, ref TPool pool) where TPool : IEcsPoolImplementation, new()
{
pool = self.ExcludePool<TPool>();
return self;
}
public static EcsAspect.Builder Opt<TPool>(this EcsAspect.Builder self, ref TPool pool) where TPool : IEcsPoolImplementation, new()
{
pool = self.OptionalPool<TPool>();
return self;
}
}
2024-08-26 11:08:42 +08:00
#endregion
2025-03-10 13:00:30 +08:00
}
namespace DCFApixels.DragonECS.Core
{
2024-08-23 22:31:43 +08:00
#region Constraint Markers
2024-03-08 20:40:19 +08:00
public readonly ref struct IncludeMarker
{
private readonly EcsAspect.Builder _builder;
public IncludeMarker(EcsAspect.Builder builder)
{
_builder = builder;
}
2024-08-23 22:31:43 +08:00
public T GetInstance<T>() where T : IEcsPoolImplementation, new()
2024-03-08 20:40:19 +08:00
{
return _builder.IncludePool<T>();
}
}
public readonly ref struct ExcludeMarker
{
private readonly EcsAspect.Builder _builder;
public ExcludeMarker(EcsAspect.Builder builder)
{
_builder = builder;
}
2024-08-23 22:31:43 +08:00
public T GetInstance<T>() where T : IEcsPoolImplementation, new()
2024-03-08 20:40:19 +08:00
{
return _builder.ExcludePool<T>();
}
}
public readonly ref struct OptionalMarker
{
private readonly EcsAspect.Builder _builder;
public OptionalMarker(EcsAspect.Builder builder)
{
_builder = builder;
}
2024-08-23 22:31:43 +08:00
public T GetInstance<T>() where T : IEcsPoolImplementation, new()
2024-03-08 20:40:19 +08:00
{
return _builder.OptionalPool<T>();
}
}
2024-11-09 21:10:12 +08:00
public readonly ref struct SingletonMarker
{
public readonly EcsAspect.Builder Builder;
public SingletonMarker(EcsAspect.Builder builder)
{
Builder = builder;
}
public T Get<T>() where T : struct
{
return Builder.World.Get<T>();
}
}
2024-08-23 22:31:43 +08:00
#endregion
}