2023-03-02 14:42:44 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace DCFApixels.DragonECS
|
|
|
|
|
{
|
|
|
|
|
public interface IWorldArchetype { }
|
2023-03-11 17:11:40 +08:00
|
|
|
|
public struct DefaultWorld : IWorldArchetype { }
|
2023-03-02 14:42:44 +08:00
|
|
|
|
|
|
|
|
|
public interface IEcsWorld
|
|
|
|
|
{
|
|
|
|
|
//private float _timeScale;//TODO реализовать собсвенныйтайм склей для разных миров
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
public bool IsEmpty { get; }
|
|
|
|
|
public Type ArchetypeType { get; }
|
2023-03-11 17:11:40 +08:00
|
|
|
|
public int ID { get; }
|
2023-03-02 14:42:44 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public EcsPool<T> GetPool<T>() where T : struct;
|
2023-03-12 20:45:18 +08:00
|
|
|
|
public EcsFilter GetFilter<TInc>() where TInc : struct, IInc;
|
|
|
|
|
public EcsFilter GetFilter<TInc, TExc>() where TInc : struct, IInc where TExc : struct, IExc;
|
2023-03-02 14:42:44 +08:00
|
|
|
|
public ent NewEntity();
|
|
|
|
|
public void Destroy();
|
|
|
|
|
|
|
|
|
|
public bool IsMaskCompatible(Mask mask, int entity);
|
|
|
|
|
public bool IsMaskCompatibleWithout(Mask mask, int entity, int otherPoolID);
|
|
|
|
|
|
|
|
|
|
internal void OnEntityComponentAdded(int entityID, int changedPoolID);
|
|
|
|
|
internal void OnEntityComponentRemoved(int entityID, int changedPoolID);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-11 17:11:40 +08:00
|
|
|
|
|
|
|
|
|
public abstract class EcsWorld
|
2023-03-02 14:42:44 +08:00
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
internal static IEcsWorld[] Worlds = new IEcsWorld[8];
|
|
|
|
|
private static IntDispenser _worldIdDispenser = new IntDispenser();
|
|
|
|
|
|
|
|
|
|
public readonly short id;
|
2023-03-02 14:42:44 +08:00
|
|
|
|
|
2023-03-11 17:11:40 +08:00
|
|
|
|
public EcsWorld()
|
|
|
|
|
{
|
|
|
|
|
id = (short)_worldIdDispenser.GetFree();
|
|
|
|
|
Worlds[id] = (IEcsWorld)this;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sealed class EcsWorld<TArchetype> : EcsWorld, IEcsWorld
|
|
|
|
|
where TArchetype : IWorldArchetype
|
|
|
|
|
{
|
|
|
|
|
private IntDispenser _entityDispenser;
|
|
|
|
|
private EcsGroup _entities;
|
2023-03-02 14:42:44 +08:00
|
|
|
|
|
|
|
|
|
private short[] _gens;
|
2023-03-11 17:11:40 +08:00
|
|
|
|
private short[] _componentCounts;
|
2023-03-02 14:42:44 +08:00
|
|
|
|
|
|
|
|
|
private IEcsPool[] _pools;
|
|
|
|
|
|
|
|
|
|
private List<EcsFilter>[] _filtersByIncludedComponents;
|
|
|
|
|
private List<EcsFilter>[] _filtersByExcludedComponents;
|
|
|
|
|
|
|
|
|
|
private EcsFilter[] _filters;
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
public bool IsEmpty => _entities.Count < 0;
|
|
|
|
|
public Type ArchetypeType => typeof(TArchetype);
|
2023-03-11 17:11:40 +08:00
|
|
|
|
public int ID => id;
|
2023-03-02 14:42:44 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
public EcsWorld()
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
_entityDispenser = new IntDispenser();
|
2023-03-02 14:42:44 +08:00
|
|
|
|
_pools = new IEcsPool[512];
|
2023-03-12 20:45:18 +08:00
|
|
|
|
_gens = new short[512];
|
2023-03-11 17:11:40 +08:00
|
|
|
|
_filters = new EcsFilter[64];
|
|
|
|
|
_entities = new EcsGroup(this, 512);
|
2023-03-12 20:45:18 +08:00
|
|
|
|
_filtersByIncludedComponents = new List<EcsFilter>[16];
|
|
|
|
|
_filtersByExcludedComponents = new List<EcsFilter>[16];
|
2023-03-02 14:42:44 +08:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region GetPool
|
|
|
|
|
public EcsPool<T> GetPool<T>() where T : struct
|
|
|
|
|
{
|
|
|
|
|
int uniqueID = ComponentType<T>.uniqueID;
|
|
|
|
|
|
|
|
|
|
if (uniqueID >= _pools.Length)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref _pools, ComponentType.capacity);
|
|
|
|
|
Array.Resize(ref _filtersByIncludedComponents, ComponentType.capacity);
|
|
|
|
|
Array.Resize(ref _filtersByExcludedComponents, ComponentType.capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_pools[uniqueID] == null)
|
|
|
|
|
{
|
|
|
|
|
_pools[uniqueID] = new EcsPool<T>(this, 512);
|
|
|
|
|
}
|
|
|
|
|
return (EcsPool<T>)_pools[uniqueID];
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region GetFilter
|
2023-03-12 20:45:18 +08:00
|
|
|
|
|
|
|
|
|
public EcsFilter GetFilter<TInc>() where TInc : struct, IInc
|
|
|
|
|
{
|
|
|
|
|
return GetFilterInternal<Mask<TInc>>();
|
|
|
|
|
}
|
|
|
|
|
public EcsFilter GetFilter<TInc, TExc>() where TInc : struct, IInc where TExc : struct, IExc
|
|
|
|
|
{
|
|
|
|
|
return GetFilterInternal<Mask<TInc, TExc>>();
|
|
|
|
|
}
|
|
|
|
|
private EcsFilter GetFilterInternal<TMask>() where TMask : Mask, new()
|
2023-03-02 14:42:44 +08:00
|
|
|
|
{
|
|
|
|
|
var bakedmask = BakedMask<TArchetype, TMask>.Instance;
|
|
|
|
|
|
|
|
|
|
if (_filters.Length >= BakedMask<TArchetype>.capacity)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref _filters, BakedMask<TArchetype>.capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_filters[bakedmask.UniqueID] == null)
|
|
|
|
|
{
|
2023-03-12 20:45:18 +08:00
|
|
|
|
_filters[bakedmask.UniqueID] = NewFilter(bakedmask);
|
2023-03-02 14:42:44 +08:00
|
|
|
|
}
|
|
|
|
|
return _filters[bakedmask.UniqueID];
|
|
|
|
|
}
|
2023-03-12 20:45:18 +08:00
|
|
|
|
|
|
|
|
|
private EcsFilter NewFilter(BakedMask mask, int capacirty = 512)
|
|
|
|
|
{
|
|
|
|
|
var newFilter = new EcsFilter(this, mask, capacirty);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mask.IncCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int poolid = mask.Inc[i];
|
|
|
|
|
var list = _filtersByIncludedComponents[poolid];
|
|
|
|
|
if (list == null)
|
|
|
|
|
{
|
|
|
|
|
list = new List<EcsFilter>(8);
|
|
|
|
|
_filtersByIncludedComponents[poolid] = list;
|
|
|
|
|
}
|
|
|
|
|
list.Add(newFilter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mask.ExcCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int poolid = mask.Exc[i];
|
|
|
|
|
var list = _filtersByExcludedComponents[poolid];
|
|
|
|
|
if (list == null)
|
|
|
|
|
{
|
|
|
|
|
list = new List<EcsFilter>(8);
|
|
|
|
|
_filtersByExcludedComponents[poolid] = list;
|
|
|
|
|
}
|
|
|
|
|
list.Add(newFilter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newFilter;
|
|
|
|
|
}
|
2023-03-02 14:42:44 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region IsMaskCompatible/IsMaskCompatibleWithout
|
|
|
|
|
public bool IsMaskCompatible(Mask mask, int entity)
|
|
|
|
|
{
|
|
|
|
|
BakedMask bakedMask = mask.GetBaked<TArchetype>();
|
|
|
|
|
for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++)
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
if (!_pools[bakedMask.Inc[i]].Has(entity))
|
2023-03-02 14:42:44 +08:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++)
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
if (_pools[bakedMask.Exc[i]].Has(entity))
|
2023-03-02 14:42:44 +08:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsMaskCompatibleWithout(Mask mask, int entity, int otherPoolID)
|
|
|
|
|
{
|
|
|
|
|
BakedMask bakedMask = mask.GetBaked<TArchetype>();
|
|
|
|
|
for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++)
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
int poolID = bakedMask.Inc[i];
|
2023-03-02 14:42:44 +08:00
|
|
|
|
if (poolID == otherPoolID || !_pools[poolID].Has(entity))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++)
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
int poolID = bakedMask.Exc[i];
|
2023-03-02 14:42:44 +08:00
|
|
|
|
if (poolID != otherPoolID && _pools[poolID].Has(entity))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region EntityChangedReact
|
|
|
|
|
void IEcsWorld.OnEntityComponentAdded(int entityID, int changedPoolID)
|
|
|
|
|
{
|
|
|
|
|
var includeList = _filtersByIncludedComponents[changedPoolID];
|
|
|
|
|
var excludeList = _filtersByExcludedComponents[changedPoolID];
|
|
|
|
|
|
|
|
|
|
if (includeList != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var filter in includeList)
|
|
|
|
|
{
|
|
|
|
|
if (IsMaskCompatible(filter.Mask.Mask, entityID))
|
|
|
|
|
{
|
|
|
|
|
filter.Add(entityID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (excludeList != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var filter in excludeList)
|
|
|
|
|
{
|
|
|
|
|
if (IsMaskCompatibleWithout(filter.Mask.Mask, entityID, changedPoolID))
|
|
|
|
|
{
|
|
|
|
|
filter.Remove(entityID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IEcsWorld.OnEntityComponentRemoved(int entityID, int changedPoolID)
|
|
|
|
|
{
|
|
|
|
|
var includeList = _filtersByIncludedComponents[changedPoolID];
|
|
|
|
|
var excludeList = _filtersByExcludedComponents[changedPoolID];
|
|
|
|
|
|
|
|
|
|
if (includeList != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var filter in includeList)
|
|
|
|
|
{
|
|
|
|
|
if (IsMaskCompatible(filter.Mask.Mask, entityID))
|
|
|
|
|
{
|
|
|
|
|
filter.Remove(entityID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (excludeList != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var filter in excludeList)
|
|
|
|
|
{
|
|
|
|
|
if (IsMaskCompatibleWithout(filter.Mask.Mask, entityID, changedPoolID))
|
|
|
|
|
{
|
|
|
|
|
filter.Add(entityID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region NewEntity
|
2023-03-11 17:11:40 +08:00
|
|
|
|
public ent NewEntity()
|
2023-03-02 14:42:44 +08:00
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
int entid = _entityDispenser.GetFree();
|
|
|
|
|
if(_gens.Length < entid) Array.Resize(ref _gens, _gens.Length << 1);
|
|
|
|
|
return new ent(entid, _gens[entid]++, id);
|
2023-03-02 14:42:44 +08:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Destroy
|
|
|
|
|
public void Destroy()
|
|
|
|
|
{
|
2023-03-11 17:11:40 +08:00
|
|
|
|
|
2023-03-02 14:42:44 +08:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Utils
|
|
|
|
|
internal abstract class ComponentType
|
|
|
|
|
{
|
|
|
|
|
internal static int increment = 1;
|
|
|
|
|
internal static int capacity = 512;
|
|
|
|
|
}
|
|
|
|
|
internal sealed class ComponentType<T> : ComponentType
|
|
|
|
|
{
|
|
|
|
|
internal static int uniqueID;
|
|
|
|
|
|
|
|
|
|
static ComponentType()
|
|
|
|
|
{
|
|
|
|
|
uniqueID = increment++;
|
|
|
|
|
#if DEBUG || DCFAECS_NO_SANITIZE_CHECKS
|
|
|
|
|
if (increment + 1 > ushort.MaxValue)
|
|
|
|
|
{
|
|
|
|
|
throw new EcsFrameworkException($"No more room for new component for this {typeof(TArchetype).FullName} IWorldArchetype");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (increment > capacity)
|
|
|
|
|
{
|
|
|
|
|
capacity <<= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|