adds some convenience methods and doc comments
parent
27c3fa1058
commit
85f99a565c
|
@ -12,7 +12,7 @@ namespace Encompass
|
|||
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
|
||||
|
||||
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
|
||||
|
||||
|
||||
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToExistingComponentIDs = new Dictionary<Type, HashSet<Guid>>();
|
||||
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToPendingComponentIDs = new Dictionary<Type, HashSet<Guid>>();
|
||||
private readonly Dictionary<Type, HashSet<Guid>> componentMessageTypeToComponentIDs = new Dictionary<Type, HashSet<Guid>>();
|
||||
|
@ -136,12 +136,12 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent: struct, IComponent
|
||||
private void RegisterExistingOrPendingComponentMessage<TComponent>(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
componentIDToComponent[componentID] = component;
|
||||
componentIDToEntityID[componentID] = entity.ID;
|
||||
componentIDToType[componentID] = typeof(TComponent);
|
||||
|
||||
|
||||
if (!componentMessageTypeToComponentIDs.ContainsKey(typeof(TComponent)))
|
||||
{
|
||||
componentMessageTypeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>());
|
||||
|
@ -163,7 +163,7 @@ namespace Encompass
|
|||
return Enumerable.Empty<(Guid, TComponent)>();
|
||||
}
|
||||
|
||||
internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByType<TComponent>() where TComponent: struct, IComponent
|
||||
internal IEnumerable<(Guid, TComponent)> ReadExistingComponentsByType<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
|
||||
{
|
||||
|
@ -187,16 +187,19 @@ namespace Encompass
|
|||
|
||||
internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByType<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
if (!SomeExistingOrPendingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
|
||||
return ReadExistingAndPendingComponentsByType<TComponent>().First();
|
||||
}
|
||||
|
||||
internal (Guid, TComponent) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
|
||||
return ReadExistingComponentsByType<TComponent>().First();
|
||||
}
|
||||
|
||||
internal (Guid, TComponent) ReadFirstPendingComponentByType<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
|
||||
return ReadPendingComponentsByType<TComponent>().First();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,11 @@ using Encompass.Exceptions;
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// Engines are the Encompass notion of an ECS System.
|
||||
/// They are responsible for reading the World state, reading messages, emitting messages, and creating or mutating Entities and Components.
|
||||
/// Engines run once per World Update.
|
||||
/// </summary>
|
||||
public abstract class Engine
|
||||
{
|
||||
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
||||
|
@ -77,23 +82,42 @@ namespace Encompass
|
|||
this.componentMessageManager = componentMessageManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs once per World update with the calculated delta-time.
|
||||
/// </summary>
|
||||
/// <param name="dt">The time in seconds that has elapsed since the previous frame.</param>
|
||||
public abstract void Update(double dt);
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a new empty Entity.
|
||||
/// </summary>
|
||||
protected Entity CreateEntity()
|
||||
{
|
||||
return entityManager.CreateEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if an Entity with the specified ID exists.
|
||||
/// </summary>
|
||||
protected bool EntityExists(Guid entityID)
|
||||
{
|
||||
return entityManager.EntityExists(entityID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Entity with the specified ID.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.EntityNotFoundException">
|
||||
/// Thrown when an Entity with the given ID does not exist.
|
||||
/// </exception>
|
||||
protected Entity GetEntity(Guid entityID)
|
||||
{
|
||||
return entityManager.GetEntity(entityID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Entity ID associated with the specified Component Type and ID.
|
||||
/// </summary>
|
||||
protected Guid GetEntityIDByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
|
@ -107,11 +131,37 @@ namespace Encompass
|
|||
return componentMessageManager.GetEntityIDByComponentID(componentID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Entity associated with the specified Component Type and ID.
|
||||
/// </summary>
|
||||
protected Entity GetEntityByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||
{
|
||||
return GetEntity(GetEntityIDByComponentID<TComponent>(componentID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an Entity containing the specified Component type.
|
||||
/// </summary>
|
||||
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
var (id, component) = ReadComponent<TComponent>();
|
||||
return GetEntityByComponentID<TComponent>(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Entities containing the specified Component type.
|
||||
/// </summary>
|
||||
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
foreach (var (id, component) in ReadComponents<TComponent>())
|
||||
{
|
||||
yield return GetEntityByComponentID<TComponent>(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Component struct with the specified Component Type and ID.
|
||||
/// </summary>
|
||||
protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
|
@ -142,7 +192,10 @@ namespace Encompass
|
|||
return GetEntity(componentManager.GetEntityIDByComponentID(componentID));
|
||||
}
|
||||
|
||||
protected IEnumerable<ValueTuple<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
|
||||
/// <summary>
|
||||
/// Returns all of the Components with the specified Component Type.
|
||||
/// </summary>
|
||||
protected IEnumerable<(Guid, TComponent)> ReadComponents<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
|
||||
|
@ -164,7 +217,10 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
protected ValueTuple<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
|
||||
/// <summary>
|
||||
/// Returns a Component with the specified Component Type and ID. If multiples exist, an arbitrary Component is returned.
|
||||
/// </summary>
|
||||
protected (Guid, TComponent) ReadComponent<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
|
||||
|
@ -186,6 +242,9 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any Component with the specified Component Type exists.
|
||||
/// </summary>
|
||||
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
|
@ -208,7 +267,16 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
protected ValueTuple<Guid, TComponent> GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||
/// <summary>
|
||||
/// Returns a Component with the specified Type that exists on the Entity.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.NoComponentOfTypeOnEntityException">
|
||||
/// Thrown when the Entity does not have a Component of the specified Type
|
||||
/// </exception>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalReadException">
|
||||
/// Thrown when the Engine does not declare that it reads the given Component Type.
|
||||
/// </exception>
|
||||
protected (Guid, TComponent) GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<TComponent>));
|
||||
|
@ -241,6 +309,12 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Entity has a Component of the given Type.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalReadException">
|
||||
/// Thrown when the Engine does not declare that is Reads the given Component Type.
|
||||
/// </exception>
|
||||
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||
{
|
||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||
|
@ -264,6 +338,12 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets Component data for the specified Component Type on the specified Entity. If Component data for this Type already existed on the Entity, the component data is overwritten.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
|
||||
/// Thrown when the Engine does not declare that it Writes the given Component Type.
|
||||
/// </exception>
|
||||
protected Guid SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
|
||||
|
@ -288,11 +368,22 @@ namespace Encompass
|
|||
return componentID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overwrites Component struct data associated with the specified Component ID.
|
||||
/// </summary>
|
||||
protected Guid SetComponent<TComponent>(Guid componentID, TComponent component) where TComponent : struct, IComponent
|
||||
{
|
||||
return SetComponent(GetEntityByComponentID<TComponent>(componentID), component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets Draw Component data for the specified Component Type on the specified Entity.
|
||||
/// This method must be used for the Draw Component to be readable by an OrderedRenderer.
|
||||
/// If Component data for this Type already existed on the Entity, the component data is overwritten.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
|
||||
/// Thrown when the Engine does not declare that it Writes the given Component Type.
|
||||
/// </exception>
|
||||
protected Guid SetDrawComponent<TComponent>(Entity entity, TComponent component, int layer = 0) where TComponent : struct, IComponent, IDrawComponent
|
||||
{
|
||||
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
|
||||
|
@ -317,6 +408,12 @@ namespace Encompass
|
|||
return componentID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a Message.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalSendException">
|
||||
/// Thrown when the Engine does not declare that it Sends the Message Type.
|
||||
/// </exception>
|
||||
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||
{
|
||||
if (!sendTypes.Contains(typeof(TMessage)))
|
||||
|
@ -327,6 +424,10 @@ namespace Encompass
|
|||
messageManager.AddMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message after the specified number of seconds.
|
||||
/// </summary>
|
||||
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
|
||||
protected void SendMessageDelayed<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
|
||||
{
|
||||
messageManager.AddMessageDelayed(message, time);
|
||||
|
@ -355,6 +456,12 @@ namespace Encompass
|
|||
componentMessageManager.AddPendingComponentMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all messages of the specified Type.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalReadException">
|
||||
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
|
||||
/// </exception>
|
||||
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
|
||||
{
|
||||
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||
|
@ -365,11 +472,23 @@ namespace Encompass
|
|||
return messageManager.GetMessagesByType<TMessage>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an arbitrary message of the specified Type.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalReadException">
|
||||
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
|
||||
/// </exception>
|
||||
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
|
||||
{
|
||||
return ReadMessages<TMessage>().First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a Message of the specified Type has been sent this frame.
|
||||
/// </summary>
|
||||
/// <exception cref="Encompass.Exceptions.IllegalReadException">
|
||||
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
|
||||
/// </exception>
|
||||
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
|
||||
{
|
||||
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||
|
@ -380,11 +499,48 @@ namespace Encompass
|
|||
return ReadMessages<TMessage>().Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the Entity with the specified ID. This also removes all of the Components associated with the Entity.
|
||||
/// Entity destruction takes place after all the Engines have been processed by World Update.
|
||||
/// </summary>
|
||||
protected void Destroy(Guid entityID)
|
||||
{
|
||||
entityManager.MarkForDestroy(entityID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the specified Entity. This also removes all of the Components associated with the Entity.
|
||||
/// Entity destruction takes place after all the Engines have been processed by World Update.
|
||||
/// </summary>
|
||||
protected void Destroy(Entity entity)
|
||||
{
|
||||
entityManager.MarkForDestroy(entity.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys an arbitrary Entity containing a Component of the specified Type.
|
||||
/// Entity destruction takes place after all the Engines have been processed by World Update.
|
||||
/// </summary>
|
||||
protected void DestroyWith<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
Destroy(ReadEntity<TComponent>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all Entities containing a Component of the specified Type.
|
||||
/// Entity destruction takes place after all the Engines have been processed by World Update.
|
||||
/// </summary>
|
||||
protected void DestroyAllWith<TComponent>() where TComponent : struct, IComponent
|
||||
{
|
||||
foreach (var entity in ReadEntities<TComponent>())
|
||||
{
|
||||
Destroy(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Component with the specified ID from its Entity.
|
||||
/// </summary>
|
||||
protected void RemoveComponent(Guid componentID)
|
||||
{
|
||||
componentManager.MarkForRemoval(componentID);
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
namespace Encompass.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// A Spawner is a special type of Engine that runs a Spawn method in response to each Message it receives.
|
||||
/// Spawners are useful for organizing the building of new Entities in your game.
|
||||
/// </summary>
|
||||
public abstract class Spawner<TMessage> : Engine where TMessage : struct, IMessage
|
||||
{
|
||||
protected Spawner() : base()
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// An Entity is a structure composed of a unique ID and a collection of Components.
|
||||
/// An Entity may only have a single Component of any particular Type.
|
||||
/// </summary>
|
||||
public struct Entity : IEquatable<Entity>
|
||||
{
|
||||
public readonly Guid ID;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Encompass.Exceptions
|
||||
{
|
||||
[Serializable]
|
||||
internal class NoComponentOfTypeException : Exception
|
||||
{
|
||||
public NoComponentOfTypeException()
|
||||
{
|
||||
}
|
||||
|
||||
public NoComponentOfTypeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NoComponentOfTypeException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected NoComponentOfTypeException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// Structs that implement IComponent are considered to be Components.
|
||||
/// </summary>
|
||||
public interface IComponent { }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// Structs that implement IDrawComponent are considered to be DrawComponents.
|
||||
/// </summary>
|
||||
public interface IDrawComponent { }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// Structs that implement IMessage are considered to be Messages.
|
||||
/// </summary>
|
||||
public interface IMessage { }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// GeneralRenderer is a Renderer which generically reads the game state in order to draw elements to the screen.
|
||||
/// GeneralRenderers have a layer specified when they are added to the World.
|
||||
/// </summary>
|
||||
public abstract class GeneralRenderer : Renderer
|
||||
{
|
||||
public abstract void Render();
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// OrdereredRenderer provides a structure for the common pattern of wishing to draw a specific DrawComponent at a specific layer.
|
||||
/// </summary>
|
||||
public abstract class OrderedRenderer<TComponent> : Renderer where TComponent : struct, IComponent, IDrawComponent
|
||||
{
|
||||
public abstract void Render(Guid drawComponentID, TComponent drawComponent);
|
||||
|
|
|
@ -2,6 +2,9 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// The World is a collection of Engines, Renderers, Entities, Components, and Messages that compose the simulation.
|
||||
/// </summary>
|
||||
public class World
|
||||
{
|
||||
private readonly List<Engine> enginesInOrder;
|
||||
|
@ -28,10 +31,14 @@ namespace Encompass
|
|||
this.renderManager = renderManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drives the simulation. Should be called from your game engine's update loop.
|
||||
/// </summary>
|
||||
/// <param name="dt">The time in seconds that has passed since the previous frame.</param>
|
||||
public void Update(double dt)
|
||||
{
|
||||
messageManager.ProcessDelayedMessages(dt);
|
||||
|
||||
|
||||
foreach (var engine in enginesInOrder)
|
||||
{
|
||||
engine.Update(dt);
|
||||
|
@ -45,6 +52,9 @@ namespace Encompass
|
|||
componentManager.WriteComponents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the Renderers to draw.
|
||||
/// </summary>
|
||||
public void Draw()
|
||||
{
|
||||
renderManager.Draw();
|
||||
|
|
|
@ -7,6 +7,14 @@ using Encompass.Engines;
|
|||
|
||||
namespace Encompass
|
||||
{
|
||||
/// <summary>
|
||||
/// WorldBuilder is used to construct a World from Engines, Renderers, and an initial state of Entities, Components, and Messages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// WorldBuilder enforces certain rules about Engine structure. It is forbidden to have messages create cycles between Engines,
|
||||
/// and no Component may be written by more than one Engine.
|
||||
/// The WorldBuilder uses Engines and their Message read/emit information to determine a valid ordering of the Engines, which is given to the World.
|
||||
/// </remarks>
|
||||
public class WorldBuilder
|
||||
{
|
||||
private readonly List<Engine> engines = new List<Engine>();
|
||||
|
@ -35,26 +43,42 @@ namespace Encompass
|
|||
renderManager = new RenderManager(componentManager, drawLayerManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a new empty Entity.
|
||||
/// </summary>
|
||||
public Entity CreateEntity()
|
||||
{
|
||||
return entityManager.CreateEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the given Message should be sent immediately on the first World Update.
|
||||
/// </summary>
|
||||
public void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||
{
|
||||
messageManager.AddMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the given Message should be sent after the specified number of seconds after the first World Update.
|
||||
/// </summary>
|
||||
public void SendMessageDelayed<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
|
||||
{
|
||||
messageManager.AddMessageDelayed(message, time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets Component data for the specified Component Type on the specified Entity.
|
||||
/// </summary>
|
||||
public Guid SetComponent<TComponent>(Entity entity, TComponent component, int priority = 0) where TComponent : struct, IComponent
|
||||
{
|
||||
return componentManager.MarkComponentForWrite(entity, component, priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets Draw Component data for the specified Component Type on the specified Entity.
|
||||
/// This method must be used for the Draw Component to be readable by an OrderedRenderer.
|
||||
/// </summary>
|
||||
public Guid SetDrawComponent<TComponent>(Entity entity, TComponent component, int priority = 0, int layer = 0) where TComponent : struct, IComponent, IDrawComponent
|
||||
{
|
||||
return componentManager.MarkDrawComponentForWrite(entity, component, priority, layer);
|
||||
|
@ -66,6 +90,10 @@ namespace Encompass
|
|||
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified Engine to the World.
|
||||
/// </summary>
|
||||
/// <param name="engine">An instance of an Engine.</param>
|
||||
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
||||
{
|
||||
engine.AssignEntityManager(entityManager);
|
||||
|
@ -126,6 +154,9 @@ namespace Encompass
|
|||
return engine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified OrderedRenderer to the World.
|
||||
/// </summary>
|
||||
public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent, IDrawComponent
|
||||
{
|
||||
renderer.AssignEntityManager(entityManager);
|
||||
|
@ -134,6 +165,12 @@ namespace Encompass
|
|||
return renderer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified GeneralRenderer to the World at the specified layer.
|
||||
/// Higher layer numbers draw on top of lower layer numbers.
|
||||
/// </summary>
|
||||
/// <param name="renderer">An instance of a GeneralRenderer.</param>
|
||||
/// <param name="layer">The layer at which the GeneralRenderer should render. Higher numbers draw over lower numbers.</param>
|
||||
public TRenderer AddGeneralRenderer<TRenderer>(TRenderer renderer, int layer) where TRenderer : GeneralRenderer
|
||||
{
|
||||
renderer.AssignEntityManager(entityManager);
|
||||
|
@ -164,6 +201,11 @@ namespace Encompass
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the World out of the state specified on the WorldBuilder.
|
||||
/// Validates and constructs an ordering of the given Engines.
|
||||
/// </summary>
|
||||
/// <returns>An instance of World.</returns>
|
||||
public World Build()
|
||||
{
|
||||
BuildEngineGraph();
|
||||
|
|
|
@ -487,6 +487,38 @@ namespace Tests
|
|||
Assert.That(results, Does.Contain((componentCID, mockComponent)));
|
||||
}
|
||||
|
||||
[Receives(typeof(DestroyComponentMessage))]
|
||||
class DestroyEntityEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
foreach (var message in ReadMessages<DestroyComponentMessage>())
|
||||
{
|
||||
Destroy(message.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DestroyEntityWithoutID()
|
||||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new AddComponentEngine());
|
||||
worldBuilder.AddEngine(new DestroyEntityEngine());
|
||||
worldBuilder.AddEngine(new ReaderEngine());
|
||||
|
||||
var mockComponent = new MockComponent { };
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
var componentID = worldBuilder.SetComponent(entity, mockComponent);
|
||||
worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
|
||||
Assert.DoesNotThrow(() => world.Update(0.01));
|
||||
Assert.That(results, Does.Not.Contain((componentID, mockComponent)));
|
||||
}
|
||||
|
||||
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
|
||||
class DestroyAndAddComponentEngine : Engine
|
||||
{
|
||||
|
@ -681,7 +713,7 @@ namespace Tests
|
|||
Assert.Throws<ComponentTypeMismatchException>(() => world.Update(0.01f));
|
||||
}
|
||||
|
||||
struct EntityIDComponent : IComponent { public Guid entityID; }
|
||||
struct EntityIDComponent : IComponent { public Guid entityID; }
|
||||
static bool hasEntity;
|
||||
|
||||
[Reads(typeof(EntityIDComponent))]
|
||||
|
@ -774,7 +806,7 @@ namespace Tests
|
|||
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
||||
{
|
||||
RemoveComponent(componentID);
|
||||
SendMessageDelayed(new MockMessage {}, 1);
|
||||
SendMessageDelayed(new MockMessage { }, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -787,9 +819,9 @@ namespace Tests
|
|||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new DelayedMessageEngine());
|
||||
worldBuilder.AddEngine(new MessageReadEngine());
|
||||
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent {});
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
|
||||
|
@ -817,7 +849,7 @@ namespace Tests
|
|||
foreach (var message in ReadMessages<MockMessage>())
|
||||
{
|
||||
var entity = CreateEntity();
|
||||
SetComponent(entity, new MockComponent {});
|
||||
SetComponent(entity, new MockComponent { });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -841,7 +873,7 @@ namespace Tests
|
|||
worldBuilder.AddEngine(new ActivateComponentEngine());
|
||||
worldBuilder.AddEngine(new RemoveComponentEngine());
|
||||
|
||||
worldBuilder.SendMessage(new MockMessage {});
|
||||
worldBuilder.SendMessage(new MockMessage { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
|
||||
|
@ -858,13 +890,13 @@ namespace Tests
|
|||
{
|
||||
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
||||
{
|
||||
SetComponent(componentID, new MockComponent {});
|
||||
SetComponent(componentID, new MockComponent { });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Receives(typeof(DestroyComponentMessage))]
|
||||
class DestroyEntityEngine : Engine
|
||||
class DestroyEntityByIDEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
|
@ -880,16 +912,133 @@ namespace Tests
|
|||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new AddComponentEngine());
|
||||
worldBuilder.AddEngine(new DestroyEntityEngine());
|
||||
worldBuilder.AddEngine(new DestroyEntityByIDEngine());
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent {});
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
|
||||
|
||||
Assert.DoesNotThrow(() => world.Update(0.01));
|
||||
}
|
||||
|
||||
static Entity readEntity;
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
class ReadEntityByComponentTypeEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
readEntity = ReadEntity<MockComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEntityByComponentType()
|
||||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
|
||||
entity.Should().BeEquivalentTo(readEntity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Entity[] readEntities;
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
class ReadEntitiesWithComponentTypeEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
readEntities = ReadEntities<MockComponent>().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadEntities()
|
||||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
|
||||
worldBuilder.AddEngine(new DestroyAllWithEngine());
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
|
||||
var entityB = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entityB, new MockComponent { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
|
||||
readEntities.Should().Contain(entity);
|
||||
readEntities.Should().Contain(entityB);
|
||||
}
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
class DestroyWithEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
if (SomeComponent<MockComponent>())
|
||||
{
|
||||
DestroyWith<MockComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DestroyWith()
|
||||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
|
||||
worldBuilder.AddEngine(new DestroyWithEngine());
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
world.Update(0.01); // update twice so the read happens after destroy
|
||||
|
||||
readEntities.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Reads(typeof(MockComponent))]
|
||||
class DestroyAllWithEngine : Engine
|
||||
{
|
||||
public override void Update(double dt)
|
||||
{
|
||||
DestroyAllWith<MockComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DestroyAllWith()
|
||||
{
|
||||
var worldBuilder = new WorldBuilder();
|
||||
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
|
||||
worldBuilder.AddEngine(new DestroyAllWithEngine());
|
||||
|
||||
var entity = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||
|
||||
var entityB = worldBuilder.CreateEntity();
|
||||
worldBuilder.SetComponent(entityB, new MockComponent { });
|
||||
|
||||
var world = worldBuilder.Build();
|
||||
world.Update(0.01);
|
||||
world.Update(0.01); // update twice so the read happens after destroy
|
||||
|
||||
readEntities.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Tests</RootNamespace>
|
||||
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
|
||||
|
|
Loading…
Reference in New Issue