entity relation system
							parent
							
								
									d7e795309f
								
							
						
					
					
						commit
						da35e99266
					
				| 
						 | 
				
			
			@ -4,6 +4,7 @@ public abstract class EntityComponentReader
 | 
			
		|||
{
 | 
			
		||||
	internal EntityStorage EntityStorage;
 | 
			
		||||
	internal ComponentDepot ComponentDepot;
 | 
			
		||||
	internal RelationDepot RelationDepot;
 | 
			
		||||
	protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
 | 
			
		||||
 | 
			
		||||
	internal void RegisterEntityStorage(EntityStorage entityStorage)
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +17,11 @@ public abstract class EntityComponentReader
 | 
			
		|||
		ComponentDepot = componentDepot;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	internal void RegisterRelationDepot(RelationDepot relationDepot)
 | 
			
		||||
	{
 | 
			
		||||
		RelationDepot = relationDepot;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
 | 
			
		||||
	{
 | 
			
		||||
		return ComponentDepot.ReadComponents<TComponent>();
 | 
			
		||||
| 
						 | 
				
			
			@ -50,4 +56,19 @@ public abstract class EntityComponentReader
 | 
			
		|||
	{
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	{
 | 
			
		||||
		ComponentDepot.OnEntityDestroy(entity.ID);
 | 
			
		||||
		RelationDepot.OnEntityDestroy(entity.ID);
 | 
			
		||||
		EntityStorage.Destroy(entity);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								src/World.cs
								
								
								
								
							
							
						
						
									
										12
									
								
								src/World.cs
								
								
								
								
							| 
						 | 
				
			
			@ -1,22 +1,30 @@
 | 
			
		|||
namespace MoonTools.ECS;
 | 
			
		||||
namespace MoonTools.ECS;
 | 
			
		||||
 | 
			
		||||
public class World
 | 
			
		||||
{
 | 
			
		||||
	private readonly EntityStorage EntityStorage = new EntityStorage();
 | 
			
		||||
	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)
 | 
			
		||||
	{
 | 
			
		||||
		system.RegisterEntityStorage(EntityStorage);
 | 
			
		||||
		system.RegisterComponentDepot(ComponentDepot);
 | 
			
		||||
		system.RegisterMessageDepot(MessageDepot);
 | 
			
		||||
		system.RegisterRelationDepot(RelationDepot);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	internal void AddRenderer(Renderer renderer)
 | 
			
		||||
	{
 | 
			
		||||
		renderer.RegisterEntityStorage(EntityStorage);
 | 
			
		||||
		renderer.RegisterComponentDepot(ComponentDepot);
 | 
			
		||||
		renderer.RegisterRelationDepot(RelationDepot);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void AddRelationKind<TRelationKind>()
 | 
			
		||||
	{
 | 
			
		||||
		RelationDepot.Register<TRelationKind>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Entity CreateEntity()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue