Show More
Commit Description:
Add missing component and message.
Commit Description:
Add missing component and message.
References:
File last commit:
Show/Diff file:
Action:
encompass-cs/test/EngineTest.cs
1976 lines | 62.6 KiB | text/x-csharp | CSharpLexer
1976 lines | 62.6 KiB | text/x-csharp | CSharpLexer
r169 | 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 class EngineTest | ||||
{ | ||||
static MockComponent resultComponent; | ||||
static MockComponent[] resultComponents = new MockComponent[1]; | ||||
static MockMessage resultMessage; | ||||
static MockMessage[] resultMessages = new MockMessage[1]; | ||||
[Reads(typeof(MockComponent))] | ||||
public class ReadComponentsTestEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
resultComponents = ReadComponents<MockComponent>().ToArray(); | ||||
} | ||||
} | ||||
static List<(MockComponent, Entity)> resultComponentsIncludingEntity = new List<(MockComponent, Entity)>(); | ||||
static (MockComponent, Entity) resultComponentIncludingEntity; | ||||
[Reads(typeof(MockComponent))] | ||||
public class ReadComponentsIncludingEntityEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
foreach (ref readonly var entity in ReadEntities<MockComponent>()) | ||||
{ | ||||
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity); | ||||
resultComponentsIncludingEntity.Add((mockComponent, entity)); | ||||
} | ||||
} | ||||
} | ||||
[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) | ||||
{ | ||||
ref readonly var entity = ref ReadEntity<MockComponent>(); | ||||
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity); | ||||
resultComponentIncludingEntity = (mockComponent, entity); | ||||
} | ||||
} | ||||
[Test] | ||||
public void ReadComponents() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadComponentsTestEngine()); | ||||
var entity = worldBuilder.CreateEntity(); | ||||
var entityB = worldBuilder.CreateEntity(); | ||||
MockComponent mockComponent; | ||||
mockComponent.myInt = 2; | ||||
MockComponent mockComponentB; | ||||
mockComponentB.myInt = 1; | ||||
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() | ||||
{ | ||||
resultComponentsIncludingEntity.Clear(); | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine()); | ||||
var entity = worldBuilder.CreateEntity(); | ||||
var entityB = worldBuilder.CreateEntity(); | ||||
MockComponent mockComponent; | ||||
mockComponent.myInt = 2; | ||||
MockComponent mockComponentB; | ||||
mockComponentB.myInt = 1; | ||||
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 = 3; | ||||
worldBuilder.SetComponent(entity, mockComponent); | ||||
var world = worldBuilder.Build(); | ||||
world.Update(0.01f); | ||||
Assert.AreEqual(mockComponent, resultComponent); | ||||
} | ||||
[Test] | ||||
public void ReadComponentThrowsWhenNoneExist() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadComponentTestEngine()); | ||||
var world = worldBuilder.Build(); | ||||
Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01f), "No component of type MockComponent exists"); | ||||
} | ||||
[Test] | ||||
public void ReadComponentWhenMultipleComponents() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadComponentTestEngine()); | ||||
var entity = worldBuilder.CreateEntity(); | ||||
var entityB = worldBuilder.CreateEntity(); | ||||
MockComponent mockComponent; | ||||
mockComponent.myInt = 2; | ||||
MockComponent mockComponentB; | ||||
mockComponentB.myInt = 1; | ||||
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 = 2; | ||||
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) | ||||
{ | ||||
ref readonly var entity = ref ReadEntity<MockComponent>(); | ||||
SetComponent(entity, new MockComponent { myInt = 420 }); | ||||
} | ||||
} | ||||
// 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 = 3; | ||||
worldBuilder.SetComponent(entity, mockComponent); | ||||
var world = worldBuilder.Build(); | ||||
world.Update(0.01); | ||||
world.Update(0.01); | ||||
Assert.AreEqual(420, resultComponent.myInt); | ||||
} | ||||
[Reads(typeof(MockComponent))] | ||||
public class UndeclaredUpdateComponentTestEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
ref readonly var entity = ref ReadEntity<MockComponent>(); | ||||
SetComponent(entity, new MockComponent { myInt = 420 }); | ||||
} | ||||
} | ||||
[Test] | ||||
public void UpdateUndeclaredComponent() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new UndeclaredUpdateComponentTestEngine()); | ||||
var entity = worldBuilder.CreateEntity(); | ||||
MockComponent mockComponent; | ||||
mockComponent.myInt = 3; | ||||
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 ReadMessagesEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
resultMessages = ReadMessages<MockMessage>().ToArray(); | ||||
} | ||||
} | ||||
[Receives(typeof(MockMessage))] | ||||
public class ReadMessageEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
resultMessage = ReadMessage<MockMessage>(); | ||||
} | ||||
} | ||||
[Test] | ||||
public void EmitAndReadMessage() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new MessageEmitEngine()); | ||||
worldBuilder.AddEngine(new ReadMessageEngine()); | ||||
var world = worldBuilder.Build(); | ||||
world.Update(0.01f); | ||||
Assert.AreEqual(resultMessage.myString, "howdy"); | ||||
} | ||||
[Test] | ||||
public void ReadMessageThrowsWhenNoneOfTypeExist() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadMessageEngine()); | ||||
var world = worldBuilder.Build(); | ||||
Assert.Throws<NoMessageOfTypeException>(() => world.Update(0.01), "No Message of type MockMessage exists"); | ||||
} | ||||
public class UndeclaredMessageEmitEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
MockMessage message; | ||||
message.myString = "howdy"; | ||||
this.SendMessage(message); | ||||
} | ||||
} | ||||
static MockMessage[] emptyReadMessagesResult; | ||||
[Receives(typeof(MockMessage))] | ||||
class ReadMessagesWhenNoneExistEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
emptyReadMessagesResult = ReadMessages<MockMessage>().ToArray(); | ||||
} | ||||
} | ||||
[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 entities = ReadEntities<MockComponent>(); | ||||
pairA = (GetComponent<MockComponent>(entities[0]), entities[0]); | ||||
pairB = (GetComponent<MockComponent>(entities[1]), entities[1]); | ||||
} | ||||
} | ||||
// 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; | ||||
MockComponent componentB; | ||||
componentB.myInt = 20; | ||||
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)); | ||||
} | ||||
[Reads(typeof(MockComponent))] | ||||
class ReadEmptyMockComponentsEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
ReadEntities<MockComponent>().ToArray().Should().BeEmpty(); | ||||
} | ||||
} | ||||
[Test] | ||||
public void ReadComponentsOfTypeWhereNoneExist() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadEmptyMockComponentsEngine()); | ||||
var world = worldBuilder.Build(); | ||||
world.Update(0.01f); | ||||
} | ||||
struct DestroyerComponent : IComponent { } | ||||
[Reads(typeof(DestroyerComponent))] | ||||
class DestroyerEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
foreach (ref readonly var entity in ReadEntities<DestroyerComponent>()) | ||||
{ | ||||
Destroy(entity); | ||||
} | ||||
} | ||||
} | ||||
static List<(MockComponent, Entity)> results = new List<(MockComponent, Entity)>(); | ||||
[Reads(typeof(MockComponent))] | ||||
class ReaderEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
results.Clear(); | ||||
foreach (ref readonly var entity in ReadEntities<MockComponent>()) | ||||
{ | ||||
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity); | ||||
results.Add((mockComponent, entity)); | ||||
} | ||||
} | ||||
} | ||||
[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; | ||||
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 (ref readonly var message in ReadMessages<DestroyComponentMessage>()) | ||||
{ | ||||
Destroy(message.entity); | ||||
} | ||||
} | ||||
} | ||||
[Test] | ||||
public void DestroyEntityWithoutID() | ||||
{ | ||||
results.Clear(); | ||||
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 (ref readonly var entity in ReadEntities<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 (ref readonly var entity in ReadEntities<MockComponent>()) | ||||
{ | ||||
RemoveComponent<MockComponent>(entity); | ||||
SetComponent(entity, new MockComponent()); | ||||
} | ||||
} | ||||
} | ||||
static Entity entityResult; | ||||
[ReadsImmediate(typeof(MockComponent))] | ||||
class GetEntityFromImmediateReadComponents : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
ref readonly var entity = ref ReadEntity<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 (ref readonly var entity in ReadEntities<MockComponent>()) | ||||
{ | ||||
RemoveComponent<MockComponent>(entity); | ||||
SendMessage(new MockMessage { }, 1); | ||||
} | ||||
} | ||||
} | ||||
[Test] | ||||
public void EngineSendMessageDelayed() | ||||
{ | ||||
Array.Clear(resultMessages, 0, resultMessages.Length); | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ActivateTimeDilationEngine()); | ||||
worldBuilder.AddEngine(new DelayedMessageEngine()); | ||||
worldBuilder.AddEngine(new ReadMessagesEngine()); | ||||
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 (ref readonly var entity in ReadEntities<MockComponent>()) | ||||
{ | ||||
RemoveComponent<MockComponent>(entity); | ||||
SendMessageIgnoringTimeDilation(new MockMessage { }, 1); | ||||
} | ||||
} | ||||
} | ||||
[Test] | ||||
public void EngineSendMessageDelayedIgnoringTimeDilation() | ||||
{ | ||||
Array.Clear(resultMessages, 0, resultMessages.Length); | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ActivateTimeDilationEngine()); | ||||
worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine()); | ||||
worldBuilder.AddEngine(new ReadMessagesEngine()); | ||||
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 (ref readonly 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 ReadEntity() | ||||
{ | ||||
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); | ||||
} | ||||
[Test] | ||||
public void ReadEntityThrowsWhenNoComponentOfTypeExists() | ||||
{ | ||||
var worldBuilder = new WorldBuilder(); | ||||
worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine()); | ||||
var world = worldBuilder.Build(); | ||||
Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01), "No component of type MockComponent exists"); | ||||
} | ||||
struct MockComponentB : IComponent | ||||
{ | ||||
private int value; | ||||
public MockComponentB(int value) | ||||
{ | ||||
this.value = 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 (ref readonly var entity in ReadEntities<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) | ||||
{ | ||||
ref readonly var entity = ref ReadEntity<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) | ||||
{ | ||||
ref readonly var component = ref ReadComponent<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 { } | ||||
[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(); | ||||
} | ||||
[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(); | ||||
} | ||||
[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))] | ||||
[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>().ToArray()); | ||||
} | ||||
} | ||||
[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 (ref readonly var entity in ReadEntities<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 double Timer { get; } | ||||
public MockTimerComponent(double time) | ||||
{ | ||||
Timer = time; | ||||
} | ||||
} | ||||
[Reads(typeof(MockTimerComponent))] | ||||
[Writes(typeof(MockTimerComponent))] | ||||
class ReadWhileRemovingComponentsEngine : Engine | ||||
{ | ||||
public override void Update(double dt) | ||||
{ | ||||
foreach (ref readonly var entity in ReadEntities<MockTimerComponent>()) | ||||
{ | ||||
ref readonly var component = ref GetComponent<MockTimerComponent>(entity); | ||||
if (component.Timer - dt <= 0) | ||||
{ | ||||
RemoveComponent<MockTimerComponent>(entity); | ||||
} | ||||
else | ||||
{ | ||||
SetComponent<MockTimerComponent>(entity, new MockTimerComponent(component.Timer - dt)); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
[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(); | ||||
} | ||||
} | ||||
} | ||||
} | ||||