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.Collections;
|
||||
|
||||
class PagedSparseSet<T> where T : struct
|
||||
class PagedSparseSet
|
||||
{
|
||||
public const uint32 PageSize = 4096;
|
||||
|
||||
private List<List<Entity>> _sparse = new .()~ DeleteContainerAndItems!(_);
|
||||
private List<(Entity, T)> _packed = new .() ~ delete _;
|
||||
private List<T> _packedEntities = new .() ~ delete _;
|
||||
private List<Entity> _packed = 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)
|
||||
|
@ -21,10 +21,11 @@ class PagedSparseSet<T> where T : struct
|
|||
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);
|
||||
_packed.Add((e, toAdd));
|
||||
_packed.Add(e);
|
||||
_packedEntities.Add(toAdd);
|
||||
_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.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;
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
public int_cosize Count() => (.)_packed.Count;
|
||||
|
||||
///Ensure that a page exist and is loaded
|
||||
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;
|
||||
using Theater_ECS.Containers;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
//public typealias Entity = int_cosize;
|
||||
|
||||
public typealias Component = int_cosize;
|
||||
|
||||
class ECS
|
||||
{
|
||||
public int_cosize MaxComponents;
|
||||
private EntityRegister _registry = new .() ~ delete _;
|
||||
|
||||
private int_cosize _ComponentsNext = 0;
|
||||
private List<ComponentManager> _ComponentsPacked = new .() ~ DeleteContainerAndItems!(_);
|
||||
private Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
||||
///Create a new Entity
|
||||
public Entity Entity_Create() => _registry.Create();
|
||||
|
||||
private int_cosize _EntityNext = 0;
|
||||
private List<List<int_cosize>> _EntityList = new .() ~ DeleteContainerAndItems!(_);
|
||||
private Queue<Entity> _EntityAvailable = new .() ~ delete _;
|
||||
private HashSet<Entity> _DeletedEntities = new .() ~ delete _;
|
||||
///Delete an Entity
|
||||
public void Entity_Delete(Entity e) => _registry.Remove(e);
|
||||
|
||||
public Component RegisterComponent<T>(int_cosize count) where T : struct
|
||||
{
|
||||
_ComponentsPacked.Add(new ComponentManager()..Initialize<T>(count));
|
||||
_EntityList.Add(new .(MaxComponents));
|
||||
_ComponentMap.Add(typeof(T), _ComponentsNext);
|
||||
return _ComponentsNext++;
|
||||
}
|
||||
///Returns wether the given entity is still alive
|
||||
public bool Entity_Alive(Entity e) => _registry.IsAlive(e);
|
||||
|
||||
public Component GetComponent<T>()
|
||||
{
|
||||
if(_ComponentMap.ContainsKey(typeof(T)))
|
||||
return _ComponentMap[typeof(T)];
|
||||
return -1;
|
||||
}
|
||||
//TODO: Entity_GetAllComponents
|
||||
|
||||
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)
|
||||
{
|
||||
if(e >= (.)_EntityNext || _DeletedEntities.Contains(e))
|
||||
return;
|
||||
private ComponentRegistry _compRegistry = new .() ~ delete _;
|
||||
|
||||
_DeletedEntities.Add(e);
|
||||
_EntityAvailable.Add(e);
|
||||
///Registers a component but hides some of the work behind generics
|
||||
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)
|
||||
{
|
||||
if(_EntityList[i][(.)e] != -1)
|
||||
{
|
||||
_ComponentsPacked[i].Remove(_EntityList[i][(.)e]);
|
||||
_EntityList[i][(.)e] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
///Retrieves the id of a component
|
||||
public Result<Component> GetComponent<T>() => _compRegistry.GetComponent(typeof(T).GetFullName(.. scope .()));
|
||||
public Result<Component> GetComponent(StringView name) => _compRegistry.GetComponent(name);
|
||||
|
||||
public Result<void*> AddComponentToEntity(Entity e, Component c)
|
||||
{
|
||||
if(e >= (.)_EntityNext || _DeletedEntities.Contains(e))
|
||||
return .Err;
|
||||
public int_cosize GetComponentCount(Component c) => _compRegistry.Components[c].Count();
|
||||
|
||||
var res = _ComponentsPacked[c].Create(e);
|
||||
_EntityList[c][(.)e] = res.0;
|
||||
return res.1;
|
||||
}
|
||||
public Span<Entity> GetComponentEntities(Component c) => _compRegistry.Components[c].GetAll();
|
||||
|
||||
public Result<void*> RemoveComponentFromEntity(Entity e, Component c)
|
||||
{
|
||||
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);
|
||||
}
|
||||
[Inline] public void* GetComponentData(Entity e, Component c) => _compRegistry.Components[c].Get(e);
|
||||
}
|
|
@ -5,7 +5,7 @@ using System;
|
|||
public struct Entity : uint32
|
||||
{
|
||||
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 uint8 VersionOffset => 20;
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ class EntityRegister
|
|||
{
|
||||
if(_available == 0)
|
||||
{
|
||||
#if DEBUG
|
||||
if(_entities.Count + 1 > Entity.IndexMask)
|
||||
return .Null; //It would be beneficial if we could avoid this check
|
||||
#endif
|
||||
return .((.)_entities..Add(.((.)_entities.Count, 0)).Count, 0);
|
||||
}
|
||||
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 y = (.)gRand.NextI32() % 25;
|
||||
|
||||
public void Update(float a, float b) mut
|
||||
{
|
||||
x += a;
|
||||
y += b;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
namespace Theater_ECS;
|
||||
using Theater_ECS.Example;
|
||||
using Theater_ECS.Containers;
|
||||
|
||||
|
||||
using System;
|
||||
|
@ -7,47 +8,88 @@ using System.Collections;
|
|||
|
||||
class Program
|
||||
{
|
||||
public static uint64 counter = 0;
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
EntityRegister e = scope .();
|
||||
PagedSparseSet<Position> p = scope .();
|
||||
PagedSparseSet<Velocity> v =scope .();
|
||||
List<Entity> es = new .();
|
||||
defer delete es;
|
||||
for(int i < 1000000)
|
||||
{
|
||||
var ent = e.Create();
|
||||
p.Add(ent, .());
|
||||
v.Add(ent, .());
|
||||
es.Add(ent);
|
||||
}
|
||||
ECS e = scope .();
|
||||
MovementSystem s = scope .();
|
||||
s.RegisterSystem(e);
|
||||
|
||||
System.Diagnostics.Stopwatch watch = scope .();
|
||||
for(int a < 10)
|
||||
{
|
||||
watch.Start();
|
||||
for(int i < 10)
|
||||
{
|
||||
var pos = p.GetAll();
|
||||
var vel = v.GetAll();
|
||||
|
||||
for(var ps in pos)
|
||||
{
|
||||
ps.1.x += vel[@ps.Index].1.x;
|
||||
ps.1.y += vel[@ps.Index].1.y;
|
||||
}
|
||||
}
|
||||
Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
|
||||
watch.Stop();
|
||||
watch.Reset();
|
||||
for(int32 i < 500000)
|
||||
{
|
||||
var entity = e.Entity_Create();
|
||||
e.[Friend]_compRegistry.Components[s.[Friend]_Components[0]].Add(entity, &Position());
|
||||
e.[Friend]_compRegistry.Components[s.[Friend]_Components[1]].Add(entity, &Velocity());
|
||||
/*if((gRand.NextI32() % 3) == 1)
|
||||
e.[Friend]_compRegistry.Components[s.[Friend]_Components[2]].Add(entity, &waste());*/
|
||||
}
|
||||
|
||||
|
||||
System.Diagnostics.Stopwatch watch = scope .();
|
||||
for(int o < 10)
|
||||
{
|
||||
watch.Start();
|
||||
for(int ii < 400)
|
||||
{
|
||||
|
||||
s.RunSystem(e);
|
||||
|
||||
}
|
||||
Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
|
||||
watch.Stop();
|
||||
watch.Reset();
|
||||
}
|
||||
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;
|
||||
using Theater_ECS.Containers;
|
||||
using Theater_ECS.Example;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
abstract class System
|
||||
{
|
||||
protected Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
||||
protected List<Entity> _Target = new .(100) ~ delete _;
|
||||
private List<Component> _Components = new .(10) ~ 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