Implement the sparse set and entity registry that entt uses/describes
This commit is contained in:
Booklordofthedings 2024-11-12 18:07:00 +01:00
parent 239ddd1b18
commit 9bf9452cbe
6 changed files with 150 additions and 94 deletions

31
src/Entity.bf Normal file
View file

@ -0,0 +1,31 @@
namespace Theater_ECS;
using System;
public struct Entity : uint32
{
public static readonly Entity Null => 0x000FFFFF;
public static readonly uint32 VersionMask => 0xFFF00000;
public static readonly uint32 IndexMask => 0x000FFFFF;
public static readonly uint8 VersionOffset => 20;
public uint32 Index => (.)this & IndexMask;
public uint32 Version => (.)(this & VersionMask) >> VersionOffset
public Entity Next => .(Index, Version + 1);
public this()
{
this = Entity.Null;
};
public this(uint32 index, uint32 version)
{
this = (index & IndexMask) | ((version << VersionOffset) & VersionMask);
}
public override void ToString(String strBuffer)
{
strBuffer.Append(scope $"{Index}:{Version}");
}
}

View file

@ -3,35 +3,6 @@ namespace Theater_ECS;
using System; using System;
using System.Collections; using System.Collections;
static
{
public struct Entity : uint32
{
public static readonly Entity Null => 0xFFFFF;
public static readonly uint32 VersionMask => 0xFFF00000;
public static readonly uint32 IndexMask => 0x000FFFFF;
public static readonly uint8 VersionOffset => 20;
public uint32 Index => (.)this & IndexMask;
public uint32 Version => (.)(this & VersionMask) >> VersionOffset
public Entity Next => .(Index, Version + 1);
public this() {};
public this(uint32 index, uint32 version)
{
this = (index & IndexMask) | ((version << VersionOffset) & VersionMask);
}
public override void ToString(String strBuffer)
{
strBuffer.Append(scope $"{Index}:{Version}");
}
}
}
class EntityRegister class EntityRegister
{ {
private List<Entity> _entities = new .() ~ delete _; private List<Entity> _entities = new .() ~ delete _;
@ -39,7 +10,7 @@ class EntityRegister
private Entity _next = .Null; private Entity _next = .Null;
public Entity CreateEntity() public Entity Create()
{ {
if(_available == 0) if(_available == 0)
{ {
@ -59,7 +30,7 @@ class EntityRegister
} }
} }
public void RemoveEntity(Entity e) public void Remove(Entity e)
{ {
let temp = _next; let temp = _next;
_next = .(e.Index, 255); _next = .(e.Index, 255);

View file

@ -7,7 +7,7 @@ class MoverSystem : System
[SystemMethod("MoveItems", typeof(Velocity), typeof(Position))] [SystemMethod("MoveItems", typeof(Velocity), typeof(Position))]
public void MoveItems(ref Velocity v,ref Position p) public void MoveItems(ref Velocity v,ref Position p)
{ {
//p.x = v.x + p.x; p.x = v.x + p.x;
//p.y = v.y = p.y; p.y = v.y = p.y;
} }
} }

View file

@ -11,53 +11,18 @@ class Program
public static void Main() public static void Main()
{ {
/* EntityRegister e = scope .();
Entity e = Entity(45454, 797979790); PagedSparseSet<Position> p = scope .();
Console.WriteLine(e); PagedSparseSet<Velocity> v =scope .();
Console.Read(); List<Entity> es = new .();
defer delete es;
EntityRegister r = scope .(); for(int i < 1000000)
while(true)
{ {
Console.WriteLine(scope $"Next: {r.[Friend]_next}"); var ent = e.Create();
for(var i in r.[Friend]_entities) p.Add(ent, .());
Console.WriteLine(i); v.Add(ent, .());
var l = Console.ReadLine(.. scope .()); es.Add(ent);
Console.Clear();
if(l == "c")
Console.WriteLine(r.CreateEntity());
else if(l == "d")
r.RemoveEntity(.((.)gRand.Next(0 , r.[Friend]_entities.Count), 0));
Console.WriteLine();
} }
*/
ECS ecs = scope .();
var pos = ecs.RegisterComponent<Position>(100);
//var waste = ecs.RegisterComponent<waste>(100);
var vel = ecs.RegisterComponent<Velocity>(100);
List<Entity> entities = scope .();
for(int i < 500000)
entities.Add(ecs.CreateEntity());
for(var i in entities)
*(Position*)ecs.AddComponentToEntity(i, pos).Value = .();
for(var i in entities)
*(Velocity*)ecs.AddComponentToEntity(i, vel).Value = .();
/*
for(var i in entities)
if(gRand.NextI32() % 2 == 1)
*(waste*)ecs.AddComponentToEntity(i, waste).Value = .();
*/
MoverSystem s = scope .();
s.IntializeSystem(ecs);
System.Diagnostics.Stopwatch watch = scope .(); System.Diagnostics.Stopwatch watch = scope .();
for(int a < 10) for(int a < 10)
@ -65,9 +30,14 @@ class Program
watch.Start(); watch.Start();
for(int i < 10) for(int i < 10)
{ {
var pos = p.GetAll();
var vel = v.GetAll();
s.RunSystem(ecs); 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"); Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
watch.Stop(); watch.Stop();

View file

@ -1,5 +1,77 @@
namespace Theater_ECS; namespace Theater_ECS;
class PagedSparseSet<T> using System;
using System.Collections;
class PagedSparseSet<T> where T : struct
{ {
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 _;
public (Entity, T) this[Entity e]
{
public get => _packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index];
}
public bool Contains(Entity e)
{
return _sparse[e.Index/PageSize][e.Index % PageSize] != Entity.Null;
}
public void Add(Entity e, T toAdd)
{
EnsureLoadedPage(e.Index/PageSize);
_packed.Add((e, toAdd));
_sparse[e.Index/PageSize][e.Index % PageSize] = (.)_packed.Count-1;
}
public void Remove(Entity e)
{
EnsurePage(e.Index/PageSize);
if(_sparse[e.Index/PageSize] == null)
return;
let toRm = _packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index];
let b = _packed.Back;
_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[e.Index/PageSize][e.Index % PageSize] = Entity.Null;
_packed.PopBack();
}
public T* Get(Entity e)
{
return &_packed[_sparse[e.Index/PageSize][e.Index % PageSize].Index].1;
}
public Span<(Entity, T)> GetAll()
{
return _packed;
}
///Ensure that a page exist and is loaded
public void EnsureLoadedPage(uint32 page)
{
EnsurePage(page);
if(_sparse[page] == null)
{
_sparse[page] = new List<Entity>(PageSize);
_sparse[page].[Friend]Count = 4096;
_sparse[page].SetAll(Entity.Null);
}
}
///Ensure that a page exists, it might be null though
private void EnsurePage(uint32 page)
{
while(_sparse.Count < page+1)
_sparse.Add(null);
}
} }

12
src/World.bf Normal file
View file

@ -0,0 +1,12 @@
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!(_);
}