Last active
June 8, 2018 21:03
-
-
Save will-hart/4dce1335d05d34dfbc32d61d32e56b5b to your computer and use it in GitHub Desktop.
Sentry ECS - a simple public domain entity component system
This file contains 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
public abstract class AbstractComponent : IComponent | |
{ | |
[NonSerialized] | |
protected Entity _owner; | |
public AbstractComponent(Entity owner) | |
{ | |
_owner = owner; | |
ID = Guid.NewGuid().ToString("n"); | |
} | |
public void SetOwner(Entity newOwner) | |
{ | |
_owner = owner; | |
} | |
public virtual void OnDestroy() { } | |
public abstract Type ObjectType { get; } | |
public abstract ComponentTypes ComponentType { get; } | |
public T Clone<T>() where T : AbstractComponent | |
{ | |
return (T) MemberwiseClone(); | |
} | |
public Entity Owner | |
{ | |
get { return _owner; } | |
} | |
public string ID { get; private set; } | |
} |
This file contains 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
public abstract class AbstractEditorWindow : EditorWindow | |
{ | |
private float _nextUpdate = 1; | |
// refreshes the display every second | |
protected virtual void Update() | |
{ | |
_nextUpdate -= Time.deltaTime; | |
if (_nextUpdate > 0) return; | |
Repaint(); | |
_nextUpdate = 1; | |
} | |
protected void DrawDoubleLabel(string label, string value1, string separator, string value2) | |
{ | |
GUILayout.BeginHorizontal(); | |
GUILayout.Label(label, EditorStyles.miniBoldLabel); | |
GUILayout.Box(value1, GUILayout.Width(100)); | |
GUILayout.Label(separator, GUILayout.Width(50)); | |
GUILayout.Box(value2, GUILayout.Width(100)); | |
GUILayout.EndHorizontal(); | |
} | |
protected void DrawLabel(string label, string value, int width = 200, bool isBox = true) | |
{ | |
GUILayout.BeginHorizontal(); | |
GUILayout.Label(label, EditorStyles.miniBoldLabel); | |
if (isBox) | |
{ | |
GUILayout.Box(value, GUILayout.Width(width)); | |
} | |
else | |
{ | |
GUILayout.Label(value, GUILayout.Width(width)); | |
} | |
GUILayout.EndHorizontal(); | |
} | |
} |
This file contains 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
public abstract class AbstractSystem : ISystem | |
{ | |
public abstract Type ObjectType { get; } | |
public virtual int ExecutionPriority { get { return 100; } } | |
public abstract void Init(); | |
public abstract void Update(); | |
} |
This file contains 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
public static class ComponentFactory | |
{ | |
public static readonly Dictionary<ComponentTypes, Type> ComponentLookup = new Dictionary<ComponentTypes, Type> | |
{ | |
{ComponentTypes.SampleComponent, typeof(SampleComponent)} | |
}; | |
public static IComponent Create(ComponentTypes type, Entity entity) | |
{ | |
if (!ComponentLookup.ContainsKey(type)) return null; | |
return (IComponent) Activator.CreateInstance(ComponentLookup[type], entity); | |
} | |
} |
This file contains 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
public class Entity | |
{ | |
private readonly Dictionary<Type, IComponent> _components = new Dictionary<Type, IComponent>(); | |
protected Entity(IEnumerable<ComponentTypes> componentTypes) | |
{ | |
foreach (var ctype in componentTypes) | |
{ | |
AddComponent(ctype); | |
} | |
} | |
public virtual void OnDestroy() | |
{ | |
} | |
public bool HasComponent(ComponentTypes type) | |
{ | |
return _components.ContainsKey(ComponentFactory.ComponentLookup[type]); | |
} | |
public T GetComponent<T>() where T : IComponent | |
{ | |
var t = typeof(T); | |
if (!_components.ContainsKey(t)) | |
throw new KeyNotFoundException(string.Format("Unable to find component {0} on {1}", t, GetType())); | |
return (T) _components[t]; | |
} | |
protected void AddComponent(ComponentTypes ctype) | |
{ | |
var comp = ComponentFactory.Create(ctype, this); | |
AddComponent(comp); | |
} | |
protected void AddComponent(IComponent component) | |
{ | |
_components.Add(component.ObjectType, component); | |
EntityPool.AddComponent(component); | |
} | |
public IEnumerable<IComponent> Components | |
{ | |
get | |
{ | |
return _components.Values; | |
} | |
} | |
} |
This file contains 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
public static class EntityPool | |
{ | |
/// <summary> | |
/// A list of components sorted by their type, allowing O(1) lookup for components | |
/// </summary> | |
private static readonly Dictionary<ComponentTypes, List<IComponent>> ComponentPools = | |
new Dictionary<ComponentTypes, List<IComponent>>(Enum.GetNames(typeof(ComponentTypes)).Length); | |
/// <summary> | |
/// A list of systems on the game | |
/// </summary> | |
private static readonly List<ISystem> SystemPools = new List<ISystem>(); | |
/// <summary> | |
/// Static constructor | |
/// </summary> | |
static EntityPool() | |
{ | |
Reset(); | |
} | |
/// <summary> | |
/// Resets the component and system pools to their fresh, empty state | |
/// </summary> | |
public static void Reset() | |
{ | |
foreach (var componentList in ComponentPools.Values) | |
{ | |
var tempList = new List<IComponent>(componentList); | |
foreach (var component in tempList) | |
{ | |
DestroyComponent(component); | |
} | |
} | |
SystemPools.Clear(); | |
ComponentPools.Clear(); | |
var enumVals = Enum.GetValues(typeof(ComponentTypes)); | |
foreach (var ev in enumVals) | |
{ | |
var ct = (ComponentTypes) ev; | |
ComponentPools.Add(ct, new List<IComponent>()); | |
} | |
} | |
/// <summary> | |
/// Adds a component to the pool | |
/// </summary> | |
/// <param name="component"></param> | |
public static void AddComponent(IComponent component) | |
{ | |
ComponentPools[component.ComponentType].Add(component); | |
} | |
/// <summary> | |
/// Adds a system into the pool and sorts by execution priority | |
/// </summary> | |
/// <param name="system"></param> | |
public static void AddSystem(ISystem system) | |
{ | |
SystemPools.Add(system); | |
system.Init(); | |
SystemPools.Sort((a, b) => a.ExecutionPriority.CompareTo(b.ExecutionPriority)); | |
} | |
/// <summary> | |
/// Removes all the components attached to an entity from the pool | |
/// </summary> | |
/// <param name="entity"></param> | |
public static void DestroyEntity(Entity entity) | |
{ | |
foreach (var component in entity.Components) | |
{ | |
DestroyComponent(component); | |
} | |
entity.OnDestroy(); | |
} | |
/// <summary> | |
/// Removes the given component from the pool | |
/// </summary> | |
/// <param name="component"></param> | |
public static void DestroyComponent(IComponent component) | |
{ | |
component.OnDestroy(); | |
ComponentPools[component.ComponentType].Remove(component); | |
} | |
/// <summary> | |
/// Runs through all the systems and updates them | |
/// </summary> | |
public static void Update() | |
{ | |
foreach (var system in SystemPools) | |
{ | |
system.Update(); | |
} | |
} | |
/// <summary> | |
/// Gets the IComponents references of a given type in the system | |
/// </summary> | |
/// <param name="type"></param> | |
/// <returns></returns> | |
public static IEnumerable<IComponent> Get(ComponentTypes type) | |
{ | |
return ComponentPools[type]; | |
} | |
/// <summary> | |
/// Gets the components of a given type in the system | |
/// </summary> | |
/// <param name="type"></param> | |
/// <returns></returns> | |
public static IEnumerable<T> Get<T>(ComponentTypes type) where T : IComponent | |
{ | |
return ComponentPools[type].Cast<T>(); | |
} | |
/// <summary> | |
/// Gets a component type by ID | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="type"></param> | |
/// <param name="id"></param> | |
/// <returns></returns> | |
public static T Get<T>(ComponentTypes type, string id) where T : class, IComponent | |
{ | |
var comps = Get(type); | |
return comps.FirstOrDefault(o => o.ID == id) as T; | |
} | |
} |
This file contains 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
[Serializable] | |
public sealed class SampleComponent : AbstractComponent | |
{ | |
public float AWeirdNumber; | |
#region AbstractComponent Implementation | |
private static readonly Type _type = typeof(SampleComponent); | |
public SampleComponent(Entity owner) : base(owner) | |
{ | |
} | |
public override Type ObjectType | |
{ | |
get { return _type; } | |
} | |
public override ComponentTypes ComponentType | |
{ | |
get | |
{ | |
return ComponentTypes.SampleComponent; | |
} | |
} | |
#endregion | |
} |
This file contains 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
public sealed class SampleEntity : Entity | |
{ | |
public SampleEntity() : base(new List<ComponentTypes> | |
{ | |
ComponentTypes.SampleComponent | |
}) { } | |
} |
This file contains 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
public class SampleSystem : AbstractSystem | |
{ | |
public override void Update() | |
{ | |
var samples = EntityPool.Get<SampleComponent>(ComponentTypes.SampleComponent); | |
foreach (var sample in samples) | |
{ | |
sample.AWeirdNumber = Random.value; | |
} | |
} | |
#region Abstract System Implementation | |
private static readonly Type _type = typeof(SampleSystem); | |
public override Type ObjectType | |
{ | |
get { return _type; } | |
} | |
public override void Init() | |
{ | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment