Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines
Starting to add additional timers for different stages of the process of
updating in order to get more insight into what is slowing it down.
The update takes 9ms, which is much longer than it used to.
Engine-specific timers are coming later.
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(); | ||||
} | ||||
} | ||||
} | ||||
} | ||||