Skip to content

Instantly share code, notes, and snippets.

@copygirl
Created October 10, 2015 04:31
Show Gist options
  • Save copygirl/631fad4e7efc3ae366fd to your computer and use it in GitHub Desktop.
Save copygirl/631fad4e7efc3ae366fd to your computer and use it in GitHub Desktop.
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
}
}
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));
}
}
}
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
}
}
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