diff --git a/Blarg.GameFramework/Blarg.GameFramework.csproj b/Blarg.GameFramework/Blarg.GameFramework.csproj index 415242e..fbe2608 100644 --- a/Blarg.GameFramework/Blarg.GameFramework.csproj +++ b/Blarg.GameFramework/Blarg.GameFramework.csproj @@ -144,7 +144,13 @@ + + + + + + @@ -160,6 +166,8 @@ + + diff --git a/Blarg.GameFramework/Entities/Component.cs b/Blarg.GameFramework/Entities/Component.cs new file mode 100644 index 0000000..22a0a83 --- /dev/null +++ b/Blarg.GameFramework/Entities/Component.cs @@ -0,0 +1,11 @@ +using System; +using Blarg.GameFramework.Support; + +namespace Blarg.GameFramework.Entities +{ + public abstract class Component : IPoolable + { + public abstract void Reset(); + } +} + diff --git a/Blarg.GameFramework/Entities/ComponentSystem.cs b/Blarg.GameFramework/Entities/ComponentSystem.cs new file mode 100644 index 0000000..4e83f36 --- /dev/null +++ b/Blarg.GameFramework/Entities/ComponentSystem.cs @@ -0,0 +1,49 @@ +using System; +using Blarg.GameFramework.Events; + +namespace Blarg.GameFramework.Entities +{ + public class ComponentSystem : EventListener, IDisposable + { + public readonly EntityManager EntityManager; + + public ComponentSystem(EntityManager entityManager, EventManager eventManager) + : base(eventManager) + { + if (entityManager == null) + throw new ArgumentNullException("entityManager"); + + EntityManager = entityManager; + } + + public virtual void OnAppPause() + { + } + + public virtual void OnAppResume() + { + } + + public virtual void OnResize() + { + } + + public virtual void OnRender(float delta) + { + } + + public virtual void OnUpdate(float delta) + { + } + + public override bool Handle(Event e) + { + return false; + } + + public virtual void Dispose() + { + } + } +} + diff --git a/Blarg.GameFramework/Entities/Entity.cs b/Blarg.GameFramework/Entities/Entity.cs new file mode 100644 index 0000000..dc09e14 --- /dev/null +++ b/Blarg.GameFramework/Entities/Entity.cs @@ -0,0 +1,38 @@ +using System; + +namespace Blarg.GameFramework.Entities +{ + public class Entity + { + EntityManager _entityManager; + + internal Entity(EntityManager entityManager) + { + if (entityManager == null) + throw new ArgumentNullException("entityManager"); + + _entityManager = entityManager; + } + + public T Get() where T : Component + { + return null; + } + + public T Add() where T : Component + { + return null; + } + + public void Remove() where T : Component + { + return; + } + + public bool Has() where T : Component + { + return false; + } + } +} + diff --git a/Blarg.GameFramework/Entities/EntityManager.cs b/Blarg.GameFramework/Entities/EntityManager.cs new file mode 100644 index 0000000..56a567b --- /dev/null +++ b/Blarg.GameFramework/Entities/EntityManager.cs @@ -0,0 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Blarg.GameFramework.Entities.SystemComponents; +using Blarg.GameFramework.Events; +using Blarg.GameFramework.Support; + +namespace Blarg.GameFramework.Entities +{ + using EntityList = List; + using ComponentList = List; + using EntityToComponentMap = Dictionary; + using ComponentStore = Dictionary>; + using GlobalComponentStore = Dictionary; + using ComponentSystemList = List; + + public class EntityManager : IDisposable + { + public readonly EventManager EventManager; + + EntityList _entities; + ComponentStore _components; + GlobalComponentStore _globalComponents; + ComponentSystemList _componentSystems; + + EntityPool _entityPool; + + EntityList _tempEntityList; + + public EntityManager(EventManager eventManager) + { + if (eventManager == null) + throw new ArgumentNullException("eventManager"); + + EventManager = eventManager; + _entities = new EntityList(); + _components = new ComponentStore(); + _globalComponents = new GlobalComponentStore(); + _componentSystems = new ComponentSystemList(); + + _entityPool = new EntityPool(this); + + _tempEntityList = new EntityList(100); + } + + #region ComponentSystem management + + public T AddSubsystem() where T : ComponentSystem + { + if (GetSubsystem() != null) + throw new InvalidOperationException("ComponentSystem of that type is already registered."); + + T subsystem = (T)Activator.CreateInstance(typeof(T), this, EventManager); + _componentSystems.Add(subsystem); + return subsystem; + } + + public T GetSubsystem() where T : ComponentSystem + { + int i = GetSubsystemIndex(typeof(T)); + if (i == -1) + return null; + else + return (T)_componentSystems[i]; + } + + public void RemoveSubsystem() where T : ComponentSystem + { + int i = GetSubsystemIndex(typeof(T)); + if (i == -1) + return; + else + _componentSystems.RemoveAt(i); + } + + public void RemoveAllSubsystems() + { + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].Dispose(); + _componentSystems.Clear(); + } + + #endregion + + #region Entity management + + public Entity Add() + { + var entity = _entityPool.Take(); + _entities.Add(entity); + return entity; + } + + public Entity GetFirstWith() where T : Component + { + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + return null; + + if (componentEntities.Count > 0) + return componentEntities.Keys.GetEnumerator().Current; + else + return null; + } + + + public void GetAllWith(EntityList list) where T : Component + { + if (list == null) + throw new ArgumentNullException("list", "Must provide a list to store matching Entity's in."); + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + return; + else + { + foreach (var i in componentEntities) + list.Add(i.Key); + } + } + + public void Remove(Entity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + + RemoveAllComponentsFrom(entity); + _entities.Remove(entity); + _entityPool.Free(entity); + } + + public void RemoveAll() + { + foreach (var entity in _entities) + { + RemoveAllComponentsFrom(entity); + _entityPool.Free(entity); + } + _entities.Clear(); + } + + #endregion + + #region Component management + + public T AddComponent(Entity entity) where T : Component + { + if (GetComponent(entity) != null) + throw new InvalidOperationException("Component of that type has been added to this entity already."); + + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + { + // need to create the component-to-entity list + componentEntities = new EntityToComponentMap(); + _components.Add(typeof(T), componentEntities); + } + + T component = ObjectPools.Take(); + + componentEntities.Add(entity, component); + return component; + } + + public T GetComponent(Entity entity) where T : Component + { + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + return null; + + Component existing; + componentEntities.TryGetValue(entity, out existing); + if (existing == null) + return null; + else + return (T)existing; + } + + public void RemoveComponent(Entity entity) where T : Component + { + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + return; + + Component existing; + componentEntities.TryGetValue(entity, out existing); + if (existing == null) + throw new InvalidOperationException("Entity does not have this component."); + componentEntities.Remove(entity); + ObjectPools.Free((T)existing); + } + + public bool HasComponent(Entity entity) where T : Component + { + EntityToComponentMap componentEntities; + _components.TryGetValue(typeof(T), out componentEntities); + if (componentEntities == null) + return false; + + return componentEntities.ContainsKey(entity); + } + + public void GetAllComponentsFor(Entity entity, ComponentList list) + { + if (list == null) + throw new ArgumentNullException("Must provide a list to store the Components for this Entity."); + + foreach (var i in _components) + { + EntityToComponentMap entitiesWithComponent = i.Value; + Component component; + entitiesWithComponent.TryGetValue(entity, out component); + if (component != null) + list.Add(component); + } + } + + #endregion + + #region Global Component management + + public T AddGlobalComponent() where T : Component + { + if (GetGlobalComponent() != null) + throw new InvalidOperationException("Global component of that type has been added already."); + + T component = ObjectPools.Take(); + + _globalComponents.Add(typeof(T), component); + return component; + } + + public T GetGlobalComponent() where T : Component + { + Component existing; + _globalComponents.TryGetValue(typeof(T), out existing); + if (existing == null) + return null; + else + return (T)existing; + } + + public void RemoveGlobalComponent() where T : Component + { + Component existing; + _globalComponents.TryGetValue(typeof(T), out existing); + if (existing == null) + throw new InvalidOperationException("No global component of that type exists."); + else + { + _globalComponents.Remove(typeof(T)); + ObjectPools.Free((T)existing); + } + } + + public bool HasGlobalComponent() where T : Component + { + return _globalComponents.ContainsKey(typeof(T)); + } + + public void RemoveAllGlobalComponents() + { + foreach (var i in _globalComponents) + ObjectPools.Free(i.Key, i.Value); + } + + #endregion + + #region Events + + public void OnAppResume() + { + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].OnAppResume(); + } + + public void OnAppPause() + { + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].OnAppPause(); + } + + public void OnResize() + { + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].OnResize(); + } + + public void OnRender(float delta) + { + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].OnRender(delta); + } + + public void OnUpdate(float delta) + { + _tempEntityList.Clear(); + GetAllWith(_tempEntityList); + foreach (var entity in _tempEntityList) + Remove(entity); + + for (int i = 0; i < _componentSystems.Count; ++i) + _componentSystems[i].OnUpdate(delta); + } + + #endregion + + #region Misc support methods + + private void RemoveAllComponentsFrom(Entity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + + foreach (var i in _components) + { + var entitiesWithComponent = i.Value; + Component component; + entitiesWithComponent.TryGetValue(entity, out component); + if (component != null) + { + ObjectPools.Free(i.Key, component); + entitiesWithComponent.Remove(entity); + } + } + } + + private int GetSubsystemIndex(Type type) + { + for (int i = 0; i < _componentSystems.Count; ++i) + { + if (_componentSystems[i].GetType() == type) + return i; + } + return -1; + } + + #endregion + + #region Disposable + + public void Dispose() + { + RemoveAll(); + RemoveAllGlobalComponents(); + RemoveAllSubsystems(); + } + + #endregion + } +} + diff --git a/Blarg.GameFramework/Entities/EntityPool.cs b/Blarg.GameFramework/Entities/EntityPool.cs new file mode 100644 index 0000000..06d4283 --- /dev/null +++ b/Blarg.GameFramework/Entities/EntityPool.cs @@ -0,0 +1,24 @@ +using System; +using Blarg.GameFramework.Support; + +namespace Blarg.GameFramework.Entities +{ + internal class EntityPool : ObjectPool + { + EntityManager _entityManager; + + public EntityPool(EntityManager entityManager) + { + if (entityManager == null) + throw new ArgumentNullException("entityManager"); + + _entityManager = entityManager; + } + + protected override Entity Allocate() + { + return new Entity(_entityManager); + } + } +} + diff --git a/Blarg.GameFramework/Entities/SystemComponents/InactiveComponent.cs b/Blarg.GameFramework/Entities/SystemComponents/InactiveComponent.cs new file mode 100644 index 0000000..7a1013a --- /dev/null +++ b/Blarg.GameFramework/Entities/SystemComponents/InactiveComponent.cs @@ -0,0 +1,12 @@ +using System; + +namespace Blarg.GameFramework.Entities.SystemComponents +{ + public class InactiveComponent : Component + { + public override void Reset() + { + } + } +} +