entity relation system
							parent
							
								
									d7e795309f
								
							
						
					
					
						commit
						da35e99266
					
				| 
						 | 
					@ -4,6 +4,7 @@ public abstract class EntityComponentReader
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	internal EntityStorage EntityStorage;
 | 
						internal EntityStorage EntityStorage;
 | 
				
			||||||
	internal ComponentDepot ComponentDepot;
 | 
						internal ComponentDepot ComponentDepot;
 | 
				
			||||||
 | 
						internal RelationDepot RelationDepot;
 | 
				
			||||||
	protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
 | 
						protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	internal void RegisterEntityStorage(EntityStorage entityStorage)
 | 
						internal void RegisterEntityStorage(EntityStorage entityStorage)
 | 
				
			||||||
| 
						 | 
					@ -16,6 +17,11 @@ public abstract class EntityComponentReader
 | 
				
			||||||
		ComponentDepot = componentDepot;
 | 
							ComponentDepot = componentDepot;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						internal void RegisterRelationDepot(RelationDepot relationDepot)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RelationDepot = relationDepot;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
 | 
						protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return ComponentDepot.ReadComponents<TComponent>();
 | 
							return ComponentDepot.ReadComponents<TComponent>();
 | 
				
			||||||
| 
						 | 
					@ -50,4 +56,19 @@ public abstract class EntityComponentReader
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return EntityStorage.Exists(entity);
 | 
							return EntityStorage.Exists(entity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected IEnumerable<Relation> Relations<TRelationKind>()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return RelationDepot.Relations<TRelationKind>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected IEnumerable<Entity> RelatedToA<TRelationKind>(in Entity entity)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return RelationDepot.RelatedToA<TRelationKind>(entity.ID);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected IEnumerable<Entity> RelatedToB<TRelationKind>(in Entity entity)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return RelationDepot.RelatedToB<TRelationKind>(entity.ID);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					namespace MoonTools.ECS
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public struct Relation : IEquatable<Relation>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							public Entity A { get; }
 | 
				
			||||||
 | 
							public Entity B { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal Relation(Entity entityA, Entity entityB)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								A = entityA;
 | 
				
			||||||
 | 
								B = entityB;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal Relation(int idA, int idB)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								A = new Entity(idA);
 | 
				
			||||||
 | 
								B = new Entity(idB);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override bool Equals(object? obj)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return obj is Relation relation && Equals(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public bool Equals(Relation other)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return A.Equals(other.A) && B.Equals(other.B);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override int GetHashCode()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return HashCode.Combine(A, B);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					namespace MoonTools.ECS
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						internal class RelationDepot
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private Dictionary<Type, RelationStorage> storages = new Dictionary<Type, RelationStorage>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private RelationStorage Lookup<TRelationKind>()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return storages[typeof(TRelationKind)];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Register<TRelationKind>()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								storages[typeof(TRelationKind)] = new RelationStorage();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Add<TRelationKind>(Relation relation)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Lookup<TRelationKind>().Add(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Remove<TRelationKind>(Relation relation)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Lookup<TRelationKind>().Remove(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void OnEntityDestroy(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								foreach (var storage in storages.Values)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									storage.OnEntityDestroy(entityID);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Relation> Relations<TRelationKind>()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return Lookup<TRelationKind>().All();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Entity> RelatedToA<TRelationKind>(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return Lookup<TRelationKind>().RelatedToA(entityID);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Entity> RelatedToB<TRelationKind>(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return Lookup<TRelationKind>().RelatedToB(entityID);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,122 @@
 | 
				
			||||||
 | 
					namespace MoonTools.ECS
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						internal class RelationStorage
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private HashSet<Relation> relations = new HashSet<Relation>(16);
 | 
				
			||||||
 | 
							private Dictionary<int, HashSet<int>> entitiesRelatedToA = new Dictionary<int, HashSet<int>>(16);
 | 
				
			||||||
 | 
							private Dictionary<int, HashSet<int>> entitiesRelatedToB = new Dictionary<int, HashSet<int>>(16);
 | 
				
			||||||
 | 
							private Stack<HashSet<int>> listPool = new Stack<HashSet<int>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Relation> All()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								foreach (var relation in relations)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									yield return relation;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Add(Relation relation)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (relations.Contains(relation)) { return; }
 | 
				
			||||||
 | 
								var idA = relation.A.ID;
 | 
				
			||||||
 | 
								var idB = relation.B.ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!entitiesRelatedToA.ContainsKey(idA))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									entitiesRelatedToA[idA] = AcquireHashSetFromPool();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								entitiesRelatedToA[idA].Add(idB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!entitiesRelatedToB.ContainsKey(idB))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									entitiesRelatedToB[idB] = AcquireHashSetFromPool();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								entitiesRelatedToB[idB].Add(idA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								relations.Add(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Entity> RelatedToA(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (entitiesRelatedToA.ContainsKey(entityID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									foreach (var id in entitiesRelatedToA[entityID])
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										yield return new Entity(id);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public IEnumerable<Entity> RelatedToB(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (entitiesRelatedToB.ContainsKey(entityID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									foreach (var id in entitiesRelatedToB[entityID])
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										yield return new Entity(id);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public bool Remove(Relation relation)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (entitiesRelatedToA.ContainsKey(relation.A.ID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									entitiesRelatedToA[relation.A.ID].Remove(relation.B.ID);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (entitiesRelatedToB.ContainsKey(relation.B.ID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									entitiesRelatedToB[relation.B.ID].Remove(relation.A.ID);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return relations.Remove(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// this exists so we don't recurse in OnEntityDestroy
 | 
				
			||||||
 | 
							private bool DestroyRemove(Relation relation)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return relations.Remove(relation);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void OnEntityDestroy(int entityID)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (entitiesRelatedToA.ContainsKey(entityID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									foreach (var entityB in entitiesRelatedToA[entityID])
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										DestroyRemove(new Relation(entityID, entityB));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ReturnHashSetToPool(entitiesRelatedToA[entityID]);
 | 
				
			||||||
 | 
									entitiesRelatedToA.Remove(entityID);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (entitiesRelatedToB.ContainsKey(entityID))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									foreach (var entityA in entitiesRelatedToB[entityID])
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										DestroyRemove(new Relation(entityA, entityID));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ReturnHashSetToPool(entitiesRelatedToB[entityID]);
 | 
				
			||||||
 | 
									entitiesRelatedToB.Remove(entityID);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private HashSet<int> AcquireHashSetFromPool()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (listPool.Count == 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									listPool.Push(new HashSet<int>());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return listPool.Pop();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private void ReturnHashSetToPool(HashSet<int> hashSet)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								listPool.Push(hashSet);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -71,9 +71,20 @@ public abstract class System : EntityComponentReader
 | 
				
			||||||
		MessageDepot.Add(message);
 | 
							MessageDepot.Add(message);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RelationDepot.Add<TRelationKind>(new Relation(entityA, entityB));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected void Destroy(in Entity entity)
 | 
						protected void Destroy(in Entity entity)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ComponentDepot.OnEntityDestroy(entity.ID);
 | 
							ComponentDepot.OnEntityDestroy(entity.ID);
 | 
				
			||||||
 | 
							RelationDepot.OnEntityDestroy(entity.ID);
 | 
				
			||||||
		EntityStorage.Destroy(entity);
 | 
							EntityStorage.Destroy(entity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/World.cs
								
								
								
								
							
							
						
						
									
										12
									
								
								src/World.cs
								
								
								
								
							| 
						 | 
					@ -1,22 +1,30 @@
 | 
				
			||||||
namespace MoonTools.ECS;
 | 
					namespace MoonTools.ECS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class World
 | 
					public class World
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	private readonly EntityStorage EntityStorage = new EntityStorage();
 | 
						private readonly EntityStorage EntityStorage = new EntityStorage();
 | 
				
			||||||
	private readonly ComponentDepot ComponentDepot = new ComponentDepot();
 | 
						private readonly ComponentDepot ComponentDepot = new ComponentDepot();
 | 
				
			||||||
	private MessageDepot MessageDepot = new MessageDepot();
 | 
						private readonly MessageDepot MessageDepot = new MessageDepot();
 | 
				
			||||||
 | 
						private readonly RelationDepot RelationDepot = new RelationDepot();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	internal void AddSystem(System system)
 | 
						internal void AddSystem(System system)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		system.RegisterEntityStorage(EntityStorage);
 | 
							system.RegisterEntityStorage(EntityStorage);
 | 
				
			||||||
		system.RegisterComponentDepot(ComponentDepot);
 | 
							system.RegisterComponentDepot(ComponentDepot);
 | 
				
			||||||
		system.RegisterMessageDepot(MessageDepot);
 | 
							system.RegisterMessageDepot(MessageDepot);
 | 
				
			||||||
 | 
							system.RegisterRelationDepot(RelationDepot);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	internal void AddRenderer(Renderer renderer)
 | 
						internal void AddRenderer(Renderer renderer)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		renderer.RegisterEntityStorage(EntityStorage);
 | 
							renderer.RegisterEntityStorage(EntityStorage);
 | 
				
			||||||
		renderer.RegisterComponentDepot(ComponentDepot);
 | 
							renderer.RegisterComponentDepot(ComponentDepot);
 | 
				
			||||||
 | 
							renderer.RegisterRelationDepot(RelationDepot);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void AddRelationKind<TRelationKind>()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RelationDepot.Register<TRelationKind>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Entity CreateEntity()
 | 
						public Entity CreateEntity()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue