More Dynamic system
This commit is contained in:
parent
9bf9452cbe
commit
55077fc7c3
13 changed files with 420 additions and 386 deletions
|
@ -1,109 +0,0 @@
|
||||||
namespace Theater_ECS;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
class ComponentManager
|
|
||||||
{
|
|
||||||
private Monitor _Lock = new .() ~ delete _;
|
|
||||||
|
|
||||||
private int_cosize _ComponentSize = -1;
|
|
||||||
private readonly int _IndexSize = sizeof(int_cosize);
|
|
||||||
|
|
||||||
private int_cosize _Next = 0;
|
|
||||||
private List<uint8> _Data = null ~ delete _;
|
|
||||||
private List<int_cosize> _Available = new .() ~ delete _;
|
|
||||||
private HashSet<int_cosize> _Deleted = new .() ~ delete _;
|
|
||||||
|
|
||||||
///Make the component manager useable for a specific type of component
|
|
||||||
public void Initialize<T>(int_cosize count) where T : struct
|
|
||||||
{
|
|
||||||
_ComponentSize = sizeof(T);
|
|
||||||
_Data = new .( //So that we dont have to realloc as frequently
|
|
||||||
(_IndexSize + _ComponentSize) * count
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
///Creates space for a new component instance and returns a pointer to it
|
|
||||||
public (Component, void*) Create(Entity owner)
|
|
||||||
{
|
|
||||||
if (_Available.Count > 0)
|
|
||||||
{
|
|
||||||
var idx = _Available.PopFront();
|
|
||||||
_Deleted.Remove(idx);
|
|
||||||
|
|
||||||
*(int_cosize*)(void*)&_Data[idx * (_ComponentSize + _IndexSize)] = (.)owner;
|
|
||||||
void* toReturn = (void*)&_Data[idx * (_ComponentSize + _IndexSize) + _IndexSize];
|
|
||||||
return (
|
|
||||||
idx,
|
|
||||||
toReturn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Grow();
|
|
||||||
*(int_cosize*)(void*)(_Data.Ptr + (_Next * (_ComponentSize + _IndexSize))) = (.)owner;
|
|
||||||
//This calculates to the next biggest position + the offset of the index
|
|
||||||
void* toReturn = (void*)(_Data.Ptr + (_Next * (_ComponentSize + _IndexSize) + _IndexSize));
|
|
||||||
return (
|
|
||||||
_Next++,
|
|
||||||
toReturn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///Remove an entry, and returns a pointer to the removed object, if you want to
|
|
||||||
public Result<void*> Remove(int_cosize idx)
|
|
||||||
{
|
|
||||||
if (idx >= _Next || _Deleted.Contains(idx))
|
|
||||||
return .Err;
|
|
||||||
|
|
||||||
*(int_cosize*)(void*)&_Data[idx * (_ComponentSize + _IndexSize)] = -1;
|
|
||||||
|
|
||||||
_Deleted.Add(idx);
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
(void*)(_Data.Ptr + idx * (_ComponentSize + _IndexSize) + _IndexSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int_cosize Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (.)(_Data.Count / (_ComponentSize + _IndexSize));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Span<(Entity, T)> GetAll<T>() where T : struct
|
|
||||||
{
|
|
||||||
return .((.)(void*)_Data.Ptr, Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Unchecked]
|
|
||||||
public T* Get<T>(int_cosize idx) where T : struct
|
|
||||||
{
|
|
||||||
return (T*)(void*)(_Data.Ptr + idx * (_ComponentSize + _IndexSize) + _IndexSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Unchecked]
|
|
||||||
public void GetEntities(List<Entity> list)
|
|
||||||
{
|
|
||||||
var ec = (_Data.Count / (_ComponentSize + _IndexSize));
|
|
||||||
list.GrowUninitialized(ec);
|
|
||||||
for (int i < ec)
|
|
||||||
*(list.Ptr + (i)) = *(Entity*)(void*)(_Data.Ptr +i * (_ComponentSize + _IndexSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Use() => _Lock.Enter();
|
|
||||||
public void StopUsing() => _Lock.Exit();
|
|
||||||
|
|
||||||
|
|
||||||
public void Grow(int_cosize amount = 1)
|
|
||||||
{ //Make more size useable
|
|
||||||
_Data.GrowUninitialized((_ComponentSize + _IndexSize) * amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
52
src/ComponentRegistry.bf
Normal file
52
src/ComponentRegistry.bf
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
using Theater_ECS.Containers;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
typealias Component = int_cosize;
|
||||||
|
|
||||||
|
class ComponentRegistry
|
||||||
|
{
|
||||||
|
|
||||||
|
private Dictionary<String, Component> _componentLookup = new .() ~ DeleteDictionaryAndKeys!(_);
|
||||||
|
public List<PagedSparseSet> Components = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
|
||||||
|
///Registers a component based on a name and a size of that component
|
||||||
|
public Result<Component> RegisterComponent(StringView name, int_cosize size)
|
||||||
|
{
|
||||||
|
///Return the existing component if it matches
|
||||||
|
if(_componentLookup.ContainsKeyAlt<StringView>(name)
|
||||||
|
&& Components[_componentLookup[scope .(name)]].[Friend]_packedEntities.[Friend]_size == size)
|
||||||
|
return _componentLookup[scope .(name)];
|
||||||
|
else if(_componentLookup.ContainsKeyAlt<StringView>(name))
|
||||||
|
{
|
||||||
|
return .Err; //There already is a component of the same name but with a different size
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attempt to find a nulled out entry
|
||||||
|
//This could be made faster but thats unlikely to ever matter
|
||||||
|
for(var i in ref Components)
|
||||||
|
if(i == null)
|
||||||
|
{
|
||||||
|
i = new .(size);
|
||||||
|
_componentLookup.Add(new .(name), (.)@i.Index);
|
||||||
|
return (Component)@i.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make a new one and return it
|
||||||
|
Components.Add(new .(size));
|
||||||
|
_componentLookup.Add(new .(name), (.)(Components.Count-1));
|
||||||
|
return (.)(Components.Count-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Retrieves the id of a component
|
||||||
|
public Result<Component> GetComponent(StringView name)
|
||||||
|
{
|
||||||
|
if(!_componentLookup.ContainsKeyAlt<StringView>(name))
|
||||||
|
return .Err;
|
||||||
|
return _componentLookup[scope .(name)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
namespace Theater_ECS;
|
namespace Theater_ECS.Containers;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
class PagedSparseSet<T> where T : struct
|
class PagedSparseSet
|
||||||
{
|
{
|
||||||
public const uint32 PageSize = 4096;
|
public const uint32 PageSize = 4096;
|
||||||
|
|
||||||
private List<List<Entity>> _sparse = new .()~ DeleteContainerAndItems!(_);
|
private List<List<Entity>> _sparse = new .()~ DeleteContainerAndItems!(_);
|
||||||
private List<(Entity, T)> _packed = new .() ~ delete _;
|
private List<Entity> _packed = new .() ~ delete _;
|
||||||
private List<T> _packedEntities = new .() ~ delete _;
|
private UList _packedEntities ~ delete _;
|
||||||
|
|
||||||
public (Entity, T) this[Entity e]
|
public this(int_cosize size)
|
||||||
{
|
{
|
||||||
public get => _packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index];
|
_packedEntities = new .(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(Entity e)
|
public bool Contains(Entity e)
|
||||||
|
@ -21,10 +21,11 @@ class PagedSparseSet<T> where T : struct
|
||||||
return _sparse[e.Index/PageSize][e.Index % PageSize] != Entity.Null;
|
return _sparse[e.Index/PageSize][e.Index % PageSize] != Entity.Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(Entity e, T toAdd)
|
public void Add(Entity e, void* toAdd)
|
||||||
{
|
{
|
||||||
EnsureLoadedPage(e.Index/PageSize);
|
EnsureLoadedPage(e.Index/PageSize);
|
||||||
_packed.Add((e, toAdd));
|
_packed.Add(e);
|
||||||
|
_packedEntities.Add(toAdd);
|
||||||
_sparse[e.Index/PageSize][e.Index % PageSize] = (.)_packed.Count-1;
|
_sparse[e.Index/PageSize][e.Index % PageSize] = (.)_packed.Count-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,22 +40,25 @@ class PagedSparseSet<T> where T : struct
|
||||||
_packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index] = b;
|
_packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index] = b;
|
||||||
_packed.Back = toRm;
|
_packed.Back = toRm;
|
||||||
|
|
||||||
_sparse[b.0.Index/PageSize][b.0.Index % PageSize] = Entity(e.Index, b.0.Version);
|
_sparse[b.Index/PageSize][b.Index % PageSize] = Entity(e.Index, b.Version);
|
||||||
_sparse[e.Index/PageSize][e.Index % PageSize] = Entity.Null;
|
_sparse[e.Index/PageSize][e.Index % PageSize] = Entity.Null;
|
||||||
|
|
||||||
_packed.PopBack();
|
_packed.PopBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T* Get(Entity e)
|
[Inline]
|
||||||
|
public void* Get(Entity e)
|
||||||
{
|
{
|
||||||
return &_packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index].1;
|
return _packedEntities[(.)(_sparse[e.Index/PageSize][e.Index % PageSize].Index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span<(Entity, T)> GetAll()
|
public Span<Entity> GetAll()
|
||||||
{
|
{
|
||||||
return _packed;
|
return _packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int_cosize Count() => (.)_packed.Count;
|
||||||
|
|
||||||
///Ensure that a page exist and is loaded
|
///Ensure that a page exist and is loaded
|
||||||
public void EnsureLoadedPage(uint32 page)
|
public void EnsureLoadedPage(uint32 page)
|
||||||
{
|
{
|
162
src/Containers/UList.bf
Normal file
162
src/Containers/UList.bf
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
namespace Theater_ECS.Containers;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
//A variant of list, which has no type associated with it
|
||||||
|
class UList
|
||||||
|
{
|
||||||
|
private const int_cosize _defaultCapacity = 4;
|
||||||
|
private const int_cosize _max = int_cosize.MaxValue;
|
||||||
|
|
||||||
|
private void* _items; //The raw data contained by this object
|
||||||
|
private readonly int_cosize _sizeof; //The size of a single object
|
||||||
|
private int_cosize _size; //How much data is actually used
|
||||||
|
private int_cosize _alloc; //How much data is actually allocated
|
||||||
|
|
||||||
|
/// Adds an item to the back of the list.
|
||||||
|
public void Add<T>(T* toAdd)
|
||||||
|
{
|
||||||
|
if (_size == _alloc)
|
||||||
|
Realloc(_alloc * 2);
|
||||||
|
Internal.MemCpy(&((uint8*)_items)[_size], toAdd, sizeof(T));
|
||||||
|
_size += _sizeof;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an item to the back of the list.
|
||||||
|
public void Add(void* toAdd)
|
||||||
|
{
|
||||||
|
if (_size == _alloc)
|
||||||
|
Realloc(_alloc * 2);
|
||||||
|
Internal.MemCpy(&((uint8*)_items)[_size], toAdd, _sizeof);
|
||||||
|
_size += _sizeof;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int_cosize Capacity
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get => (_alloc / _sizeof);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != _alloc / _sizeof)
|
||||||
|
Realloc(value * _sizeof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int_cosize Count
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get => (_size / _sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void* this[int_cosize index]
|
||||||
|
{
|
||||||
|
[Checked]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Runtime.Assert(index < (_size/_sizeof));
|
||||||
|
return GetItem(_items, index * _sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Unchecked, Inline]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetItem(_items, index* _sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Checked]
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Runtime.Assert(index < (_size/_sizeof));
|
||||||
|
MemCpy(GetItem(_items, index * _sizeof), value, _sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Unchecked, Inline]
|
||||||
|
set
|
||||||
|
{
|
||||||
|
MemCpy(GetItem(_items, index * _sizeof), value, _sizeof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void* GetItem(void* i, int32 index) => (uint8*)i + index;
|
||||||
|
|
||||||
|
private void MemCpy(void* dest, void* src, int lenght) => Internal.MemCpy(dest, src, lenght);
|
||||||
|
|
||||||
|
#region Initializers
|
||||||
|
public this(int_cosize size)
|
||||||
|
{
|
||||||
|
_sizeof = size;
|
||||||
|
Realloc(size * _defaultCapacity);
|
||||||
|
_alloc = size * _defaultCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public this(int_cosize size, int_cosize capacity)
|
||||||
|
{
|
||||||
|
_sizeof = size;
|
||||||
|
Debug.Assert((uint)capacity <= (uint)_max);
|
||||||
|
Realloc(size * capacity);
|
||||||
|
_alloc = size * capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
var items = _items;
|
||||||
|
|
||||||
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||||
|
// To avoid scanning items being deleted
|
||||||
|
_items = null;
|
||||||
|
Interlocked.Fence();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Free(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region MemoryAllocation
|
||||||
|
private void Realloc(int_cosize newSize)
|
||||||
|
{
|
||||||
|
void* oldAlloc = null;
|
||||||
|
if (newSize > 0)
|
||||||
|
{
|
||||||
|
void* newItems = Alloc(newSize);
|
||||||
|
|
||||||
|
if (_size > 0)
|
||||||
|
Internal.MemCpy(newItems, _items, _size);
|
||||||
|
oldAlloc = _items;
|
||||||
|
_items = (.)newItems;
|
||||||
|
_alloc = newSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oldAlloc = _items;
|
||||||
|
_items = null;
|
||||||
|
_alloc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(oldAlloc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void* Alloc(int_cosize size)
|
||||||
|
{
|
||||||
|
return Internal.AllocRawArrayUnmarked<uint8>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Free(void* val)
|
||||||
|
{
|
||||||
|
delete val;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
102
src/ECS.bf
102
src/ECS.bf
|
@ -1,102 +1,38 @@
|
||||||
namespace Theater_ECS;
|
namespace Theater_ECS;
|
||||||
|
using Theater_ECS.Containers;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
//public typealias Entity = int_cosize;
|
|
||||||
|
|
||||||
public typealias Component = int_cosize;
|
|
||||||
|
|
||||||
class ECS
|
class ECS
|
||||||
{
|
{
|
||||||
public int_cosize MaxComponents;
|
private EntityRegister _registry = new .() ~ delete _;
|
||||||
|
|
||||||
private int_cosize _ComponentsNext = 0;
|
///Create a new Entity
|
||||||
private List<ComponentManager> _ComponentsPacked = new .() ~ DeleteContainerAndItems!(_);
|
public Entity Entity_Create() => _registry.Create();
|
||||||
private Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
|
||||||
|
|
||||||
private int_cosize _EntityNext = 0;
|
///Delete an Entity
|
||||||
private List<List<int_cosize>> _EntityList = new .() ~ DeleteContainerAndItems!(_);
|
public void Entity_Delete(Entity e) => _registry.Remove(e);
|
||||||
private Queue<Entity> _EntityAvailable = new .() ~ delete _;
|
|
||||||
private HashSet<Entity> _DeletedEntities = new .() ~ delete _;
|
|
||||||
|
|
||||||
public Component RegisterComponent<T>(int_cosize count) where T : struct
|
///Returns wether the given entity is still alive
|
||||||
{
|
public bool Entity_Alive(Entity e) => _registry.IsAlive(e);
|
||||||
_ComponentsPacked.Add(new ComponentManager()..Initialize<T>(count));
|
|
||||||
_EntityList.Add(new .(MaxComponents));
|
|
||||||
_ComponentMap.Add(typeof(T), _ComponentsNext);
|
|
||||||
return _ComponentsNext++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Component GetComponent<T>()
|
//TODO: Entity_GetAllComponents
|
||||||
{
|
|
||||||
if(_ComponentMap.ContainsKey(typeof(T)))
|
|
||||||
return _ComponentMap[typeof(T)];
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity CreateEntity()
|
|
||||||
{
|
|
||||||
if(_EntityAvailable.Count > 0)
|
|
||||||
{
|
|
||||||
var e = _EntityAvailable.PopFront();
|
|
||||||
_DeletedEntities.Remove(e);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(var i in _EntityList)
|
|
||||||
i.Add(-1);
|
|
||||||
return (.)_EntityNext++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveEntity(Entity e)
|
private ComponentRegistry _compRegistry = new .() ~ delete _;
|
||||||
{
|
|
||||||
if(e >= (.)_EntityNext || _DeletedEntities.Contains(e))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_DeletedEntities.Add(e);
|
///Registers a component but hides some of the work behind generics
|
||||||
_EntityAvailable.Add(e);
|
public Component RegisterComponent<T>() => _compRegistry.RegisterComponent(typeof(T).GetFullName(.. scope .()), sizeof(T));
|
||||||
|
public Component RegisterComponent(StringView name, int_cosize size) => _compRegistry.RegisterComponent(name, size);
|
||||||
|
|
||||||
for(int i < _EntityList.Count)
|
///Retrieves the id of a component
|
||||||
{
|
public Result<Component> GetComponent<T>() => _compRegistry.GetComponent(typeof(T).GetFullName(.. scope .()));
|
||||||
if(_EntityList[i][(.)e] != -1)
|
public Result<Component> GetComponent(StringView name) => _compRegistry.GetComponent(name);
|
||||||
{
|
|
||||||
_ComponentsPacked[i].Remove(_EntityList[i][(.)e]);
|
|
||||||
_EntityList[i][(.)e] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<void*> AddComponentToEntity(Entity e, Component c)
|
public int_cosize GetComponentCount(Component c) => _compRegistry.Components[c].Count();
|
||||||
{
|
|
||||||
if(e >= (.)_EntityNext || _DeletedEntities.Contains(e))
|
|
||||||
return .Err;
|
|
||||||
|
|
||||||
var res = _ComponentsPacked[c].Create(e);
|
public Span<Entity> GetComponentEntities(Component c) => _compRegistry.Components[c].GetAll();
|
||||||
_EntityList[c][(.)e] = res.0;
|
|
||||||
return res.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<void*> RemoveComponentFromEntity(Entity e, Component c)
|
[Inline] public void* GetComponentData(Entity e, Component c) => _compRegistry.Components[c].Get(e);
|
||||||
{
|
|
||||||
if(e >= (.)_EntityNext || _DeletedEntities.Contains(e))
|
|
||||||
return .Err;
|
|
||||||
|
|
||||||
return _ComponentsPacked[c].Remove((.)e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetEntityFromComponent(Component c, List<Entity> e)
|
|
||||||
{
|
|
||||||
_ComponentsPacked[c].[Unchecked]GetEntities(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExecuteSystem<A>(delegate void(ECS, Entity, ref A) func) where A : struct
|
|
||||||
{
|
|
||||||
var idx = GetComponent<A>();
|
|
||||||
var data = _ComponentsPacked[idx].GetAll<A>();
|
|
||||||
for(var i in ref data)
|
|
||||||
func.Invoke(this, i.0, ref i.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
public struct Entity : uint32
|
public struct Entity : uint32
|
||||||
{
|
{
|
||||||
public static readonly Entity Null => 0x000FFFFF;
|
public static readonly Entity Null => 0x000FFFFF;
|
||||||
public static readonly uint32 VersionMask => 0xFFF00000;
|
public static readonly Entity VersionMask => (.)0xFFF00000;
|
||||||
public static readonly uint32 IndexMask => 0x000FFFFF;
|
public static readonly uint32 IndexMask => 0x000FFFFF;
|
||||||
public static readonly uint8 VersionOffset => 20;
|
public static readonly uint8 VersionOffset => 20;
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,10 @@ class EntityRegister
|
||||||
{
|
{
|
||||||
if(_available == 0)
|
if(_available == 0)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
if(_entities.Count + 1 > Entity.IndexMask)
|
if(_entities.Count + 1 > Entity.IndexMask)
|
||||||
return .Null; //It would be beneficial if we could avoid this check
|
return .Null; //It would be beneficial if we could avoid this check
|
||||||
|
#endif
|
||||||
return .((.)_entities..Add(.((.)_entities.Count, 0)).Count, 0);
|
return .((.)_entities..Add(.((.)_entities.Count, 0)).Count, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace Theater_ECS.Example;
|
|
||||||
|
|
||||||
|
|
||||||
class MoverSystem : System
|
|
||||||
{
|
|
||||||
|
|
||||||
[SystemMethod("MoveItems", typeof(Velocity), typeof(Position))]
|
|
||||||
public void MoveItems(ref Velocity v,ref Position p)
|
|
||||||
{
|
|
||||||
p.x = v.x + p.x;
|
|
||||||
p.y = v.y = p.y;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,4 +6,10 @@ struct Position
|
||||||
{
|
{
|
||||||
public float x = (.)gRand.NextI32() % 25;
|
public float x = (.)gRand.NextI32() % 25;
|
||||||
public float y = (.)gRand.NextI32() % 25;
|
public float y = (.)gRand.NextI32() % 25;
|
||||||
|
|
||||||
|
public void Update(float a, float b) mut
|
||||||
|
{
|
||||||
|
x += a;
|
||||||
|
y += b;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
namespace Theater_ECS;
|
namespace Theater_ECS;
|
||||||
using Theater_ECS.Example;
|
using Theater_ECS.Example;
|
||||||
|
using Theater_ECS.Containers;
|
||||||
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -7,37 +8,32 @@ using System.Collections;
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
public static uint64 counter = 0;
|
|
||||||
|
|
||||||
public static void Main()
|
public static void Main()
|
||||||
{
|
{
|
||||||
EntityRegister e = scope .();
|
ECS e = scope .();
|
||||||
PagedSparseSet<Position> p = scope .();
|
MovementSystem s = scope .();
|
||||||
PagedSparseSet<Velocity> v =scope .();
|
s.RegisterSystem(e);
|
||||||
List<Entity> es = new .();
|
|
||||||
defer delete es;
|
|
||||||
for(int i < 1000000)
|
for(int32 i < 500000)
|
||||||
{
|
{
|
||||||
var ent = e.Create();
|
var entity = e.Entity_Create();
|
||||||
p.Add(ent, .());
|
e.[Friend]_compRegistry.Components[s.[Friend]_Components[0]].Add(entity, &Position());
|
||||||
v.Add(ent, .());
|
e.[Friend]_compRegistry.Components[s.[Friend]_Components[1]].Add(entity, &Velocity());
|
||||||
es.Add(ent);
|
/*if((gRand.NextI32() % 3) == 1)
|
||||||
|
e.[Friend]_compRegistry.Components[s.[Friend]_Components[2]].Add(entity, &waste());*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
System.Diagnostics.Stopwatch watch = scope .();
|
System.Diagnostics.Stopwatch watch = scope .();
|
||||||
for(int a < 10)
|
for(int o < 10)
|
||||||
{
|
{
|
||||||
watch.Start();
|
watch.Start();
|
||||||
for(int i < 10)
|
for(int ii < 400)
|
||||||
{
|
{
|
||||||
var pos = p.GetAll();
|
|
||||||
var vel = v.GetAll();
|
|
||||||
|
|
||||||
for(var ps in pos)
|
s.RunSystem(e);
|
||||||
{
|
|
||||||
ps.1.x += vel[@ps.Index].1.x;
|
|
||||||
ps.1.y += vel[@ps.Index].1.y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
|
Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
@ -45,9 +41,55 @@ class Program
|
||||||
}
|
}
|
||||||
Console.Read();
|
Console.Read();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct waste
|
struct waste
|
||||||
{
|
{
|
||||||
private float[10] a;
|
uint32[4] asd;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
A definition of the functionality and usage of the Theater-ECS
|
||||||
|
|
||||||
|
Goals:
|
||||||
|
- Fast
|
||||||
|
The target is handling the maximum alive at one point amount ~1 048 000 components in a single frame
|
||||||
|
- Use what you need
|
||||||
|
Allocations should be kept at a minimum (Paging), and components and systems should not be tracked unless they
|
||||||
|
are used and the user explicitly asks for them
|
||||||
|
- Dynamic
|
||||||
|
The ecs should by default be fully dynamic, meaning supporting abitrary systems and components.
|
||||||
|
Also Types should not be hardlocked to a single component, so that one type can be used for multiple
|
||||||
|
components.
|
||||||
|
|
||||||
|
Components:
|
||||||
|
- ECS
|
||||||
|
-CreateEntity
|
||||||
|
Create a new entity and return it
|
||||||
|
-DeleteEntity
|
||||||
|
Delete the input identity
|
||||||
|
-IsAlive
|
||||||
|
Check wether a given identity is alive
|
||||||
|
-GetAllData
|
||||||
|
Return Span(ComponentId, void*) containing every component that has data on this entity
|
||||||
|
|
||||||
|
-RegisterComponent
|
||||||
|
Create a new container and return the id of the component
|
||||||
|
-GetComponentId
|
||||||
|
Returns the component id of a given input
|
||||||
|
-AddComponentToEntity
|
||||||
|
creates a component data for the entry and returns a pointer to it
|
||||||
|
-RemoveComponentFromEntity
|
||||||
|
Deataches a component from an entity
|
||||||
|
-GetComponentData
|
||||||
|
Returns a Span<Entity, ComponentData> for a given component
|
||||||
|
|
||||||
|
-RegisterSystem
|
||||||
|
Add a system the the ecs object
|
||||||
|
-RunSystem
|
||||||
|
Run a single run of a system without registering it
|
||||||
|
-RunSystems
|
||||||
|
Run all available systems
|
||||||
|
-GetSystemEnumerator
|
||||||
|
Retrieve an IEnumerable for a given set of component IDS
|
||||||
|
*/
|
|
@ -1,15 +1,96 @@
|
||||||
namespace Theater_ECS;
|
namespace Theater_ECS;
|
||||||
|
using Theater_ECS.Containers;
|
||||||
|
using Theater_ECS.Example;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
abstract class System
|
abstract class System
|
||||||
{
|
{
|
||||||
protected Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
private List<Component> _Components = new .(10) ~ delete _;
|
||||||
protected List<Entity> _Target = new .(100) ~ delete _;
|
|
||||||
|
|
||||||
public abstract void RunSystem(ECS ecs);
|
///Get the compoent id and cache it inside of this system
|
||||||
|
public void RegisterComponent<T>(ECS ecs) where T : struct => _Components.Add(ecs.RegisterComponent<T>());
|
||||||
|
|
||||||
public abstract bool IntializeSystem(ECS ecs);
|
public abstract void RegisterSystem(ECS ecs);
|
||||||
|
|
||||||
|
public virtual void Run(void* p1) { };
|
||||||
|
public virtual void Run(void* p1, void* p2) { };
|
||||||
|
public virtual void Run(void* p1, void* p2, void* p3) { };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void RunSystem(ECS ecs)
|
||||||
|
{
|
||||||
|
int_cosize count = int_cosize.MaxValue;
|
||||||
|
Component comp = _Components[0];
|
||||||
|
|
||||||
|
for(var c in _Components)
|
||||||
|
if(ecs.GetComponentCount(c) < count)
|
||||||
|
{
|
||||||
|
count = ecs.GetComponentCount(c);
|
||||||
|
comp = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//We now have the list of entities to loop through
|
||||||
|
Span<Entity> entities = ecs.GetComponentEntities(comp);
|
||||||
|
UList main = ecs.[Friend]_compRegistry.Components[comp].[Friend]_packedEntities;
|
||||||
|
|
||||||
|
switch(_Components.Count)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
if(comp == _Components[0])
|
||||||
|
{
|
||||||
|
var cun = entities.Length-1;
|
||||||
|
for(int ii < cun)
|
||||||
|
{
|
||||||
|
this.Run(
|
||||||
|
main[(.)entities[ii].Index],
|
||||||
|
ecs.GetComponentData(entities[ii], _Components[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
var cun = entities.Length-1;
|
||||||
|
for(int ii < cun)
|
||||||
|
{
|
||||||
|
this.Run(
|
||||||
|
ecs.GetComponentData(entities[ii], _Components[0]),
|
||||||
|
ecs.GetComponentData(entities[ii], _Components[1]),
|
||||||
|
ecs.GetComponentData(entities[ii], _Components[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MovementSystem : System
|
||||||
|
{
|
||||||
|
public override void RegisterSystem(ECS ecs)
|
||||||
|
{
|
||||||
|
RegisterComponent<Position>(ecs);
|
||||||
|
//RegisterComponent<waste>(ecs);
|
||||||
|
RegisterComponent<Velocity>(ecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run(void* pos, void* vel)
|
||||||
|
{
|
||||||
|
((Position*)pos).x += ((Velocity*)vel).x;
|
||||||
|
((Position*)pos).y += ((Velocity*)vel).y;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run(void* pos, void* waste, void* vel)
|
||||||
|
{
|
||||||
|
((Position*)pos).x += ((Velocity*)vel).x;
|
||||||
|
((Position*)pos).y += ((Velocity*)vel).y;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,117 +0,0 @@
|
||||||
namespace Theater_ECS;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
[AttributeUsage(.Method)]
|
|
||||||
struct SystemMethodAttribute : Attribute, IOnTypeInit
|
|
||||||
{
|
|
||||||
private StringView _Name;
|
|
||||||
private Span<Type> _Types;
|
|
||||||
|
|
||||||
public this(StringView name, params Span<Type> types)
|
|
||||||
{
|
|
||||||
_Name = name;
|
|
||||||
_Types = types;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Comptime]
|
|
||||||
public void OnTypeInit(Type type, Self* prev)
|
|
||||||
{
|
|
||||||
//InitializeFunction
|
|
||||||
Compiler.EmitTypeBody(type, scope $"""
|
|
||||||
public override bool IntializeSystem(ECS ecs)
|
|
||||||
\{
|
|
||||||
{InitializeSystemTypes( .. scope .())}
|
|
||||||
return true;
|
|
||||||
\}
|
|
||||||
|
|
||||||
""");
|
|
||||||
|
|
||||||
|
|
||||||
Compiler.EmitTypeBody(type, scope $"""
|
|
||||||
[System.DisableChecks]
|
|
||||||
public override void RunSystem(ECS ecs)
|
|
||||||
\{
|
|
||||||
Lock(ecs);
|
|
||||||
|
|
||||||
Component lowest = System.int_cosize.MaxValue;
|
|
||||||
System.int_cosize lowestAmount = System.int_cosize.MaxValue;
|
|
||||||
for(var c in _ComponentMap)
|
|
||||||
\{
|
|
||||||
if(ecs.[System.Friend]_ComponentsPacked[c.value].Count < lowestAmount)
|
|
||||||
\{
|
|
||||||
lowest = c.value;
|
|
||||||
lowestAmount = ecs.[System.Friend]_ComponentsPacked[c.value].Count;
|
|
||||||
\}
|
|
||||||
\}
|
|
||||||
|
|
||||||
|
|
||||||
ecs.GetEntityFromComponent(lowest, _Target);
|
|
||||||
|
|
||||||
{CacheComponentData(.. scope .())}
|
|
||||||
|
|
||||||
|
|
||||||
for(var e in _Target)
|
|
||||||
\{
|
|
||||||
|
|
||||||
{_Name}(
|
|
||||||
{GetComponentData(.. scope .())}
|
|
||||||
);
|
|
||||||
\}
|
|
||||||
|
|
||||||
_Target.Clear();
|
|
||||||
|
|
||||||
Unlock(ecs);
|
|
||||||
|
|
||||||
\}
|
|
||||||
|
|
||||||
private void Lock(ECS ecs)
|
|
||||||
\{
|
|
||||||
for(var i in _ComponentMap)
|
|
||||||
ecs.[System.Friend]_ComponentsPacked[i.value].Use();
|
|
||||||
\}
|
|
||||||
|
|
||||||
private void Unlock(ECS ecs)
|
|
||||||
\{
|
|
||||||
for(var i in _ComponentMap)
|
|
||||||
ecs.[System.Friend]_ComponentsPacked[i.value].StopUsing();
|
|
||||||
\}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeSystemTypes(String buffer)
|
|
||||||
{
|
|
||||||
for(var i in _Types)
|
|
||||||
{
|
|
||||||
buffer.Append(scope $"""
|
|
||||||
if(ecs.GetComponent<{i.GetName( .. scope .())}>() == -1)
|
|
||||||
return false;
|
|
||||||
_ComponentMap.Add(typeof({i.GetName(.. scope .())}), ecs.GetComponent<{i.GetName( .. scope .())}>());
|
|
||||||
|
|
||||||
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetComponentData(String buffer)
|
|
||||||
{
|
|
||||||
for(var i in _Types)
|
|
||||||
{
|
|
||||||
buffer.Append(scope $"""
|
|
||||||
ref *ecs.[System.Friend]_ComponentsPacked[_{i.GetName(.. scope .())}].Get<{i.GetName(.. scope .())}>(ecs.[System.Friend]_EntityList[_{i.GetName(.. scope .())}][(.)e])
|
|
||||||
""");
|
|
||||||
if(@i.Index+1 < _Types.Length)
|
|
||||||
buffer.Append(",\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CacheComponentData(String buffer)
|
|
||||||
{
|
|
||||||
for(var i in _Types)
|
|
||||||
{
|
|
||||||
buffer.Append(scope $"Component _{i.GetName(.. scope .())} = _ComponentMap[typeof({i.GetName(.. scope .())})];\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
src/World.bf
12
src/World.bf
|
@ -1,12 +0,0 @@
|
||||||
namespace Theater_ECS;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
class World
|
|
||||||
{
|
|
||||||
EntityRegister Registry = new .() ~ delete _;
|
|
||||||
|
|
||||||
private Dictionary<Type, uint32> _components = new .() ~ delete _;
|
|
||||||
private List<Variant> _componentStorage = new .() ~ DeleteContainerAndDisposeItems!(_);
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue