Skip to content

Commit

Permalink
multiple hierarchy support
Browse files Browse the repository at this point in the history
  • Loading branch information
andreakarasho committed Mar 8, 2024
1 parent 28bd833 commit 918e656
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 91 deletions.
142 changes: 78 additions & 64 deletions plugins/TinyEcs.Plugins/Relationship.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@

namespace TinyEcs;

public struct Relationship
public readonly struct Hierarchy { }


public struct Relationship<T> where T : struct
{
public int Count;
public EcsID First;
public EcsID Parent;
public EcsID Next;
public EcsID Prev;

public readonly RelationshipIterator Children(World world) => new (world, First);
public readonly RelationshipIterator<T> Children(World world) => new (world, First);
}

public readonly struct Parent { }
public readonly struct Child { }
public readonly struct Parent<T> where T : struct { }
public readonly struct Child<T> where T : struct { }

public ref struct RelationshipIterator
public ref struct RelationshipIterator<T> where T : struct
{
private readonly World _world;
private EcsID _currentEntity, _next;
Expand All @@ -35,12 +38,12 @@ public bool MoveNext()
_currentEntity = _next;

if (_next != 0)
_next = _world.Get<Relationship>(_next).Next;
_next = _world.Get<Relationship<T>>(_next).Next;

return _currentEntity != 0;
}

public readonly RelationshipIterator GetEnumerator() => this;
public readonly RelationshipIterator<T> GetEnumerator() => this;
}

public static class RelationshipPlugin
Expand All @@ -49,63 +52,74 @@ public static class RelationshipPlugin
internal static void ModuleInit()
{
World.OnPluginInitialization += world => {
world.Component<Parent>();
world.Component<Child>();
world.Component<Relationship>();
world.OnEntityDeleted += e => {
if (e.Has<Parent>())
{
e.World.Merge();

foreach (var childId in e.Children())
{
e.World.DeferredEntity(childId).Delete();
}

e.World.Merge();
}
world.Component<Parent<Hierarchy>>();
world.Component<Child<Hierarchy>>();
world.Component<Relationship<Hierarchy>>();
world.BindDeletion<Hierarchy>();
};
}

if (e.Has<Child>())
private static void BindDeletion<T>(this World world) where T : struct
{
world.OnEntityDeleted += e => {
if (e.Has<Parent<T>>())
{
var first = e.Get<Relationship<T>>().First;
while (first != 0)
{
ref var rel = ref e.Get<Relationship>();
if (rel.Parent != 0 && e.World.Exists(rel.Parent))
e.World.Entity(rel.Parent).RemoveChild(e);
var next = e.World.Get<Relationship<T>>(first).Next;
e.World.Delete(first);
first = next;
}
};
}

if (e.Has<Child<T>>())
{
ref var rel = ref e.Get<Relationship<T>>();
if (rel.Parent != 0 && e.World.Exists(rel.Parent))
e.World.Entity(rel.Parent).RemoveChild<T>(e);
}
};
}

public static void AddChild(this EntityView entity, EcsID child)
=> entity.World.AddChild(entity.ID, child);
public static void AddChild<T>(this EntityView entity, EcsID child) where T : struct
=> entity.World.AddChild<T>(entity.ID, child);

public static void RemoveChild(this EntityView entity, EcsID child)
=> entity.World.RemoveChild(entity.ID, child);
public static void RemoveChild<T>(this EntityView entity, EcsID child) where T : struct
=> entity.World.RemoveChild<T>(entity.ID, child);

public static void ClearChildren(this EntityView entity)
=> entity.World.ClearChildren(entity.ID);
public static void ClearChildren<T>(this EntityView entity) where T : struct
=> entity.World.ClearChildren<T>(entity.ID);

public static RelationshipIterator Children(this EntityView entity)
public static RelationshipIterator<T> Children<T>(this EntityView entity) where T : struct
{
if (!entity.Has<Relationship>())
if (!entity.Has<Relationship<T>>())
return default;

return entity.Get<Relationship>().Children(entity.World);
return entity.Get<Relationship<T>>().Children(entity.World)!;
}

public static void AddChild(this World world, EcsID parentId, EcsID childId)
public static void AddChild<T>(this World world, EcsID parentId, EcsID childId) where T : struct
{
if (!world.Has<Relationship>(parentId))
var hierarchy = world.Entity<T>();
if (!hierarchy.Has<T>())
{
hierarchy.Set<T>();
world.BindDeletion<T>();
}

if (!world.Has<Relationship<T>>(parentId))
{
world.Set(parentId, new Relationship());
world.Set(parentId, new Relationship<T>());
}

if (!world.Has<Relationship>(childId))
if (!world.Has<Relationship<T>>(childId))
{
world.Set(childId, new Relationship());
world.Set(childId, new Relationship<T>());
}

ref var parentRelationship = ref world.Get<Relationship>(parentId);
ref var childRelationship = ref world.Get<Relationship>(childId);
ref var parentRelationship = ref world.Get<Relationship<T>>(parentId);
ref var childRelationship = ref world.Get<Relationship<T>>(childId);

// Update child's parent
childRelationship.Parent = parentId;
Expand All @@ -119,34 +133,34 @@ public static void AddChild(this World world, EcsID parentId, EcsID childId)
{
// Traverse to the end of the children list
var current = parentRelationship.First;
while (world.Get<Relationship>(current).Next != 0)
while (world.Get<Relationship<T>>(current).Next != 0)
{
current = world.Get<Relationship>(current).Next;
current = world.Get<Relationship<T>>(current).Next;
}

// Add the child at the end
ref var lastRelationship = ref world.Get<Relationship>(current);
ref var lastRelationship = ref world.Get<Relationship<T>>(current);
lastRelationship.Next = childId;
childRelationship.Prev = current;
parentRelationship.Count++;
}

world.Set<Parent>(parentId);
world.Set<Child>(childId);
world.Set<Parent<T>>(parentId);
world.Set<Child<T>>(childId);
}

public static void RemoveChild(this World world, EcsID parentId, EcsID childId)
public static void RemoveChild<T>(this World world, EcsID parentId, EcsID childId) where T : struct
{
if (!world.Has<Relationship>(parentId))
if (!world.Has<Relationship<T>>(parentId))
return;

ref var parentRelationship = ref world.Get<Relationship>(parentId);
ref var childRelationship = ref world.Get<Relationship>(childId);
ref var parentRelationship = ref world.Get<Relationship<T>>(parentId);
ref var childRelationship = ref world.Get<Relationship<T>>(childId);

if (childRelationship.Prev != 0)
{
// Update previous sibling's next pointer
ref var prevRelationship = ref world.Get<Relationship>(childRelationship.Prev);
ref var prevRelationship = ref world.Get<Relationship<T>>(childRelationship.Prev);
prevRelationship.Next = childRelationship.Next;
}
else
Expand All @@ -158,7 +172,7 @@ public static void RemoveChild(this World world, EcsID parentId, EcsID childId)
if (childRelationship.Next != 0)
{
// Update next sibling's previous pointer
ref var nextRelationship = ref world.Get<Relationship>(childRelationship.Next);
ref var nextRelationship = ref world.Get<Relationship<T>>(childRelationship.Next);
nextRelationship.Prev = childRelationship.Prev;
}

Expand All @@ -168,33 +182,33 @@ public static void RemoveChild(this World world, EcsID parentId, EcsID childId)
childRelationship.Prev = 0;
parentRelationship.Count--;

world.Unset<Relationship>(childId);
world.Unset<Child>(childId);
world.Unset<Relationship<T>>(childId);
world.Unset<Child<T>>(childId);

if (parentRelationship.Count <= 0)
{
world.Unset<Parent>(parentId);
world.Unset<Parent<T>>(parentId);

if (parentRelationship.Parent == 0)
world.Unset<Relationship>(parentId);
world.Unset<Relationship<T>>(parentId);
}
}

public static void ClearChildren(this World world, EcsID parentId)
public static void ClearChildren<T>(this World world, EcsID parentId) where T : struct
{
if (!world.Has<Relationship>(parentId))
if (!world.Has<Relationship<T>>(parentId))
return;

ref var parentRelationship = ref world.Get<Relationship>(parentId);
ref var parentRelationship = ref world.Get<Relationship<T>>(parentId);
var currentChild = parentRelationship.First;

while (currentChild != 0)
{
var nextChild = world.Get<Relationship>(currentChild).Next;
world.RemoveChild(parentId, currentChild);
var nextChild = world.Get<Relationship<T>>(currentChild).Next;
world.RemoveChild<T>(parentId, currentChild);
currentChild = nextChild;
}

world.Unset<Parent>(parentId);
world.Unset<Parent<T>>(parentId);
}
}
44 changes: 31 additions & 13 deletions samples/MyBattleground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
.Set<Position>(new Position() {X = 2})
.Set<Velocity>(new Velocity());


var rabbit = ecs.Entity();
var eats = ecs.Entity();
var carrots = ecs.Entity();
var grass = ecs.Entity();


e.Disable();
var enabled = e.IsEnabled();
e.Disable();
Expand All @@ -36,41 +43,52 @@
var child3 = ecs.Entity("child 2");


e.AddChild(child);
e.AddChild(child2);
child2.AddChild(child3);
e.AddChild<Hierarchy>(child);
e.AddChild<Hierarchy>(child2);
child2.AddChild<Hierarchy>(child3);


e.AddChild<Chunk>(child2);
e.AddChild<Chunk>(child3);

child2.Delete();

foreach (var childId in e.Children<Hierarchy>())
{

}

foreach (var childId in e.Children())
foreach (var childId in e.Children<Chunk>())
{

}

ecs.Filter<(With<Child>, With<Parent>)>().Query((EntityView entity, ref Relationship relation) => {
ecs.Filter<(With<Child<Hierarchy>>, With<Parent<Hierarchy>>)>().Query((EntityView entity, ref Relationship<Hierarchy> relation) => {
Console.WriteLine("im [{0}] a child of {1}, but also having {2} children", entity.Name(), ecs.Entity(relation.Parent).Name(), relation.Count);
});

Console.WriteLine();

ecs.Filter<With<Parent>>().Query((EntityView entity, ref Relationship relation) => {
ecs.Filter<With<Parent<Hierarchy>>>().Query((EntityView entity, ref Relationship<Hierarchy> relation) => {
Console.WriteLine("parent {0} has {1} children", entity.Name(), relation.Count);

foreach (var id in entity.Children())
foreach (var id in entity.Children<Hierarchy>())
{
Console.WriteLine("\tChild: {0}", ecs.Entity(id).Name());
}
});

Console.WriteLine();

e.RemoveChild(child);
ecs.Filter<With<Parent>>().Query((EntityView entity, ref Relationship relation) => {
e.RemoveChild<Hierarchy>(child);
ecs.Filter<With<Parent<Hierarchy>>>().Query((EntityView entity, ref Relationship<Hierarchy> relation) => {
Console.WriteLine("parent {0} has {1} children", entity.Name(), relation.Count);
});

Console.WriteLine();

child2.RemoveChild(child3);
ecs.Filter<With<Parent>>().Query((EntityView entity, ref Relationship relation) => {
child2.RemoveChild<Hierarchy>(child3);
ecs.Filter<With<Parent<Hierarchy>>>().Query((EntityView entity, ref Relationship<Hierarchy> relation) => {
Console.WriteLine("parent {0} has {1} children", entity.Name(), relation.Count);
});

Expand All @@ -85,10 +103,10 @@
{
var c = ecs.Entity();
Console.WriteLine("Add {0}", c.ID);
e.AddChild(c);
e.AddChild<Hierarchy>(c);
}

foreach (var childId in e.Children())
foreach (var childId in e.Children<Hierarchy>())
{
Console.WriteLine("child {0}", childId);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ internal int FindMatch(ReadOnlySpan<Term> searching)
ref readonly var current = ref currents[i];
ref readonly var search = ref searching[j];

if (current.ID.CompareTo(search.ID) == 0)
if (_comparer.Compare(current.ID, search.ID) == 0)
{
if (search.Op != TermOp.With)
return -1;
Expand Down
24 changes: 12 additions & 12 deletions src/ComponentComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ public int Compare(Term x, Term y)

public static int CompareTerms(World world, ulong a, ulong b)
{
//if (IDOp.IsPair(a) && IDOp.IsPair(b))
//{
// if (IDOp.GetPairFirst(a) == IDOp.GetPairFirst(b))
// {
// var secondY = IDOp.GetPairSecond(b);

// if (secondY == Lookup.Entity<EcsAny>.Component.ID)
// {
// return 0;
// }
// }
//}
// if (IDOp.IsPair(a) && IDOp.IsPair(b))
// {
// if (IDOp.GetPairFirst(a) == IDOp.GetPairFirst(b))
// {
// var secondY = IDOp.GetPairSecond(b);

// if (secondY == ulong.MaxValue)
// {
// return 0;
// }
// }
// }

return a.CompareTo(b);
}
Expand Down
2 changes: 1 addition & 1 deletion src/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ internal EntityView NewEmpty(ulong id = 0)
internal ref EcsRecord GetRecord(EcsID id)
{
ref var record = ref _entities.Get(id);
EcsAssert.Assert(!Unsafe.IsNullRef(ref record));
EcsAssert.Assert(!Unsafe.IsNullRef(ref record), $"entity {id} is dead or doesn't exist!");
return ref record;
}

Expand Down

0 comments on commit 918e656

Please sign in to comment.