diff --git a/SDK/include/NDK/BaseSystem.hpp b/SDK/include/NDK/BaseSystem.hpp index a323f2952..99dcbf768 100644 --- a/SDK/include/NDK/BaseSystem.hpp +++ b/SDK/include/NDK/BaseSystem.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace Ndk @@ -33,7 +34,7 @@ namespace Ndk bool Filters(const Entity* entity) const; - inline const std::vector& GetEntities() const; + inline const EntityList& GetEntities() const; inline SystemIndex GetIndex() const; inline int GetUpdateOrder() const; inline float GetUpdateRate() const; @@ -84,12 +85,11 @@ namespace Ndk static inline bool Initialize(); static inline void Uninitialize(); - std::vector m_entities; - Nz::Bitset m_entityBits; Nz::Bitset<> m_excludedComponents; mutable Nz::Bitset<> m_filterResult; Nz::Bitset<> m_requiredAnyComponents; Nz::Bitset<> m_requiredComponents; + EntityList m_entities; SystemIndex m_systemIndex; World* m_world; bool m_updateEnabled; diff --git a/SDK/include/NDK/BaseSystem.inl b/SDK/include/NDK/BaseSystem.inl index e949d254b..5969dd8e3 100644 --- a/SDK/include/NDK/BaseSystem.inl +++ b/SDK/include/NDK/BaseSystem.inl @@ -56,7 +56,7 @@ namespace Ndk * \return A constant reference to the list of entities */ - inline const std::vector& BaseSystem::GetEntities() const + inline const EntityList& BaseSystem::GetEntities() const { return m_entities; } @@ -121,10 +121,7 @@ namespace Ndk inline bool BaseSystem::HasEntity(const Entity* entity) const { - if (!entity) - return false; - - return m_entityBits.UnboundedTest(entity->GetId()); + return m_entities.Has(entity); } /*! @@ -288,9 +285,7 @@ namespace Ndk { NazaraAssert(entity, "Invalid entity"); - m_entities.emplace_back(entity); - m_entityBits.UnboundedSet(entity->GetId(), true); - + m_entities.Insert(entity); entity->RegisterSystem(m_systemIndex); OnEntityAdded(entity); @@ -308,14 +303,7 @@ namespace Ndk { NazaraAssert(entity, "Invalid entity"); - auto it = std::find(m_entities.begin(), m_entities.end(), *entity); - NazaraAssert(it != m_entities.end(), "Entity is not part of this system"); - - // To avoid moving a lot of handles, we swap and pop - std::swap(*it, m_entities.back()); - m_entities.pop_back(); // We get it out of the vector - - m_entityBits.Reset(entity->GetId()); + m_entities.Remove(entity); entity->UnregisterSystem(m_systemIndex); OnEntityRemoved(entity); // And we alert our callback @@ -360,7 +348,7 @@ namespace Ndk } /*! - * \brief Uninitializes the BaseSystem + * \brief Uninitialize the BaseSystem */ inline void BaseSystem::Uninitialize() diff --git a/SDK/include/NDK/EntityList.hpp b/SDK/include/NDK/EntityList.hpp index 22a632e65..76097f87b 100644 --- a/SDK/include/NDK/EntityList.hpp +++ b/SDK/include/NDK/EntityList.hpp @@ -21,6 +21,8 @@ namespace Ndk using size_type = std::size_t; inline EntityList(); + inline EntityList(const EntityList& entityList); + inline EntityList(EntityList&& entityList) noexcept; ~EntityList() = default; inline void Clear(); @@ -33,11 +35,14 @@ namespace Ndk inline void Remove(Entity* entity); // STL API - inline iterator begin(); + inline iterator begin() const; inline bool empty() const; - inline iterator end(); + inline iterator end() const; inline size_type size() const; + inline EntityList& operator=(const EntityList& entityList); + inline EntityList& operator=(EntityList&& entityList) noexcept; + private: inline std::size_t FindNext(std::size_t currentId) const; inline World* GetWorld() const; @@ -46,7 +51,7 @@ namespace Ndk World* m_world; }; - class EntityList::iterator : public std::iterator + class NDK_API EntityList::iterator : public std::iterator { friend EntityList; diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 21bf51bdb..f5a751323 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -16,13 +16,32 @@ namespace Ndk */ /*! - * \brief Clears the set from every entities + * \brief Construct a new entity list */ inline EntityList::EntityList() : m_world(nullptr) { } + /*! + * \brief Construct a new entity list by copying another one + */ + inline EntityList::EntityList(const EntityList& entityList) : + m_entityBits(entityList.m_entityBits), + m_world(entityList.m_world) + { + } + + /*! + * \brief Construct a new entity list by moving a list into this one + */ + inline EntityList::EntityList(EntityList&& entityList) noexcept : + m_entityBits(std::move(entityList.m_entityBits)), + m_world(entityList.m_world) + { + } + + /*! * \brief Clears the set from every entities * @@ -96,17 +115,17 @@ namespace Ndk } // STL Interface - inline EntityList::iterator EntityList::begin() + inline EntityList::iterator EntityList::begin() const { return EntityList::iterator(this, m_entityBits.FindFirst()); } inline bool EntityList::empty() const { - return m_entityBits.TestAny(); + return !m_entityBits.TestAny(); } - inline EntityList::iterator EntityList::end() + inline EntityList::iterator EntityList::end() const { return EntityList::iterator(this, m_entityBits.npos); } @@ -116,6 +135,22 @@ namespace Ndk return m_entityBits.Count(); } + inline EntityList& EntityList::operator=(const EntityList& entityList) + { + m_entityBits = entityList.m_entityBits; + m_world = entityList.m_world; + + return *this; + } + + inline EntityList& EntityList::operator=(EntityList && entityList) noexcept + { + m_entityBits = std::move(entityList.m_entityBits); + m_world = entityList.m_world; + + return *this; + } + inline std::size_t EntityList::FindNext(std::size_t currentId) const { return m_entityBits.FindNext(currentId); diff --git a/SDK/include/NDK/StateMachine.hpp b/SDK/include/NDK/StateMachine.hpp index dae81ecd0..e9e665c44 100644 --- a/SDK/include/NDK/StateMachine.hpp +++ b/SDK/include/NDK/StateMachine.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Jérôme Leclercq +// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Ndk { @@ -25,14 +26,21 @@ namespace Ndk inline const std::shared_ptr& GetCurrentState() const; + inline bool IsTopState(const State* state) const; + + inline std::shared_ptr PopState(); + inline bool PopStatesUntil(std::shared_ptr state); + inline void PushState(std::shared_ptr state); + + inline void SetState(std::shared_ptr state); + inline bool Update(float elapsedTime); inline StateMachine& operator=(StateMachine&& fsm) = default; StateMachine& operator=(const StateMachine&) = delete; private: - std::shared_ptr m_currentState; - std::shared_ptr m_nextState; + std::vector> m_states; }; } diff --git a/SDK/include/NDK/StateMachine.inl b/SDK/include/NDK/StateMachine.inl index d30146a97..1086df54d 100644 --- a/SDK/include/NDK/StateMachine.inl +++ b/SDK/include/NDK/StateMachine.inl @@ -3,7 +3,6 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include -#include #include namespace Ndk @@ -11,7 +10,7 @@ namespace Ndk /*! * \ingroup NDK * \class Ndk::StateMachine - * \brief NDK class that represents a state machine, to represent the multiple states of your program + * \brief NDK class that represents a state machine, to represent the multiple states of your program as a stack */ /*! @@ -23,61 +22,153 @@ namespace Ndk * \remark Produces a NazaraAssert if nullptr is given */ - inline StateMachine::StateMachine(std::shared_ptr originalState) : - m_currentState(std::move(originalState)) + inline StateMachine::StateMachine(std::shared_ptr originalState) { - NazaraAssert(m_currentState, "StateMachine must have a state to begin with"); - m_currentState->Enter(*this); + NazaraAssert(originalState, "StateMachine must have a state to begin with"); + PushState(std::move(originalState)); } /*! * \brief Destructs the object * - * \remark Calls "Leave" on the state + * \remark Calls "Leave" on all the states */ inline StateMachine::~StateMachine() { - m_currentState->Leave(*this); + for (std::shared_ptr& state : m_states) + state->Leave(*this); } /*! - * \brief Changes the current state of the machine + * \brief Replaces the current state on the top of the machine * - * \param state Next state to represent + * \param state State to replace the top one if it is nullptr, no action is performed */ inline void StateMachine::ChangeState(std::shared_ptr state) { - m_nextState = std::move(state); + if (state) + { + PopState(); + PushState(std::move(state)); + } } /*! - * \brief Gets the current state of the machine + * \brief Gets the current state on the top of the machine * \return A constant reference to the state + * + * \remark The stack is supposed to be non empty, otherwise it is undefined behaviour + * + * \see PopStatesUntil */ inline const std::shared_ptr& StateMachine::GetCurrentState() const { - return m_currentState; + return m_states.back(); } /*! - * \brief Updates the state - * \return True if update is successful + * \brief Checks whether the state is on the top of the machine + * \return true If it is the case + * + * \param state State to compare the top with + */ + + inline bool StateMachine::IsTopState(const State* state) const + { + if (m_states.empty()) + return false; + + return m_states.back().get() == state; + } + + /*! + * \brief Pops the state on the top of the machine + * \return Old state on the top, nullptr if stack was empty + * + * \remark This method can completely empty the stack + */ + + inline std::shared_ptr StateMachine::PopState() + { + if (m_states.empty()) + return nullptr; + + m_states.back()->Leave(*this); + std::shared_ptr oldTopState = std::move(m_states.back()); + m_states.pop_back(); + return oldTopState; + } + + /*! + * \brief Pops all the states of the machine until a specific one is reached + * \return true If that specific state is on top, false if stack is empty + * + * \param state State to find on the stack if it is nullptr, no action is performed + * + * \remark This method can completely empty the stack + */ + + inline bool StateMachine::PopStatesUntil(std::shared_ptr state) + { + if (!state) + return false; + + while (!m_states.empty() && !IsTopState(state.get())) + PopState(); + + return !m_states.empty(); + } + + /*! + * \brief Pushes a new state on the top of the machine + * + * \param state Next state to represent if it is nullptr, it performs no action + * + * \remark Produces a NazaraAssert if the same state is pushed two times on the stack + */ + + inline void StateMachine::PushState(std::shared_ptr state) + { + if (state) + { + NazaraAssert(std::find(m_states.begin(), m_states.end(), state) == m_states.end(), "The same state was pushed two times"); + + m_states.push_back(std::move(state)); + m_states.back()->Enter(*this); + } + } + + /*! + * \brief Pops every states of the machine to put a new one + * + * \param state State to reset the stack with if it is nullptr, no action is performed + */ + + inline void StateMachine::SetState(std::shared_ptr state) + { + if (state) + { + while (!m_states.empty()) + PopState(); + + PushState(std::move(state)); + } + } + + /*! + * \brief Updates all the states + * \return true If update is successful for everyone of them * * \param elapsedTime Delta time used for the update */ inline bool StateMachine::Update(float elapsedTime) { - if (m_nextState) - { - m_currentState->Leave(*this); - m_currentState = std::move(m_nextState); - m_currentState->Enter(*this); - } - - return m_currentState->Update(*this, elapsedTime); + return std::all_of(m_states.begin(), m_states.end(), [=](std::shared_ptr& state) { + return state->Update(*this, elapsedTime); + }); } } diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index b1f1abde1..29b9a4a9e 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -47,7 +47,7 @@ namespace Ndk void Clear() noexcept; const EntityHandle& CloneEntity(EntityId id); - const EntityHandle& GetEntity(EntityId id); + inline const EntityHandle& GetEntity(EntityId id); inline const EntityList& GetEntities() const; inline BaseSystem& GetSystem(SystemIndex index); template SystemType& GetSystem(); @@ -55,7 +55,7 @@ namespace Ndk inline bool HasSystem(SystemIndex index) const; template bool HasSystem() const; - void KillEntity(Entity* entity); + inline void KillEntity(Entity* entity); inline void KillEntities(const EntityVector& list); inline bool IsEntityValid(const Entity* entity) const; diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 45d358ad4..d45f0ff1d 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -164,18 +164,53 @@ namespace Ndk return HasSystem(index); } + /*! + * \brief Marks an entity for deletion + * + * \param Pointer to the entity + * + * \remark If the entity pointer is invalid, nothing is done + * \remark For safety, entities are not killed until the next world update + */ + inline void World::KillEntity(Entity* entity) + { + if (IsEntityValid(entity)) + m_killedEntities.UnboundedSet(entity->GetId(), true); + } + /*! * \brief Kills a set of entities * + * This function has the same effect as calling KillEntity for every entity contained in the vector + * * \param list Set of entities to kill */ - inline void World::KillEntities(const EntityVector& list) { for (const EntityHandle& entity : list) KillEntity(entity); } + /*! + * \brief Gets an entity + * \return A constant reference to a handle of the entity + * + * \param id Identifier of the entity + * + * \remark Handle referenced by this function can move in memory when updating the world, do not keep a reference to a handle from a world update to another + * \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned + */ + inline const EntityHandle& World::GetEntity(EntityId id) + { + if (IsEntityIdValid(id)) + return m_entityBlocks[id]->handle; + else + { + NazaraError("Invalid ID"); + return EntityHandle::InvalidHandle; + } + } + /*! * \brief Checks whether or not an entity is valid * \return true If it is the case @@ -266,6 +301,7 @@ namespace Ndk { m_aliveEntities = std::move(world.m_aliveEntities); m_dirtyEntities = std::move(world.m_dirtyEntities); + m_entityBlocks = std::move(world.m_entityBlocks); m_freeIdList = std::move(world.m_freeIdList); m_killedEntities = std::move(world.m_killedEntities); m_orderedSystems = std::move(world.m_orderedSystems); @@ -285,7 +321,7 @@ namespace Ndk inline void World::Invalidate() { - m_dirtyEntities.Resize(m_entities.size(), false); + m_dirtyEntities.Resize(m_entityBlocks.size(), false); m_dirtyEntities.Set(true); // Activation of all bits } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 7cdc87d07..7419ac627 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -24,6 +24,7 @@ namespace Ndk HandledObject(std::move(entity)), m_components(std::move(entity.m_components)), m_componentBits(std::move(entity.m_componentBits)), + m_removedComponentBits(std::move(entity.m_removedComponentBits)), m_systemBits(std::move(entity.m_systemBits)), m_id(entity.m_id), m_world(entity.m_world), diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index db3cef529..88a23060d 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -68,12 +68,14 @@ namespace Ndk m_freeIdList.pop_back(); entBlock = &m_entities[id]; + entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed) + m_entityBlocks[id] = entBlock; } else { // We allocate a new entity - id = static_cast(m_entities.size()); + id = static_cast(m_entityBlocks.size()); if (m_entities.capacity() > m_entities.size()) { @@ -114,6 +116,7 @@ namespace Ndk // First, destruction of entities, then handles // This is made to avoid that handle warn uselessly entities before their destruction m_entities.clear(); + m_entityBlocks.clear(); m_aliveEntities.Clear(); m_dirtyEntities.Clear(); @@ -138,7 +141,7 @@ namespace Ndk return EntityHandle::InvalidHandle; } - EntityHandle clone = CreateEntity(); + const EntityHandle& clone = CreateEntity(); const Nz::Bitset<>& componentBits = original->GetComponentBits(); for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i)) @@ -150,40 +153,6 @@ namespace Ndk return GetEntity(clone->GetId()); } - /*! - * \brief Kills an entity - * - * \param Pointer to the entity - * - * \remark No change is done if entity is invalid - */ - - void World::KillEntity(Entity* entity) - { - if (IsEntityValid(entity)) - m_killedEntities.UnboundedSet(entity->GetId(), true); - } - - /*! - * \brief Gets an entity - * \return A constant reference to the modified entity - * - * \param id Identifier of the entity - * - * \remark Produces a NazaraError if entity identifier is not valid - */ - - const EntityHandle& World::GetEntity(EntityId id) - { - if (IsEntityIdValid(id)) - return m_entities[id].handle; - else - { - NazaraError("Invalid ID"); - return EntityHandle::InvalidHandle; - } - } - /*! * \brief Updates the world * @@ -205,6 +174,10 @@ namespace Ndk m_entities.push_back(std::move(*blockPtr)); m_waitingEntities.clear(); + + // Update entity blocks pointers + for (std::size_t i = 0; i < m_entities.size(); ++i) + m_entityBlocks[i] = &m_entities[i]; } // Handle killed entities before last call diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index f0ac6edda..218fd312d 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -73,7 +73,7 @@ namespace Nz std::vector billboards; }; - typedef std::map BatchedBillboardContainer; + using BatchedBillboardContainer = std::map; struct BatchedBillboardPipelineEntry { @@ -81,7 +81,7 @@ namespace Nz bool enabled = false; }; - typedef std::map BillboardPipelineBatches; + using BillboardPipelineBatches = std::map; /// Sprites struct SpriteChain_XYZ_Color_UV @@ -97,7 +97,7 @@ namespace Nz std::vector spriteChains; }; - typedef std::map SpriteOverlayBatches; + using SpriteOverlayBatches = std::map; struct BatchedBasicSpriteEntry { @@ -107,7 +107,7 @@ namespace Nz bool enabled = false; }; - typedef std::map SpriteMaterialBatches; + using SpriteMaterialBatches = std::map; struct BatchedSpritePipelineEntry { @@ -115,7 +115,7 @@ namespace Nz bool enabled = false; }; - typedef std::map SpritePipelineBatches; + using SpritePipelineBatches = std::map; /// Meshes struct MeshDataComparator @@ -132,7 +132,7 @@ namespace Nz Spheref squaredBoundingSphere; }; - typedef std::map MeshInstanceContainer; + using MeshInstanceContainer = std::map; struct BatchedModelEntry { @@ -142,7 +142,7 @@ namespace Nz bool enabled = false; }; - typedef std::map MeshMaterialBatches; + using MeshMaterialBatches = std::map; struct BatchedMaterialEntry { @@ -150,7 +150,7 @@ namespace Nz MeshMaterialBatches materialMap; }; - typedef std::map MeshPipelineBatches; + using MeshPipelineBatches = std::map; struct TransparentModelData { @@ -160,7 +160,7 @@ namespace Nz const Material* material; }; - typedef std::vector TransparentModelContainer; + using TransparentModelContainer = std::vector; struct Layer { diff --git a/include/Nazara/Physics2D/PhysWorld2D.hpp b/include/Nazara/Physics2D/PhysWorld2D.hpp index 4873e5642..30a3b1141 100644 --- a/include/Nazara/Physics2D/PhysWorld2D.hpp +++ b/include/Nazara/Physics2D/PhysWorld2D.hpp @@ -64,10 +64,10 @@ namespace Nz struct Callback { - ContactEndCallback endCallback; - ContactPreSolveCallback preSolveCallback; - ContactPostSolveCallback postSolveCallback; - ContactStartCallback startCallback; + ContactEndCallback endCallback = nullptr; + ContactPreSolveCallback preSolveCallback = nullptr; + ContactPostSolveCallback postSolveCallback = nullptr; + ContactStartCallback startCallback = nullptr; void* userdata; }; diff --git a/tests/SDK/NDK/BaseSystem.cpp b/tests/SDK/NDK/BaseSystem.cpp index a202c1f70..3bb0fa9e4 100644 --- a/tests/SDK/NDK/BaseSystem.cpp +++ b/tests/SDK/NDK/BaseSystem.cpp @@ -38,7 +38,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]") WHEN("We add an entity") { - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); entity->AddComponent(); THEN("System should have it") @@ -50,7 +50,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]") WHEN("We add an entity with excluded component") { - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); entity->AddComponent(); entity->AddComponent(); diff --git a/tests/SDK/NDK/Entity.cpp b/tests/SDK/NDK/Entity.cpp index cf02370ec..dd3216fb0 100644 --- a/tests/SDK/NDK/Entity.cpp +++ b/tests/SDK/NDK/Entity.cpp @@ -57,18 +57,18 @@ SCENARIO("Entity", "[NDK][ENTITY]") { Ndk::World world; Ndk::BaseSystem& system = world.AddSystem(); - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); WHEN("We add our UpdateComponent") { UpdatableComponent& updatableComponent = entity->AddComponent(); - REQUIRE(!updatableComponent.IsUpdated()); + CHECK(!updatableComponent.IsUpdated()); THEN("Update the world should update the entity's component") { world.Update(1.f); UpdatableComponent& updatableComponentGet = entity->GetComponent(); - REQUIRE(updatableComponentGet.IsUpdated()); + CHECK(updatableComponentGet.IsUpdated()); } THEN("Update the world should not update the entity's component if it's disabled") @@ -76,14 +76,14 @@ SCENARIO("Entity", "[NDK][ENTITY]") entity->Enable(false); world.Update(1.f); UpdatableComponent& updatableComponentGet = entity->GetComponent(); - REQUIRE(!updatableComponentGet.IsUpdated()); + CHECK(!updatableComponentGet.IsUpdated()); } THEN("We can remove its component") { entity->RemoveComponent(Ndk::GetComponentIndex()); world.Update(1.f); - REQUIRE(!entity->HasComponent()); + CHECK(!entity->HasComponent()); } } @@ -94,7 +94,7 @@ SCENARIO("Entity", "[NDK][ENTITY]") THEN("It's no more valid") { - REQUIRE(!world.IsEntityValid(entity)); + CHECK(!world.IsEntityValid(entity)); } } } diff --git a/tests/SDK/NDK/EntityOwner.cpp b/tests/SDK/NDK/EntityOwner.cpp index eb9a29edf..df5130c2a 100644 --- a/tests/SDK/NDK/EntityOwner.cpp +++ b/tests/SDK/NDK/EntityOwner.cpp @@ -7,7 +7,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") GIVEN("A world & an entity") { Ndk::World world; - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); WHEN("We set the ownership of the entity to our owner") { @@ -15,14 +15,14 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") THEN("Entity is still valid") { - REQUIRE(entity.IsValid()); + CHECK(entity.IsValid()); } THEN("Resetting or getting out of scope is no more valid") { entityOwner.Reset(); world.Update(1.f); - REQUIRE(!entity.IsValid()); + CHECK(!entity.IsValid()); } } } diff --git a/tests/SDK/NDK/StateMachine.cpp b/tests/SDK/NDK/StateMachine.cpp index 1c14b1e76..066a150d7 100644 --- a/tests/SDK/NDK/StateMachine.cpp +++ b/tests/SDK/NDK/StateMachine.cpp @@ -28,21 +28,79 @@ class TestState : public Ndk::State bool m_isUpdated; }; +class SecondTestState : public Ndk::State +{ + public: + void Enter(Ndk::StateMachine& /*fsm*/) override + { + m_isUpdated = false; + } + + bool IsUpdated() const + { + return m_isUpdated; + } + + void Leave(Ndk::StateMachine& /*fsm*/) override + { + } + + bool Update(Ndk::StateMachine& fsm, float /*elapsedTime*/) override + { + if (fsm.IsTopState(this)) + m_isUpdated = true; + return true; + } + + private: + bool m_isUpdated; +}; + SCENARIO("State & StateMachine", "[NDK][STATE]") { - GIVEN("A statemachine with our TestState") + GIVEN("A statemachine with our test states") { std::shared_ptr testState = std::make_shared(); - Ndk::StateMachine stateMachine(testState); + std::shared_ptr secondTestState = std::make_shared(); + Ndk::StateMachine stateMachine(secondTestState); + stateMachine.PushState(testState); REQUIRE(!testState->IsUpdated()); + REQUIRE(!secondTestState->IsUpdated()); WHEN("We update our machine") { stateMachine.Update(1.f); - THEN("Our state has been updated") + THEN("Our state on the top has been updated but not the bottom one") { + REQUIRE(stateMachine.IsTopState(testState.get())); + REQUIRE(!stateMachine.IsTopState(secondTestState.get())); + REQUIRE(testState->IsUpdated()); + REQUIRE(!secondTestState->IsUpdated()); + } + } + + WHEN("We exchange the states' positions while emptying the stack") + { + REQUIRE(stateMachine.PopStatesUntil(secondTestState)); + REQUIRE(stateMachine.IsTopState(secondTestState.get())); + + std::shared_ptr oldState = stateMachine.PopState(); + REQUIRE(stateMachine.PopState() == nullptr); + + stateMachine.SetState(testState); + stateMachine.PushState(oldState); + + stateMachine.Update(1.f); + + THEN("Both states should be updated") + { + REQUIRE(!stateMachine.IsTopState(testState.get())); + REQUIRE(stateMachine.IsTopState(secondTestState.get())); + + REQUIRE(testState->IsUpdated()); + REQUIRE(secondTestState->IsUpdated()); } } } diff --git a/tests/SDK/NDK/World.cpp b/tests/SDK/NDK/World.cpp index 85ee241b4..d46b6621b 100644 --- a/tests/SDK/NDK/World.cpp +++ b/tests/SDK/NDK/World.cpp @@ -79,7 +79,7 @@ SCENARIO("World", "[NDK][WORLD]") AND_WHEN("We update our world with our entity") { REQUIRE(&world.GetSystem(UpdateSystem::systemIndex) == &world.GetSystem()); - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); UpdatableComponent& component = entity->AddComponent(); THEN("Our entity component must be updated")