1953 lines
62 KiB
C#
1953 lines
62 KiB
C#
using NUnit.Framework;
|
|
using FluentAssertions;
|
|
|
|
using Encompass;
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using Encompass.Exceptions;
|
|
|
|
namespace Tests
|
|
{
|
|
struct MockComponent : IComponent
|
|
{
|
|
public int myInt;
|
|
public string myString;
|
|
}
|
|
|
|
public class EngineTest
|
|
{
|
|
static List<MockComponent> resultComponents;
|
|
static MockComponent resultComponent;
|
|
|
|
static List<MockMessage> resultMessages = new List<MockMessage>();
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentsTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponents = ReadComponents<MockComponent>().ToList();
|
|
}
|
|
}
|
|
|
|
static List<(MockComponent, Entity)> resultComponentsIncludingEntity;
|
|
static (MockComponent, Entity) resultComponentIncludingEntity;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentsIncludingEntityEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponentsIncludingEntity = ReadComponentsIncludingEntity<MockComponent>().ToList();
|
|
}
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponent = ReadComponent<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentIncludingEntityEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponentIncludingEntity = ReadComponentIncludingEntity<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentsTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
MockComponent mockComponentB;
|
|
mockComponentB.myInt = 1;
|
|
mockComponentB.myString = "howdy";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
worldBuilder.SetComponent(entityB, mockComponentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
resultComponents.Should().Contain(mockComponent);
|
|
resultComponents.Should().Contain(mockComponentB);
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentsIncludingEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
MockComponent mockComponentB;
|
|
mockComponentB.myInt = 1;
|
|
mockComponentB.myString = "howdy";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
worldBuilder.SetComponent(entityB, mockComponentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
var resultComponentValues = resultComponentsIncludingEntity.Select((kv) => kv.Item1);
|
|
resultComponentValues.Should().Contain(mockComponent);
|
|
resultComponentValues.Should().Contain(mockComponentB);
|
|
|
|
var resultEntities = resultComponentsIncludingEntity.Select((kv) => kv.Item2);
|
|
resultEntities.Should().Contain(entity);
|
|
resultEntities.Should().Contain(entityB);
|
|
|
|
resultComponentsIncludingEntity.Should().Contain((mockComponent, entity));
|
|
resultComponentsIncludingEntity.Should().Contain((mockComponentB, entityB));
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.AreEqual(mockComponent, resultComponent);
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentWhenMultipleComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
MockComponent mockComponentB;
|
|
mockComponentB.myInt = 1;
|
|
mockComponentB.myString = "howdy";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
worldBuilder.SetComponent(entityB, mockComponentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB));
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentWithEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentIncludingEntityEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
(mockComponent, entity).Should().BeEquivalentTo(resultComponentIncludingEntity);
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
public class UpdateComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
|
|
|
|
component.myInt = 420;
|
|
component.myString = "blaze it";
|
|
SetComponent(entity, component);
|
|
}
|
|
}
|
|
|
|
// this test needs to be improved...
|
|
|
|
[Test]
|
|
public void UpdateComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
worldBuilder.AddEngine(new UpdateComponentTestEngine());
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
Assert.AreEqual(420, resultComponent.myInt);
|
|
Assert.AreEqual("blaze it", resultComponent.myString);
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class UndeclaredUpdateComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
|
|
|
|
component.myInt = 420;
|
|
component.myString = "blaze it";
|
|
SetComponent(entity, component);
|
|
|
|
component = ReadComponent<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void UpdateUndeclaredComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new UndeclaredUpdateComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
|
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
|
|
}
|
|
|
|
struct MockMessage : IMessage
|
|
{
|
|
public string myString;
|
|
}
|
|
|
|
[Sends(typeof(MockMessage))]
|
|
public class MessageEmitEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
[Receives(typeof(MockMessage))]
|
|
public class MessageReadEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultMessages = this.ReadMessages<MockMessage>().ToList();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EmitAndReadMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new MessageEmitEngine());
|
|
worldBuilder.AddEngine(new MessageReadEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.AreEqual(resultMessages.First().myString, "howdy");
|
|
}
|
|
|
|
public class UndeclaredMessageEmitEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
static IEnumerable<MockMessage> emptyReadMessagesResult;
|
|
|
|
[Receives(typeof(MockMessage))]
|
|
class ReadMessagesWhenNoneExistEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
emptyReadMessagesResult = ReadMessages<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadMessagesWhenNoneHaveBeenEmitted()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadMessagesWhenNoneExistEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
emptyReadMessagesResult.Should().BeEmpty();
|
|
}
|
|
|
|
[Test]
|
|
public void EmitUndeclaredMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new UndeclaredMessageEmitEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
|
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to send undeclared Message MockMessage"));
|
|
}
|
|
|
|
static bool someTest;
|
|
|
|
[Sends(typeof(MockMessage))]
|
|
class EmitMockMessageEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
[Receives(typeof(MockMessage))]
|
|
class SomeMessageTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
someTest = this.SomeMessage<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SomeMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new EmitMockMessageEngine());
|
|
worldBuilder.AddEngine(new SomeMessageTestEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(someTest, Is.True);
|
|
}
|
|
|
|
class UndeclaredSomeMessageEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
someTest = this.SomeMessage<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void UndeclaredSomeMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new EmitMockMessageEngine());
|
|
worldBuilder.AddEngine(new UndeclaredSomeMessageEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
|
|
}
|
|
|
|
struct EntityMessage : IMessage, IHasEntity
|
|
{
|
|
public EntityMessage(Entity entity, int myInt)
|
|
{
|
|
Entity = entity;
|
|
MyInt = myInt;
|
|
}
|
|
|
|
public Entity Entity { get; }
|
|
public int MyInt { get; }
|
|
}
|
|
|
|
[Sends(typeof(EntityMessage), typeof(MockMessage))]
|
|
class EntityMessageEmitterEngine : Engine
|
|
{
|
|
private Entity _entity;
|
|
|
|
public EntityMessageEmitterEngine(Entity entity)
|
|
{
|
|
_entity = entity;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
SendMessage(new EntityMessage(_entity, 2));
|
|
SendMessage(new EntityMessage(_entity, 4));
|
|
SendMessage(new EntityMessage(_entity, 5));
|
|
SendMessage(new MockMessage());
|
|
}
|
|
}
|
|
|
|
static List<EntityMessage> entityMessageResults;
|
|
|
|
[Receives(typeof(EntityMessage))]
|
|
class EntityMessageReceiverEngine : Engine
|
|
{
|
|
private Entity _entity;
|
|
|
|
public EntityMessageReceiverEngine(Entity entity)
|
|
{
|
|
_entity = entity;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entityMessageResults = ReadMessagesWithEntity<EntityMessage>(_entity).ToList();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MessagesWithEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddEngine(new EntityMessageEmitterEngine(entity));
|
|
worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
entityMessageResults.Should().HaveCount(3);
|
|
entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 2));
|
|
entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 4));
|
|
entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 5));
|
|
}
|
|
|
|
[Test]
|
|
public void NoMessagesWithEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity));
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01);
|
|
|
|
entityMessageResults.Should().BeEmpty();
|
|
}
|
|
|
|
[Sends(typeof(EntityMessage), typeof(MockMessage))]
|
|
class EntityMessageSingularEmitterEngine : Engine
|
|
{
|
|
private Entity _entity;
|
|
|
|
public EntityMessageSingularEmitterEngine(Entity entity)
|
|
{
|
|
_entity = entity;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
SendMessage(new EntityMessage(_entity, 2));
|
|
SendMessage(new MockMessage());
|
|
}
|
|
}
|
|
|
|
static EntityMessage entityMessageResult;
|
|
|
|
[Receives(typeof(EntityMessage))]
|
|
class SingularMessageWithEntityEngine : Engine
|
|
{
|
|
private Entity _entity;
|
|
|
|
public SingularMessageWithEntityEngine(Entity entity)
|
|
{
|
|
_entity = entity;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entityMessageResult = ReadMessageWithEntity<EntityMessage>(_entity);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MessageWithEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.AddEngine(new EntityMessageSingularEmitterEngine(entity));
|
|
worldBuilder.AddEngine(new SingularMessageWithEntityEngine(entity));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
entityMessageResult.Should().Be(new EntityMessage(entity, 2));
|
|
}
|
|
|
|
class SomeComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
Assert.IsTrue(SomeComponent<MockComponent>());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SomeComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
}
|
|
|
|
static (MockComponent, Entity) pairA;
|
|
static (MockComponent, Entity) pairB;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class SameValueComponentReadEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var components = ReadComponentsIncludingEntity<MockComponent>();
|
|
|
|
pairA = components.First();
|
|
pairB = components.Last();
|
|
}
|
|
}
|
|
|
|
// Tests that components with identical values should be distinguishable by their entities
|
|
[Test]
|
|
public void SameValueComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new SameValueComponentReadEngine());
|
|
|
|
MockComponent componentA;
|
|
componentA.myInt = 20;
|
|
componentA.myString = "hello";
|
|
|
|
MockComponent componentB;
|
|
componentB.myInt = 20;
|
|
componentB.myString = "hello";
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, componentA);
|
|
|
|
var entityB = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entityB, componentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(EngineTest.pairA, Is.Not.EqualTo(EngineTest.pairB));
|
|
Assert.That(EngineTest.pairA.Item1, Is.EqualTo(EngineTest.pairB.Item1));
|
|
}
|
|
|
|
static IEnumerable<(MockComponent, Entity)> emptyComponentReadResult;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class ReadEmptyMockComponentsEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
emptyComponentReadResult = ReadComponentsIncludingEntity<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentsOfTypeWhereNoneExist()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadEmptyMockComponentsEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(emptyComponentReadResult, Is.Empty);
|
|
}
|
|
|
|
struct DestroyerComponent : IComponent { }
|
|
|
|
[Reads(typeof(DestroyerComponent))]
|
|
class DestroyerEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (component, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
|
|
{
|
|
Destroy(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
static List<(MockComponent, Entity)> results;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class ReaderEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
results = ReadComponentsIncludingEntity<MockComponent>().ToList();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void DestroyEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new DestroyerEngine());
|
|
worldBuilder.AddEngine(new ReaderEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
|
|
DestroyerComponent destroyerComponent;
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 2;
|
|
mockComponent.myString = "blah";
|
|
|
|
worldBuilder.SetComponent(entity, destroyerComponent);
|
|
worldBuilder.SetComponent(entity, mockComponent);
|
|
|
|
worldBuilder.SetComponent(entityB, destroyerComponent);
|
|
worldBuilder.SetComponent(entityB, mockComponent);
|
|
|
|
worldBuilder.SetComponent(entityC, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
Assert.That(results, Does.Not.Contain((mockComponent, entity)));
|
|
Assert.That(results, Does.Not.Contain((mockComponent, entityB)));
|
|
Assert.That(results, Does.Contain((mockComponent, entityC)));
|
|
}
|
|
|
|
[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();
|
|
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((mockComponent, entity)));
|
|
}
|
|
|
|
[Reads(typeof(DestroyerComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class DestroyAndAddComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (componentPair, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
Destroy(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void DestroyEntityWhileRemovingComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new DestroyAndAddComponentEngine());
|
|
worldBuilder.AddEngine(new ReaderEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new DestroyerComponent());
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.DoesNotThrow(() => world.Update(0.01));
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[WritesImmediate(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class AddAndRemoveMockComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
SetComponent(entity, new MockComponent());
|
|
}
|
|
}
|
|
}
|
|
|
|
static Entity entityResult;
|
|
|
|
[ReadsImmediate(typeof(MockComponent))]
|
|
class GetEntityFromImmediateReadComponents : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var (_, entity) = ReadComponentIncludingEntity<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetEntityFromImmediateComponentID()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new AddAndRemoveMockComponentEngine());
|
|
worldBuilder.AddEngine(new GetEntityFromImmediateReadComponents());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.DoesNotThrow(() => world.Update(0.01));
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class DelayedMessageEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
SendMessage(new MockMessage { }, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EngineSendMessageDelayed()
|
|
{
|
|
resultMessages.Clear();
|
|
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
|
|
worldBuilder.AddEngine(new DelayedMessageEngine());
|
|
worldBuilder.AddEngine(new MessageReadEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
resultMessages.Should().BeEmpty();
|
|
|
|
world.Update(0.5);
|
|
|
|
resultMessages.Should().BeEmpty();
|
|
|
|
world.Update(0.5);
|
|
|
|
resultMessages.Should().BeEmpty();
|
|
|
|
world.Update(2);
|
|
|
|
resultMessages.Should().NotBeEmpty();
|
|
resultMessages.First().Should().BeOfType<MockMessage>();
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class DelayedMessageIgnoringTimeDilationEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
SendMessageIgnoringTimeDilation(new MockMessage { }, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EngineSendMessageDelayedIgnoringTimeDilation()
|
|
{
|
|
resultMessages.Clear();
|
|
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
|
|
worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine());
|
|
worldBuilder.AddEngine(new MessageReadEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
resultMessages.Should().BeEmpty();
|
|
|
|
world.Update(0.5);
|
|
|
|
resultMessages.Should().BeEmpty();
|
|
|
|
world.Update(0.5);
|
|
|
|
resultMessages.Should().NotBeEmpty();
|
|
resultMessages.First().Should().BeOfType<MockMessage>();
|
|
}
|
|
|
|
[Receives(typeof(MockMessage))]
|
|
[WritesImmediate(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent), 1)]
|
|
class ActivateComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var message in ReadMessages<MockMessage>())
|
|
{
|
|
var entity = CreateEntity();
|
|
SetComponent(entity, new MockComponent { });
|
|
}
|
|
}
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent), 0)]
|
|
class RemoveComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EngineAddAndRemoveComponentSameFrameWithRemovePriority()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ActivateComponentEngine());
|
|
worldBuilder.AddEngine(new RemoveComponentEngine());
|
|
worldBuilder.AddEngine(new ReadComponentsTestEngine());
|
|
|
|
worldBuilder.SendMessage(new MockMessage { });
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.DoesNotThrow(() => world.Update(0.01));
|
|
world.Update(0.01); // update again for the read
|
|
resultComponents.Should().BeEmpty();
|
|
}
|
|
|
|
struct DestroyComponentMessage : IMessage { public Entity entity; }
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class AddComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponent>())
|
|
{
|
|
SetComponent(entity, new MockComponent { });
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
struct MockComponentB : IComponent
|
|
{
|
|
public MockComponentB(int value)
|
|
{
|
|
this.value = value;
|
|
}
|
|
|
|
int value;
|
|
}
|
|
|
|
static MockComponentB getComponentResult;
|
|
|
|
[Reads(typeof(MockComponent), typeof(MockComponentB))]
|
|
class GetComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
getComponentResult = GetComponent<MockComponentB>(ReadEntity<MockComponent>());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
worldBuilder.SetComponent(entity, new MockComponentB(3));
|
|
worldBuilder.AddEngine(new GetComponentEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
getComponentResult.Should().BeEquivalentTo(new MockComponentB(3));
|
|
}
|
|
|
|
[Reads(typeof(MockComponent), typeof(MockComponentB))]
|
|
class GetComponentExceptionEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponent>())
|
|
{
|
|
GetComponent<MockComponentB>(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetComponentWhenComponentIsNotOnEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new GetComponentExceptionEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
Assert.Throws<Encompass.Exceptions.NoComponentOfTypeOnEntityException>(() => world.Update(0.01));
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
class RemoveComponentByTypeEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (_, entity) in ReadComponentsIncludingEntity<MockComponent>())
|
|
{
|
|
RemoveComponent<MockComponent>(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void RemoveComponentByType()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentsTestEngine());
|
|
worldBuilder.AddEngine(new RemoveComponentByTypeEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
|
|
|
var entityB = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent { });
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
resultComponents.Should().BeEmpty();
|
|
}
|
|
|
|
static double dilatedDeltaTime;
|
|
|
|
class ActivateTimeDilationEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
if (!TimeDilationActive)
|
|
{
|
|
ActivateTimeDilation(0.2, 1, 1, 1);
|
|
}
|
|
else
|
|
{
|
|
dilatedDeltaTime = dt;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ActivateTimeDilation()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01); // activate time dilation
|
|
|
|
world.Update(0.5);
|
|
|
|
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
|
|
|
|
world.Update(0.5);
|
|
|
|
dilatedDeltaTime.Should().BeApproximately(0.1, 0.01);
|
|
|
|
world.Update(1);
|
|
|
|
world.Update(0.5);
|
|
|
|
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
|
|
}
|
|
|
|
class ActivateTimeDilationLowerFactorEngine : Engine
|
|
{
|
|
private bool activated = false;
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
if (!activated)
|
|
{
|
|
ActivateTimeDilation(0.2, 1, 1, 1);
|
|
activated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
class ActivateTimeDilationHigherFactorEngine : Engine
|
|
{
|
|
private bool activated = false;
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
if (!activated)
|
|
{
|
|
ActivateTimeDilation(0.5, 1, 1, 1);
|
|
activated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool timeDilationActive;
|
|
class ReadDilatedDeltaTimeEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
dilatedDeltaTime = dt;
|
|
timeDilationActive = TimeDilationActive;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MultipleActivateTimeDilation()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine());
|
|
worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine());
|
|
worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01); // activate time dilation
|
|
|
|
world.Update(0.5); // 0.3 and 0.375
|
|
|
|
dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01);
|
|
timeDilationActive.Should().BeTrue();
|
|
|
|
world.Update(5);
|
|
|
|
dilatedDeltaTime.Should().BeApproximately(5, 0.01);
|
|
timeDilationActive.Should().BeFalse();
|
|
}
|
|
|
|
static double undilatedDeltaTime;
|
|
|
|
[IgnoresTimeDilation]
|
|
class IgnoresTimeDilationEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
undilatedDeltaTime = dt;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void IgnoresTimeDilation()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
|
|
worldBuilder.AddEngine(new IgnoresTimeDilationEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01); // activate time dilation
|
|
|
|
world.Update(0.5);
|
|
|
|
undilatedDeltaTime.Should().Be(0.5);
|
|
}
|
|
|
|
class AddComponentWithoutPriorityEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var entity = CreateEntity();
|
|
AddComponent(entity, new MockComponent());
|
|
|
|
var entityB = CreateEntity();
|
|
AddComponent(entityB, new MockComponent());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void AddComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
worldBuilder.AddEngine(new AddComponentWithoutPriorityEngine());
|
|
worldBuilder.AddEngine(new ReadComponentsTestEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
resultComponents.Should().HaveCount(2);
|
|
|
|
world.Update(0.01);
|
|
|
|
resultComponents.Should().HaveCount(4);
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class AddComponentToPreviouslyExistingEntityEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
|
|
|
|
AddComponent(entity, new MockComponent());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void AddComponentToPreviouslyExistingEntityTest()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new AddComponentToPreviouslyExistingEntityEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.Throws<IllegalWriteException>(() => world.Update(0.01));
|
|
}
|
|
|
|
[WritesImmediate(typeof(MockComponentB))]
|
|
class AddImmediateComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var entity = CreateEntity();
|
|
AddComponent(entity, new MockComponentB(5));
|
|
}
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
class ReadImmediateComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var (component, entity) = ReadComponentIncludingEntity<MockComponentB>();
|
|
|
|
getComponentResult = component;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void AddImmediateComponentTest()
|
|
{
|
|
getComponentResult = default(MockComponentB);
|
|
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
worldBuilder.AddEngine(new AddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new ReadImmediateComponentEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
getComponentResult.Should().Be(new MockComponentB(5));
|
|
}
|
|
|
|
static bool entityExistsResult;
|
|
|
|
class EntityExistsEngine : Engine
|
|
{
|
|
private int _id;
|
|
|
|
public EntityExistsEngine(int id)
|
|
{
|
|
_id = id;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entityExistsResult = EntityExists(_id);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PruneEmptyEntities()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
var entity = worldBuilder.CreateEntity();
|
|
var id = entity.ID;
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
entityExistsResult.Should().BeFalse();
|
|
}
|
|
|
|
public class QueryTests
|
|
{
|
|
struct MockComponentB : IComponent { }
|
|
struct MockComponentC : IComponent { }
|
|
struct MockComponentD : IComponent { }
|
|
|
|
[Reads(typeof(MockComponent), typeof(MockComponentB))]
|
|
[Writes(typeof(MockComponentB))]
|
|
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
|
|
class EntityQueryWithComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
foreach (var entity in TrackedEntities)
|
|
{
|
|
entities.Add(entity);
|
|
RemoveComponent<MockComponentB>(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
worldBuilder.SetComponent(entity, new MockComponentB());
|
|
|
|
worldBuilder.SetComponent(entityB, new MockComponent());
|
|
worldBuilder.SetComponent(entityB, new MockComponentB());
|
|
|
|
worldBuilder.SetComponent(entityC, new MockComponentB());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB });
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.Should().BeEmpty();
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Writes(typeof(MockComponent))]
|
|
[QueryWithout(typeof(MockComponent))]
|
|
class EntityQueryWithoutComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithoutComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
foreach (var entity in TrackedEntities)
|
|
{
|
|
entities.Add(entity);
|
|
SetComponent(entity, new MockComponent());
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithoutComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
worldBuilder.SetComponent(entity, new MockComponentB());
|
|
|
|
worldBuilder.SetComponent(entityB, new MockComponent());
|
|
worldBuilder.SetComponent(entityB, new MockComponentB());
|
|
|
|
worldBuilder.SetComponent(entityC, new MockComponentB());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC });
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.Should().BeEmpty();
|
|
}
|
|
|
|
[Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
|
|
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
|
|
[QueryWithout(typeof(MockComponentD))]
|
|
class EntityQueryWithandWithoutComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithandWithoutComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
|
|
entities.AddRange(TrackedEntities);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithAndWithoutComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
var entityD = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
worldBuilder.SetComponent(entity, new MockComponentB());
|
|
worldBuilder.SetComponent(entity, new MockComponentD());
|
|
|
|
worldBuilder.SetComponent(entityB, new MockComponent());
|
|
|
|
worldBuilder.SetComponent(entityC, new MockComponent());
|
|
worldBuilder.SetComponent(entityC, new MockComponentB());
|
|
worldBuilder.SetComponent(entityC, new MockComponentC());
|
|
worldBuilder.SetComponent(entityC, new MockComponentD());
|
|
|
|
worldBuilder.SetComponent(entityD, new MockComponent());
|
|
worldBuilder.SetComponent(entityD, new MockComponentB());
|
|
worldBuilder.SetComponent(entityD, new MockComponentC());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD });
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[WritesImmediate(typeof(MockComponentB))]
|
|
[Writes(typeof(MockComponentB), 0)]
|
|
class AddImmediateComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponent>())
|
|
{
|
|
SetComponent(entity, new MockComponentB());
|
|
}
|
|
}
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
[QueryWith(typeof(MockComponentB))]
|
|
class EntityQueryWithImmediateComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithImmediateComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
entities.AddRange(TrackedEntities);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithImmediateComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new AddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new EntityQueryWithImmediateComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity });
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
[QueryWithout(typeof(MockComponentB))]
|
|
class EntityQueryWithoutImmediateComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithoutImmediateComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
entities.AddRange(TrackedEntities);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithoutImmediateComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new AddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new EntityQueryWithoutImmediateComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
|
|
}
|
|
|
|
[Reads(typeof(MockComponentC), typeof(MockComponentD))]
|
|
[WritesImmediate(typeof(MockComponent), typeof(MockComponentB))]
|
|
[Writes(typeof(MockComponent), 0)]
|
|
[Writes(typeof(MockComponentB), 0)]
|
|
class ConditionallyAddImmediateComponentsEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponentC>())
|
|
{
|
|
SetComponent(entity, new MockComponent());
|
|
}
|
|
|
|
foreach (var entity in ReadEntities<MockComponentD>())
|
|
{
|
|
SetComponent(entity, new MockComponent());
|
|
SetComponent(entity, new MockComponentB());
|
|
}
|
|
}
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponent), typeof(MockComponentB))]
|
|
[QueryWith(typeof(MockComponent))]
|
|
[QueryWithout(typeof(MockComponentB))]
|
|
class EntityQueryWithAndWithoutImmediateComponentsEngine : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithAndWithoutImmediateComponentsEngine(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
|
|
entities.AddRange(TrackedEntities);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithAndWithoutImmediateComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entityB, new MockComponentC());
|
|
worldBuilder.SetComponent(entityC, new MockComponentD());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new ConditionallyAddImmediateComponentsEngine());
|
|
worldBuilder.AddEngine(new EntityQueryWithAndWithoutImmediateComponentsEngine(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
|
|
}
|
|
|
|
[Reads(typeof(MockComponentC))]
|
|
[WritesImmediate(typeof(MockComponentB))]
|
|
[Writes(typeof(MockComponentB), 0)]
|
|
class ConditionallyAddImmediateComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var entity in ReadEntities<MockComponentC>())
|
|
{
|
|
SetComponent(entity, new MockComponentB());
|
|
}
|
|
}
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
[Reads(typeof(MockComponent))]
|
|
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
|
|
class EntityQueryWithImmediateAndNonImmediateComponents : Engine
|
|
{
|
|
private List<Entity> entities;
|
|
|
|
public EntityQueryWithImmediateAndNonImmediateComponents(List<Entity> entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
entities.Clear();
|
|
entities.AddRange(TrackedEntities);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntitiesWithImmediateAndNonImmediateComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
var entityC = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.SetComponent(entityB, new MockComponent());
|
|
worldBuilder.SetComponent(entityB, new MockComponentC());
|
|
worldBuilder.SetComponent(entityC, new MockComponentD());
|
|
|
|
var queriedEntities = new List<Entity>();
|
|
worldBuilder.AddEngine(new ConditionallyAddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new EntityQueryWithImmediateAndNonImmediateComponents(queriedEntities));
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
class ReadImmediateComponentsEngine : Engine
|
|
{
|
|
private List<MockComponentB> _components;
|
|
|
|
public ReadImmediateComponentsEngine(List<MockComponentB> components)
|
|
{
|
|
_components = components;
|
|
}
|
|
public override void Update(double dt)
|
|
{
|
|
_components.AddRange(ReadComponents<MockComponentB>());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadImmediateComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var _components = new List<MockComponentB>();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
worldBuilder.AddEngine(new AddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new ReadImmediateComponentsEngine(_components));
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01);
|
|
|
|
_components.Should().NotBeEmpty();
|
|
}
|
|
|
|
[ReadsImmediate(typeof(MockComponentB))]
|
|
[Reads(typeof(MockComponent))]
|
|
class HasAndGetImmediateComponentEngine : Engine
|
|
{
|
|
private List<MockComponentB> _components;
|
|
|
|
public HasAndGetImmediateComponentEngine(List<MockComponentB> components)
|
|
{
|
|
_components = components;
|
|
}
|
|
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
|
|
{
|
|
if (HasComponent<MockComponentB>(entity))
|
|
{
|
|
_components.Add(GetComponent<MockComponentB>(entity));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void HasAndGetImmediateComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var _components = new List<MockComponentB>();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
worldBuilder.AddEngine(new AddImmediateComponentEngine());
|
|
worldBuilder.AddEngine(new HasAndGetImmediateComponentEngine(_components));
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01);
|
|
|
|
_components.Should().NotBeEmpty();
|
|
}
|
|
|
|
struct MockTimerComponent : IComponent
|
|
{
|
|
public MockTimerComponent(double time)
|
|
{
|
|
Timer = time;
|
|
}
|
|
|
|
public double Timer { get; set; }
|
|
}
|
|
|
|
[Reads(typeof(MockTimerComponent))]
|
|
[Writes(typeof(MockTimerComponent))]
|
|
class ReadWhileRemovingComponentsEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockTimerComponent>())
|
|
{
|
|
var updatedComponent = component;
|
|
updatedComponent.Timer -= dt;
|
|
|
|
if (updatedComponent.Timer <= 0)
|
|
{
|
|
RemoveComponent<MockTimerComponent>(entity);
|
|
|
|
}
|
|
else
|
|
{
|
|
SetComponent<MockTimerComponent>(entity, updatedComponent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadWhileRemovingComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockTimerComponent(0.5));
|
|
|
|
var entityB = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entityB, new MockTimerComponent(0.4));
|
|
|
|
worldBuilder.AddEngine(new ReadWhileRemovingComponentsEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
Assert.DoesNotThrow(() => world.Update(0.2));
|
|
Assert.DoesNotThrow(() => world.Update(0.25));
|
|
}
|
|
|
|
[Test]
|
|
public void DestroyedEntitiesAreRemovedFromTracking()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.SetComponent(entity, new MockComponent());
|
|
|
|
worldBuilder.AddEngine(new DestroyWithEngine());
|
|
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
readEntities.Should().BeEmpty();
|
|
}
|
|
}
|
|
}
|
|
}
|