Created
October 10, 2015 04:31
-
-
Save copygirl/631fad4e7efc3ae366fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using OtherEngine.Utility; | |
namespace OtherEngine.ES | |
{ | |
/// <summary> Represents which (important) types of components are on an entity, | |
/// or alternatively which component types a processor is interested in. </summary> | |
public class Aspect : IEquatable<Aspect> | |
{ | |
readonly BitArray _bits; | |
internal bool this[int aspectId] { get { return _bits[aspectId]; } | |
set { _bits[aspectId] = value; } } | |
#region Public properties | |
public AspectManager Manager { get; private set; } | |
/// <summary> Gets the component types currently represented by this Aspect. </summary> | |
public IEnumerable<Type> ComponentTypes { get { | |
for (var i = 0; i < _bits.Length; i++) | |
if (_bits[i]) | |
yield return Manager.GetComponentTypeForId(i); | |
} } | |
/// <summary> Gets whether the specified component type | |
/// is currently represented by this Aspect. </summary> | |
public bool this[Type componentType] { | |
get { return _bits[Manager.GetIdForComponentType(componentType)]; } | |
internal set { _bits[Manager.GetIdForComponentType(componentType)] = value; } | |
} | |
#endregion | |
internal Aspect(AspectManager manager, int length) | |
{ | |
_bits = new BitArray(length); | |
Manager = manager; | |
} | |
public Aspect(Aspect aspect) | |
{ | |
if (aspect == null) | |
throw new ArgumentNullException("aspect"); | |
_bits = new BitArray(aspect._bits); | |
Manager = aspect.Manager; | |
} | |
#region Public methods | |
/// <summary> Returns if this Aspect contains | |
/// all of the specified Aspect's types. </summary> | |
public bool All(Aspect other) | |
{ | |
if (other == null) | |
throw new ArgumentNullException("other"); | |
return ((_bits & other._bits) == other._bits); | |
} | |
/// <summary> Returns if this Aspect contains | |
/// any of the specified Aspect's types. </summary> | |
public bool Any(Aspect other) | |
{ | |
if (other == null) | |
throw new ArgumentNullException("other"); | |
return (_bits & other._bits).Any(); | |
} | |
#endregion | |
#region ToString, Equals and GetHashCode | |
public override string ToString() | |
{ | |
return string.Format("[Aspect ({0})]", string.Join(", ", ComponentTypes)); | |
} | |
public override bool Equals(object obj) | |
{ | |
return Equals(obj as Aspect); | |
} | |
public override int GetHashCode() | |
{ | |
return HashHelper.ComputeHashCode(Manager, _bits); | |
} | |
#endregion | |
#region IEquatable<Aspect> implementation | |
public bool Equals(Aspect other) | |
{ | |
return ((other != null) && (other.Manager == Manager) && (other._bits == _bits)); | |
} | |
#endregion | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using OtherEngine.Utility; | |
namespace OtherEngine.ES | |
{ | |
public class AspectManager | |
{ | |
readonly List<Type> _typeLookup = new List<Type>(); | |
readonly Dictionary<Type, int> _idLookup = new Dictionary<Type, int>(); | |
/// <summary> Gets whether the aspect manager contains all valid component types. </summary> | |
public bool ContainsAllTypes { get; private set; } | |
/// <summary> Gets all component types handled by this AspectManager. </summary> | |
public IReadOnlyCollection<Type> ComponentTypes { get { return _typeLookup.AsReadOnly(); } } | |
public AspectManager(IEnumerable<Type> componentTypes, bool containsAllTypes) | |
{ | |
if (componentTypes == null) | |
throw new ArgumentNullException("componentTypes"); | |
foreach (var type in componentTypes) { | |
try { VerifyComponentType(type); } | |
catch (Exception ex) { | |
// Rethrow exception as an ArgumentElementException. | |
throw ex.MakeElementException(componentTypes, type, "componentTypes"); | |
} | |
var id = _typeLookup.Count; | |
_typeLookup.Add(type); | |
try { _idLookup.Add(type, id); } | |
catch (ArgumentException ex) { | |
throw new ArgumentEnumerableException(componentTypes, | |
"componentTypes can't contain the same value multiple times", | |
"componentTypes", ex); | |
} | |
} | |
ContainsAllTypes = containsAllTypes; | |
} | |
/// <summary> Creates an Aspect that represents the specified component types. | |
/// Only component types handled by this AspectManager are valid. </summary> | |
public Aspect CreateAspect(IEnumerable<Type> componentTypes) | |
{ | |
if (componentTypes == null) | |
throw new ArgumentNullException("componentTypes"); | |
var aspect = new Aspect(this, _typeLookup.Count); | |
foreach (var type in componentTypes) { | |
try { aspect[type] = true; } | |
catch (Exception ex) { | |
// Rethrow exception as an ArgumentElementException. | |
throw ex.MakeElementException(componentTypes, type, "componentTypes"); | |
} | |
} | |
return aspect; | |
} | |
internal int GetIdForComponentType(Type type) | |
{ | |
int id; | |
if (!_idLookup.TryGetValue(type, out id)) { | |
VerifyComponentType(type); | |
throw new ArgumentException(string.Format( | |
"{0} is not a valid component type for this AspectManager", type)); | |
} | |
return id; | |
} | |
internal Type GetComponentTypeForId(int id) | |
{ | |
if ((id < 0) || (id >= _typeLookup.Count)) | |
throw new ArgumentOutOfRangeException("id"); | |
return _typeLookup[id]; | |
} | |
static void VerifyComponentType(Type type) | |
{ | |
if (type == null) | |
throw new ArgumentNullException(); | |
if (!typeof(IComponent).IsAssignableFrom(type)) | |
throw new ArgumentException(string.Format( | |
"{0} is not an IComponent", type)); | |
if (!type.IsValueType) | |
throw new ArgumentException(string.Format( | |
"{0} is not a struct", type)); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace OtherEngine.ES | |
{ | |
public struct Entity : IEquatable<Entity> | |
{ | |
public Guid ID { get; private set; } | |
internal static Entity New() { | |
return new Entity { ID = Guid.NewGuid() }; | |
} | |
#region ToString, Equals and GetHashCode | |
public override string ToString() | |
{ | |
return string.Format("[Entity {0}]", ID); | |
} | |
public override bool Equals(object obj) | |
{ | |
return ((obj is Entity) && Equals((Entity)obj)); | |
} | |
public override int GetHashCode() | |
{ | |
return ID.GetHashCode(); | |
} | |
#endregion | |
#region IEquatable<Entity> implementation | |
public bool Equals(Entity other) | |
{ | |
return (other.ID == ID); | |
} | |
#endregion | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace OtherEngine.ES | |
{ | |
public class GameState | |
{ | |
readonly AspectManager _aspectManager; | |
readonly GameState _baseState; | |
readonly Dictionary<Entity, EntityStore> _entities = new Dictionary<Entity, EntityStore>(); | |
readonly Dictionary<Type, ComponentStore> _components = new Dictionary<Type, ComponentStore>(); | |
/// <summary> Gets whether this GameState is the main state. | |
/// If false, the state will only store changes, grabbing unavailable information | |
/// from the base state as needed. Changes can be pushed to the base state on demand. </summary> | |
public bool IsMain { get { return (_baseState != null); } } | |
internal GameState(AspectManager aspectManager) | |
{ | |
_aspectManager = aspectManager; | |
} | |
internal GameState(GameState baseState) | |
: this(baseState._aspectManager) | |
{ | |
_baseState = baseState; | |
} | |
#region Create / destroy entities | |
/// <summary> Creates and returns a new empty entity for this GameState. </summary> | |
public Entity CreateEntity() | |
{ | |
var entity = Entity.New(); | |
_entities.Add(entity, new EntityStore(_aspectManager)); | |
return entity; | |
} | |
/// <summary> Destroys the specified entity, removing it from this GameState. | |
/// This also destroys any components attached to the entity. </summary> | |
/// <exception cref="ArgumentException"> | |
/// Thrown if entity doesn't exist in this (or any base) GameState. </exception> | |
public void DestroyEntity(Entity entity) | |
{ | |
bool _; // Ignore out parameter. | |
// Attempt to get EntityStore. Throws exception if non-existent. | |
var store = GetEntityStore(entity, out _); | |
// If this is the main GameState, just remove the entity completely. | |
if (IsMain) _entities.Remove(entity); | |
// Otherwise set the entry to null. This will destroy the | |
// entity in the base GameState when changes are sent down. | |
else _entities[entity] = null; | |
// Remove the entity (and its associated component | |
// data) from any component stores it's contained in. | |
foreach (var componentType in store.Components) | |
RemoveComponentInternal(entity, componentType); | |
} | |
#endregion | |
#region Add / remove components | |
/// <summary> Adds a component of type TComponent to the specified entity. </summary> | |
/// <exception cref="ArgumentException"> | |
/// Thrown if entity doesn't exist in this (or any base) GameState | |
/// -OR- a component of the same type is already on this entity. </exception> | |
public void AddComponent<TComponent>(Entity entity, TComponent component = default(TComponent)) | |
where TComponent : struct, IComponent | |
{ | |
// Attempt to get EntityStore. Throws exception if non-existent. | |
bool entityOwn; // Store whether we own the EntityStore. | |
var entityStore = GetEntityStore(entity, out entityOwn); | |
var componentType = typeof(TComponent); | |
// Attempt to get ComponentStore. Returns null if non-existent. | |
bool componentOwn; // Store whether we own the ComponentStore. | |
var componentStore = GetComponentStore(componentType, out componentOwn); | |
// Make sure the entity doesn't already have a component of the same type. | |
if ((componentStore != null) && componentStore.Data.ContainsKey(entity)) | |
throw new ArgumentException(string.Format( | |
"{0} is already on {1}", componentType, entity), "entity"); | |
// If the ComponentStore doesn't exist, or we don't own it, create a new one. | |
// In the latter case, only store the changes in this GameState. | |
if ((componentStore == null) || !componentOwn) { | |
var aspectId = _aspectManager.GetIdForComponentType(componentType); | |
componentStore = new ComponentStore(aspectId); | |
_components.Add(componentType, componentStore); | |
} | |
// Apply changes to ComponentStore. | |
componentStore.Data[entity] = component; | |
// If we don't own the EntityStore, create new one | |
// and only store the changes in this GameState. | |
if (!entityOwn) { | |
entityStore = new EntityStore(_aspectManager, entityStore); | |
_entities.Add(entity, entityStore); | |
} | |
// Apply changes to EntityStore. | |
entityStore.Set(componentType, componentStore.AspectID, true); | |
} | |
/// <summary> Removes the component of type TComponent from the | |
/// specified entity and returns the latest component value. </summary> | |
/// <exception cref="ArgumentException"> | |
/// Thrown if entity doesn't exist in this (or any base) GameState | |
/// -OR- there's not component of type TComponent on the entity. </exception> | |
public TComponent RemoveComponent<TComponent>(Entity entity) | |
where TComponent : struct, IComponent | |
{ | |
// Attempt to get EntityStore. Throws exception if non-existent. | |
bool entityOwn; // Store whether we own the EntityStore. | |
var entityStore = GetEntityStore(entity, out entityOwn); | |
var componentType = typeof(TComponent); | |
// Attempt to get ComponentStore. Returns null if non-existent. | |
bool componentOwn; // Store whether we own the ComponentStore. | |
var componentStore = GetComponentStore(componentType, out componentOwn); | |
IComponent component; | |
// Make sure the entity doesn't already have a component of the same type. | |
if ((componentStore == null) || !componentStore.Data.TryGetValue(entity, out component)) | |
throw new ArgumentException(string.Format( | |
"{0} is not on {1}", componentType, entity), "entity"); | |
// If the ComponentStore doesn't exist, or we don't own it, create a new one. | |
// In the latter case, only store the changes in this GameState. | |
if ((componentStore == null) || !componentOwn) { | |
var aspectId = _aspectManager.GetIdForComponentType(componentType); | |
componentStore = new ComponentStore(aspectId); | |
_components.Add(componentType, componentStore); | |
} | |
// Apply changes to ComponentStore. | |
componentStore.Data[entity] = null; | |
// If we don't own the EntityStore, create new one | |
// and only store the changes in this GameState. | |
if (!entityOwn) { | |
entityStore = new EntityStore(_aspectManager, entityStore); | |
_entities.Add(entity, entityStore); | |
} | |
// Apply changes to EntityStore. | |
entityStore.Set(componentType, componentStore.AspectID, false); | |
return (TComponent)component; | |
} | |
#endregion | |
#region Get / set components of entities | |
/// <summary> Returns a component of an entity </summary> | |
public TComponent GetComponentOfEntity<TComponent>(Entity entity) | |
where TComponent : struct, IComponent | |
{ | |
} | |
/// <summary> Returns a component of an entity </summary> | |
public void SetComponentOfEntity<TComponent>(Entity entity, TComponent component) | |
where TComponent : struct, IComponent | |
{ | |
} | |
#endregion | |
/// <summary> Returns the EntityStore for an entity on this (or any base) GameState. | |
/// Throws an exception if the entity doesn't exist. </summary> | |
/// <param name="own"> Is set to true if the EntityStore is from this GameState. </param> | |
EntityStore GetEntityStore(Entity entity, out bool own) | |
{ | |
EntityStore store = null; | |
if (!(own = _entities.TryGetValue(entity, out store)) && !IsMain) { | |
bool _; // Ignore out parameter. | |
return _baseState.GetEntityStore(entity, out _); | |
} | |
if (store == null) | |
throw CreateMissingEntityException(entity); | |
return store; | |
} | |
/// <summary> Returns the ComponentStore for a component type on this | |
/// (or any base) GameState, or null if nothing is available.</summary> | |
/// <param name="own"> Is set to true if the ComponentStore is from this GameState. </param> | |
ComponentStore GetComponentStore(Type componentType, out bool own) | |
{ | |
ComponentStore store; | |
bool _; // Ignore out parameter of _baseState.GetComponentStore. | |
return ((own = _components.TryGetValue(componentType, out store)) | |
? store : _baseState?.GetComponentStore(componentType, out _)); | |
} | |
void RemoveComponentInternal(Entity entity, Type componentType) | |
{ | |
var store = GetComponentStore(componentType); | |
} | |
static Exception CreateMissingEntityException(Entity entity) | |
{ | |
return new ArgumentException(string.Format( | |
"{0} doesn't exist in this GameState", entity), "entity"); | |
} | |
class EntityStore | |
{ | |
readonly HashSet<Type> _components; | |
public Aspect Aspect { get; private set; } | |
public IEnumerable<Type> Components { get { return (_components ?? Aspect.ComponentTypes); } } | |
internal EntityStore(AspectManager manager, EntityStore store = null) | |
{ | |
_components = (manager.ContainsAllTypes ? null | |
: ((store != null) ? new HashSet<Type>(store._components) | |
: new HashSet<Type>())); | |
Aspect = ((store != null) ? new Aspect(store.Aspect) | |
: manager.CreateAspect(Enumerable.Empty<Type>())); | |
} | |
internal void Set(Type componentType, int aspectId, bool value) | |
{ | |
if (_components != null) { | |
if (value) _components.Add(componentType); | |
else _components.Remove(componentType); | |
} | |
if (aspectId >= 0) | |
Aspect[aspectId] = value; | |
} | |
} | |
class ComponentStore | |
{ | |
internal Dictionary<Entity, IComponent> Data { get; } = new Dictionary<Entity, IComponent>(); | |
public int AspectID { get; private set; } | |
public bool Tracked { get { return (AspectID >= 0); } } | |
internal ComponentStore(int aspectId) | |
{ | |
AspectID = aspectId; | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment