adds some convenience methods and doc comments
parent
27c3fa1058
commit
85f99a565c
|
@ -136,7 +136,7 @@ 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;
|
componentIDToComponent[componentID] = component;
|
||||||
componentIDToEntityID[componentID] = entity.ID;
|
componentIDToEntityID[componentID] = entity.ID;
|
||||||
|
@ -163,7 +163,7 @@ namespace Encompass
|
||||||
return Enumerable.Empty<(Guid, TComponent)>();
|
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))
|
if (componentMessageTypeToExistingComponentIDs.TryGetValue(typeof(TComponent), out HashSet<Guid> idSet))
|
||||||
{
|
{
|
||||||
|
@ -187,16 +187,19 @@ namespace Encompass
|
||||||
|
|
||||||
internal (Guid, TComponent) ReadFirstExistingOrPendingComponentByType<TComponent>() where TComponent : struct, IComponent
|
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();
|
return ReadExistingAndPendingComponentsByType<TComponent>().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal (Guid, TComponent) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
|
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();
|
return ReadExistingComponentsByType<TComponent>().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal (Guid, TComponent) ReadFirstPendingComponentByType<TComponent>() where TComponent : struct, IComponent
|
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();
|
return ReadPendingComponentsByType<TComponent>().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,11 @@ using Encompass.Exceptions;
|
||||||
|
|
||||||
namespace Encompass
|
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
|
public abstract class Engine
|
||||||
{
|
{
|
||||||
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
||||||
|
@ -77,23 +82,42 @@ namespace Encompass
|
||||||
this.componentMessageManager = componentMessageManager;
|
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);
|
public abstract void Update(double dt);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and returns a new empty Entity.
|
||||||
|
/// </summary>
|
||||||
protected Entity CreateEntity()
|
protected Entity CreateEntity()
|
||||||
{
|
{
|
||||||
return entityManager.CreateEntity();
|
return entityManager.CreateEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if an Entity with the specified ID exists.
|
||||||
|
/// </summary>
|
||||||
protected bool EntityExists(Guid entityID)
|
protected bool EntityExists(Guid entityID)
|
||||||
{
|
{
|
||||||
return entityManager.EntityExists(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)
|
protected Entity GetEntity(Guid entityID)
|
||||||
{
|
{
|
||||||
return entityManager.GetEntity(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
|
protected Guid GetEntityIDByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||||
|
@ -107,11 +131,37 @@ namespace Encompass
|
||||||
return componentMessageManager.GetEntityIDByComponentID(componentID);
|
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
|
protected Entity GetEntityByComponentID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return GetEntity(GetEntityIDByComponentID<TComponent>(componentID));
|
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
|
protected TComponent GetComponentByID<TComponent>(Guid componentID) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||||
|
@ -142,7 +192,10 @@ namespace Encompass
|
||||||
return GetEntity(componentManager.GetEntityIDByComponentID(componentID));
|
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 pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<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 pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<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
|
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
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 pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
||||||
var existingRead = receiveTypes.Contains(typeof(ComponentMessage<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
|
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
var pendingRead = receiveTypes.Contains(typeof(PendingComponentMessage<TComponent>));
|
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
|
protected Guid SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
|
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
|
||||||
|
@ -288,11 +368,22 @@ namespace Encompass
|
||||||
return componentID;
|
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
|
protected Guid SetComponent<TComponent>(Guid componentID, TComponent component) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return SetComponent(GetEntityByComponentID<TComponent>(componentID), component);
|
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
|
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;
|
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : 0;
|
||||||
|
@ -317,6 +408,12 @@ namespace Encompass
|
||||||
return componentID;
|
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
|
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!sendTypes.Contains(typeof(TMessage)))
|
if (!sendTypes.Contains(typeof(TMessage)))
|
||||||
|
@ -327,6 +424,10 @@ namespace Encompass
|
||||||
messageManager.AddMessage(message);
|
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
|
protected void SendMessageDelayed<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
messageManager.AddMessageDelayed(message, time);
|
messageManager.AddMessageDelayed(message, time);
|
||||||
|
@ -355,6 +456,12 @@ namespace Encompass
|
||||||
componentMessageManager.AddPendingComponentMessage(message);
|
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
|
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!receiveTypes.Contains(typeof(TMessage)))
|
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||||
|
@ -365,11 +472,23 @@ namespace Encompass
|
||||||
return messageManager.GetMessagesByType<TMessage>();
|
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
|
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
return ReadMessages<TMessage>().First();
|
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
|
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
if (!receiveTypes.Contains(typeof(TMessage)))
|
if (!receiveTypes.Contains(typeof(TMessage)))
|
||||||
|
@ -380,11 +499,48 @@ namespace Encompass
|
||||||
return ReadMessages<TMessage>().Any();
|
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)
|
protected void Destroy(Guid entityID)
|
||||||
{
|
{
|
||||||
entityManager.MarkForDestroy(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)
|
protected void RemoveComponent(Guid componentID)
|
||||||
{
|
{
|
||||||
componentManager.MarkForRemoval(componentID);
|
componentManager.MarkForRemoval(componentID);
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
namespace Encompass.Engines
|
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
|
public abstract class Spawner<TMessage> : Engine where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
protected Spawner() : base()
|
protected Spawner() : base()
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
namespace Encompass
|
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 struct Entity : IEquatable<Entity>
|
||||||
{
|
{
|
||||||
public readonly Guid ID;
|
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
|
namespace Encompass
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Structs that implement IComponent are considered to be Components.
|
||||||
|
/// </summary>
|
||||||
public interface IComponent { }
|
public interface IComponent { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
namespace Encompass
|
namespace Encompass
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Structs that implement IDrawComponent are considered to be DrawComponents.
|
||||||
|
/// </summary>
|
||||||
public interface IDrawComponent { }
|
public interface IDrawComponent { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
namespace Encompass
|
namespace Encompass
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Structs that implement IMessage are considered to be Messages.
|
||||||
|
/// </summary>
|
||||||
public interface IMessage { }
|
public interface IMessage { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
namespace Encompass
|
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 class GeneralRenderer : Renderer
|
||||||
{
|
{
|
||||||
public abstract void Render();
|
public abstract void Render();
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace Encompass
|
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 class OrderedRenderer<TComponent> : Renderer where TComponent : struct, IComponent, IDrawComponent
|
||||||
{
|
{
|
||||||
public abstract void Render(Guid drawComponentID, TComponent drawComponent);
|
public abstract void Render(Guid drawComponentID, TComponent drawComponent);
|
||||||
|
|
|
@ -2,6 +2,9 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Encompass
|
namespace Encompass
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The World is a collection of Engines, Renderers, Entities, Components, and Messages that compose the simulation.
|
||||||
|
/// </summary>
|
||||||
public class World
|
public class World
|
||||||
{
|
{
|
||||||
private readonly List<Engine> enginesInOrder;
|
private readonly List<Engine> enginesInOrder;
|
||||||
|
@ -28,6 +31,10 @@ namespace Encompass
|
||||||
this.renderManager = renderManager;
|
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)
|
public void Update(double dt)
|
||||||
{
|
{
|
||||||
messageManager.ProcessDelayedMessages(dt);
|
messageManager.ProcessDelayedMessages(dt);
|
||||||
|
@ -45,6 +52,9 @@ namespace Encompass
|
||||||
componentManager.WriteComponents();
|
componentManager.WriteComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Causes the Renderers to draw.
|
||||||
|
/// </summary>
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
renderManager.Draw();
|
renderManager.Draw();
|
||||||
|
|
|
@ -7,6 +7,14 @@ using Encompass.Engines;
|
||||||
|
|
||||||
namespace Encompass
|
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
|
public class WorldBuilder
|
||||||
{
|
{
|
||||||
private readonly List<Engine> engines = new List<Engine>();
|
private readonly List<Engine> engines = new List<Engine>();
|
||||||
|
@ -35,26 +43,42 @@ namespace Encompass
|
||||||
renderManager = new RenderManager(componentManager, drawLayerManager);
|
renderManager = new RenderManager(componentManager, drawLayerManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and returns a new empty Entity.
|
||||||
|
/// </summary>
|
||||||
public Entity CreateEntity()
|
public Entity CreateEntity()
|
||||||
{
|
{
|
||||||
return entityManager.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
|
public void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
messageManager.AddMessage(message);
|
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
|
public void SendMessageDelayed<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
|
||||||
{
|
{
|
||||||
messageManager.AddMessageDelayed(message, time);
|
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
|
public Guid SetComponent<TComponent>(Entity entity, TComponent component, int priority = 0) where TComponent : struct, IComponent
|
||||||
{
|
{
|
||||||
return componentManager.MarkComponentForWrite(entity, component, priority);
|
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
|
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);
|
return componentManager.MarkDrawComponentForWrite(entity, component, priority, layer);
|
||||||
|
@ -66,6 +90,10 @@ namespace Encompass
|
||||||
AddEngine((Engine)Activator.CreateInstance(typeof(ComponentMessageEmitter<>).MakeGenericType(componentType)));
|
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
|
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
|
||||||
{
|
{
|
||||||
engine.AssignEntityManager(entityManager);
|
engine.AssignEntityManager(entityManager);
|
||||||
|
@ -126,6 +154,9 @@ namespace Encompass
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified OrderedRenderer to the World.
|
||||||
|
/// </summary>
|
||||||
public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent, IDrawComponent
|
public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent, IDrawComponent
|
||||||
{
|
{
|
||||||
renderer.AssignEntityManager(entityManager);
|
renderer.AssignEntityManager(entityManager);
|
||||||
|
@ -134,6 +165,12 @@ namespace Encompass
|
||||||
return renderer;
|
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
|
public TRenderer AddGeneralRenderer<TRenderer>(TRenderer renderer, int layer) where TRenderer : GeneralRenderer
|
||||||
{
|
{
|
||||||
renderer.AssignEntityManager(entityManager);
|
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()
|
public World Build()
|
||||||
{
|
{
|
||||||
BuildEngineGraph();
|
BuildEngineGraph();
|
||||||
|
|
|
@ -487,6 +487,38 @@ namespace Tests
|
||||||
Assert.That(results, Does.Contain((componentCID, mockComponent)));
|
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))]
|
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
|
||||||
class DestroyAndAddComponentEngine : Engine
|
class DestroyAndAddComponentEngine : Engine
|
||||||
{
|
{
|
||||||
|
@ -681,7 +713,7 @@ namespace Tests
|
||||||
Assert.Throws<ComponentTypeMismatchException>(() => world.Update(0.01f));
|
Assert.Throws<ComponentTypeMismatchException>(() => world.Update(0.01f));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EntityIDComponent : IComponent { public Guid entityID; }
|
struct EntityIDComponent : IComponent { public Guid entityID; }
|
||||||
static bool hasEntity;
|
static bool hasEntity;
|
||||||
|
|
||||||
[Reads(typeof(EntityIDComponent))]
|
[Reads(typeof(EntityIDComponent))]
|
||||||
|
@ -774,7 +806,7 @@ namespace Tests
|
||||||
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
||||||
{
|
{
|
||||||
RemoveComponent(componentID);
|
RemoveComponent(componentID);
|
||||||
SendMessageDelayed(new MockMessage {}, 1);
|
SendMessageDelayed(new MockMessage { }, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -789,7 +821,7 @@ namespace Tests
|
||||||
worldBuilder.AddEngine(new MessageReadEngine());
|
worldBuilder.AddEngine(new MessageReadEngine());
|
||||||
|
|
||||||
var entity = worldBuilder.CreateEntity();
|
var entity = worldBuilder.CreateEntity();
|
||||||
worldBuilder.SetComponent(entity, new MockComponent {});
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
|
@ -817,7 +849,7 @@ namespace Tests
|
||||||
foreach (var message in ReadMessages<MockMessage>())
|
foreach (var message in ReadMessages<MockMessage>())
|
||||||
{
|
{
|
||||||
var entity = CreateEntity();
|
var entity = CreateEntity();
|
||||||
SetComponent(entity, new MockComponent {});
|
SetComponent(entity, new MockComponent { });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -841,7 +873,7 @@ namespace Tests
|
||||||
worldBuilder.AddEngine(new ActivateComponentEngine());
|
worldBuilder.AddEngine(new ActivateComponentEngine());
|
||||||
worldBuilder.AddEngine(new RemoveComponentEngine());
|
worldBuilder.AddEngine(new RemoveComponentEngine());
|
||||||
|
|
||||||
worldBuilder.SendMessage(new MockMessage {});
|
worldBuilder.SendMessage(new MockMessage { });
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
|
@ -858,13 +890,13 @@ namespace Tests
|
||||||
{
|
{
|
||||||
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
foreach (var (componentID, component) in ReadComponents<MockComponent>())
|
||||||
{
|
{
|
||||||
SetComponent(componentID, new MockComponent {});
|
SetComponent(componentID, new MockComponent { });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Receives(typeof(DestroyComponentMessage))]
|
[Receives(typeof(DestroyComponentMessage))]
|
||||||
class DestroyEntityEngine : Engine
|
class DestroyEntityByIDEngine : Engine
|
||||||
{
|
{
|
||||||
public override void Update(double dt)
|
public override void Update(double dt)
|
||||||
{
|
{
|
||||||
|
@ -880,10 +912,10 @@ namespace Tests
|
||||||
{
|
{
|
||||||
var worldBuilder = new WorldBuilder();
|
var worldBuilder = new WorldBuilder();
|
||||||
worldBuilder.AddEngine(new AddComponentEngine());
|
worldBuilder.AddEngine(new AddComponentEngine());
|
||||||
worldBuilder.AddEngine(new DestroyEntityEngine());
|
worldBuilder.AddEngine(new DestroyEntityByIDEngine());
|
||||||
|
|
||||||
var entity = worldBuilder.CreateEntity();
|
var entity = worldBuilder.CreateEntity();
|
||||||
worldBuilder.SetComponent(entity, new MockComponent {});
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
||||||
worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
|
worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
@ -891,5 +923,122 @@ namespace Tests
|
||||||
|
|
||||||
Assert.DoesNotThrow(() => 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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<RootNamespace>Tests</RootNamespace>
|
<RootNamespace>Tests</RootNamespace>
|
||||||
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
|
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
|
||||||
|
|
Loading…
Reference in New Issue