diff --git a/CMakeLists.txt b/CMakeLists.txt index 749365c..e1d16d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ add_library(ecs_engine STATIC src/engine/EngineStepContext.h src/engine/EngineSystem.h src/engine/FrameClock.h + src/engine/ResourceStore.h src/engine/WorldQuery.h src/engine/CommandBuffer.h src/engine/UpdatePhase.h @@ -128,6 +129,7 @@ if (BUILD_TESTING) tests/CommandBufferTests.cpp tests/SystemSchedulerTests.cpp tests/EngineIntegrationTests.cpp + tests/ResourceStoreTests.cpp ) target_include_directories(safecrowd_tests diff --git "a/docs/\354\240\234\354\266\234\354\232\251/\354\242\205\355\225\251\354\204\244\352\263\204/Pathfinder_\353\260\230\354\230\201_\353\260\234\355\221\234_\354\264\210\354\225\210.md" "b/docs/\354\240\234\354\266\234\354\232\251/\354\242\205\355\225\251\354\204\244\352\263\204/Pathfinder_\353\260\230\354\230\201_\353\260\234\355\221\234_\354\264\210\354\225\210.md" index 3589f83..40ca017 100644 --- "a/docs/\354\240\234\354\266\234\354\232\251/\354\242\205\355\225\251\354\204\244\352\263\204/Pathfinder_\353\260\230\354\230\201_\353\260\234\355\221\234_\354\264\210\354\225\210.md" +++ "b/docs/\354\240\234\354\266\234\354\232\251/\354\242\205\355\225\251\354\204\244\352\263\204/Pathfinder_\353\260\230\354\230\201_\353\260\234\355\221\234_\354\264\210\354\225\210.md" @@ -87,5 +87,63 @@ run 요약, variation 요약, 비교, 누적 기록의 분리는, 개별 실행 # 슬라이드 10 일부 User Story의 Acceptance Criteria가 변경되어 Sprint 별 배치도 조금씩 변경되었습니다. -# 슬라이드 11 - 구현내용 -(추가 예정) \ No newline at end of file +# 슬라이드 11 - Engine 파트 구현 범위 +이제 구현 내용 중에서 제가 담당한 Engine 파트를 간단히 설명드리겠습니다. +저희 프로젝트는 `application -> domain -> engine` 계층 구조를 유지하고 있는데, 여기서 Engine은 SafeCrowd에서 범용 ECS 실행 기반을 담당합니다. +제가 구현한 범위는 크게 다섯 가지로, `EcsCore`, `WorldQuery`, `CommandBuffer`, `EngineRuntime`, `SystemScheduler`입니다. +이 작업은 한 번에 큰 덩어리로 프로젝트에 구현한 것이 아니라, 이슈 단위로 쪼개서 브랜치와 PR 흐름으로 진행했습니다. +예를 들어 이슈 `#8`에서는 `ComponentRegistry`와 `EcsCore`를, 이슈 `#9`에서는 `WorldQuery`를, 이슈 `#10`에서는 `CommandBuffer`를, 이슈 `#12`에서는 `EngineRuntime`을, 그리고 이슈 `#47`에서는 `SystemScheduler`를 구현했습니다. +즉 엔티티와 컴포넌트를 저장하고 관리하는 코어부터, 시스템이 월드를 읽고 변경하는 방식, 그리고 실제 실행 루프와 스케줄링까지 단계적으로 확장한 것입니다. +이번 구현의 목표는 단순히 데이터를 담는 자료구조를 만드는 것이 아니라, 이후 시뮬레이션 로직이 안정적으로 올라갈 수 있는 실행 환경을 먼저 구축하는 것이었습니다. + +# 슬라이드 12 - ECS 실행 구조 설계 +구조를 보면 먼저 `EcsCore`가 엔티티 생성과 삭제, 컴포넌트 추가와 제거를 통합 관리합니다. +이때 컴포넌트가 추가되거나 제거되면 엔티티의 `Signature` 비트셋이 자동으로 갱신되도록 해서, 각 엔티티가 어떤 컴포넌트 조합을 가지고 있는지 일관되게 추적할 수 있도록 했습니다. +그 위에서 `WorldQuery`는 시스템이 필요한 엔티티만 조회할 수 있는 읽기 전용 인터페이스를 제공합니다. +반대로 구조 변경은 즉시 반영하지 않고, `CommandBuffer`와 `WorldCommands`를 통해 먼저 명령으로 기록한 뒤 나중에 한 번에 반영하도록 했습니다. +이렇게 읽기와 쓰기를 분리하도록 구현한 이유는, 시스템 실행 중 월드 구조를 바로 바꾸면 순회 안정성이 깨질 수 있기 때문입니다. + +# 슬라이드 13 - Runtime과 Scheduler 구현 및 검증 +이 구조 위에서 `EngineRuntime`은 전체 실행을 담당하는 최소 오케스트레이터 역할을 합니다. +프레임 시간을 누적한 뒤 `fixed timestep` 기준으로 필요한 만큼 simulation step을 반복 실행하고, 각 step마다 시스템이 호출되도록 구성했습니다. +그리고 `SystemScheduler`에서는 시스템마다 `phase`와 `order`를 지정할 수 있게 해서, `Startup`, `PreSimulation`, `FixedSimulation`, `PostSimulation`, `RenderSync` 같은 단계별 실행 순서를 제어할 수 있도록 했습니다. +또 각 phase가 끝나는 경계에서 `CommandBuffer`를 flush하도록 하여, 시스템이 실행되는 도중에는 구조 변경이 쌓이기만 하고 실제 반영은 일관된 시점에 일어나도록 했습니다. +마지막으로 이 구현은 `EcsCore`, `WorldQuery`, `CommandBuffer`, `EngineRuntime`, `SystemScheduler` 각각에 대해 테스트 코드를 작성해 생명주기 관리, 조회 필터링, deferred mutation, fixed-step 실행, phase 순서 보장을 검증했습니다. +정리하면, SafeCrowd에서 engine 파트 구현은 이후 도메인 시뮬레이션 로직을 안정적으로 올릴 수 있도록 ECS의 저장 구조와 실행 구조를 함께 구축한 작업이라고 말씀드릴 수 있습니다. + +# 슬라이드 14 - 2DMap +도메인 계층의 구현 내용을 설명하기에 앞서 계층의 역할에 대해 설명하겠습니다. 도메인 계층은 시설 레이아웃 정의와 에이전트 이동 로직 등 시뮬레이션의 핵심 규칙과 데이터 구조를 포함하는 계층입니다. 시뮬레이션의 토대가 되는 Map은 FacilityLayout2D, Zone2D, Connection2D, Barrier2D, SpawnZone2D로 이루어져 있습니다. +FacilityLayout2D는 전체 시뮬레이션의 최상위 컨테이너입니다. +Zone2D는 보행자가 이동할 수 있는 구역, +Connection2D는 구역과 구역을 잇는 통로, +Barrier2D는 에이전트의 이동을 막는 고정 장애물, +SpawnZone2D 에이전트의 생성 지점 역할을 수행합니다. +출구는 Zone2D의 하위 속성으로 지정되고, 출구와 연결된 Connection2D 객체도 출구 속성을 갖습니다. 이 요소들은 에이전트의 경로 계산에 사용되고, 에이전트가 exitzone에 도달하면 도착한 것으로 판단하게 됩니다. + +# 슬라이드 15 - Agent +스폰된 에이전트는 Position, Velocity, Goal, Agent 컴포넌트를 가집니다. +Position은 에이전트의 좌표, Velocity는 에이전트의 이동 방향과 속도, Goal은 목적지와 도착 여부, Agent는 에이전트 개인의 물리적 크기나 이동 능력치 등의 고유 속성을 정의합니다. +에이전트는 방향 벡터에 maxspeed 값을 곱해 이동 속도를 정합니다. 만약, 에이전트가 목적지에 도달했다면 goal의 도착 여부 값을 true로 설정하고 속도를 0으로 조정합니다. +충돌 여부 탐지를 위해서는 이동 경로상에 있는 벽 데이터를 가져와 다음 프레임 위치가 벽과 충돌이 발생하는지 계산합니다. 충돌이 발생하지 않은 경우, 속도를 그대로 유지하고 충돌이 발생한 경우, 벽의 법선 벡터를 구해 벽을 따라 이동하도록 처리합니다. +각 에이전트는 goal에 도착할 때까지 이 과정을 반복합니다. + +# 슬라이드 16 - Import module +이 슬라이드는 Domain 계층에서 import 모듈이 도면 파일을 시뮬레이션에 사용할 수 있는 구조로 바꾸는 과정을 보여줍니다. 먼저 DxfImportService가 DXF 도면을 읽어 필요한 선과 도형 정보를 가져옵니다. 이 원본 정보는 RawImportModel에 한 번 보관해서, 나중에 문제가 생겼을 때 어떤 도면 요소에서 나온 것인지 추적할 수 있게 합니다. 그다음 CanonicalGeometry 단계에서 도면의 여러 요소를 벽, 출구, 보행 가능 영역처럼 공통된 의미로 정리합니다. 이후 FacilityLayoutBuilder가 이 정보를 방, 복도, 연결 통로 같은 시뮬레이션용 공간 구조로 변환합니다. 마지막으로 ImportValidationService가 출구 누락이나 끊긴 동선 같은 문제를 검사하고, ImportResult가 최종 결과와 문제 목록을 함께 넘겨서 사용자가 확인한 뒤 다음 단계로 넘어가게 합니다. + +# 슬라이드 17 - 구현내용 - UI +이번 슬라이드에서는 Application, UI 구현 내용을 설명드리겠습니다. + +먼저 요구 사항으로는, 처음 맵을 만들 때 사용자가 기본 도구의 위치를 쉽게 찾을 수 있어야 하고, 현재 작업이나 진행 상태가 화면에 보였으면 좋겠고, +또 도면을 import할 수 있는 기능이 필요하다는 점이 있었습니다. + +이 요구를 반영해서, 맵 에디터를 처음 켰을 때 바로 사용할 수 있는 기본 도구 4가지를 화면 왼쪽에 배치했습니다. +또 사용자가 현재 진행 상황을 직관적으로 확인할 수 있도록 우측 속성 영역에 progress bar를 두었습니다. +그리고 메뉴바의 File - Import를 통해 도면 파일을 불러올 수 있도록 UI 흐름도 추가했습니다. + +즉, 이번 UI 구현은 단순히 화면을 꾸민 것이 아니라, 사용자가 처음 진입했을 때 바로 도구를 찾고, 현재 상태를 확인하고, +외부 도면을 가져오는 기본 작업 흐름이 자연스럽게 이어지도록 구성한 것입니다. + +아직 구현 예정인 부분도 있습니다. +예를 들어 결과 지표를 확인하는 전용 창과, 대안 시뮬레이션을 설정하는 창은 후속 작업으로 추가할 예정입니다. + +정리하면, 현재 Application UI는 기본 편집 도구 배치, 진행 상태 표시, 그리고 import 진입 경로를 우선 반영한 상태라고 볼 수 있습니다. \ No newline at end of file diff --git a/src/engine/EngineRuntime.cpp b/src/engine/EngineRuntime.cpp index bc99e88..8f94624 100644 --- a/src/engine/EngineRuntime.cpp +++ b/src/engine/EngineRuntime.cpp @@ -24,7 +24,7 @@ EngineConfig normalizeConfig(EngineConfig config) { EngineRuntime::EngineRuntime(EngineConfig config) : config_(normalizeConfig(config)), scheduler_(core_, buffer_), - world_(EngineWorld::ConstructionToken{}, core_, buffer_), + world_(EngineWorld::ConstructionToken{}, core_, resources_, buffer_), frameClock_(config_) { } @@ -36,6 +36,7 @@ void EngineRuntime::addSystem(std::unique_ptr system, void EngineRuntime::initialize() { frameClock_.reset(); core_ = EcsCore{}; + resources_ = ResourceStore{}; buffer_ = CommandBuffer{}; stats_ = {}; stats_.state = EngineState::Ready; @@ -70,6 +71,7 @@ void EngineRuntime::pause() { void EngineRuntime::stop() { frameClock_.reset(); core_ = EcsCore{}; + resources_ = ResourceStore{}; buffer_ = CommandBuffer{}; stats_ = {}; stats_.state = EngineState::Stopped; diff --git a/src/engine/EngineRuntime.h b/src/engine/EngineRuntime.h index 407e781..6c7d823 100644 --- a/src/engine/EngineRuntime.h +++ b/src/engine/EngineRuntime.h @@ -10,6 +10,7 @@ #include "engine/EngineSystem.h" #include "engine/EngineWorld.h" #include "engine/FrameClock.h" +#include "engine/ResourceStore.h" #include "engine/SystemDescriptor.h" #include "engine/SystemScheduler.h" @@ -39,6 +40,7 @@ class EngineRuntime { EngineConfig config_; EngineStats stats_; EcsCore core_; + ResourceStore resources_; CommandBuffer buffer_; SystemScheduler scheduler_; EngineWorld world_; diff --git a/src/engine/EngineSystem.h b/src/engine/EngineSystem.h index b0c6196..1ea062b 100644 --- a/src/engine/EngineSystem.h +++ b/src/engine/EngineSystem.h @@ -4,7 +4,6 @@ #include "engine/EngineStepContext.h" namespace safecrowd::engine { - class EngineSystem { public: virtual ~EngineSystem() = default; diff --git a/src/engine/EngineWorld.h b/src/engine/EngineWorld.h index c31aab5..f1db8e9 100644 --- a/src/engine/EngineWorld.h +++ b/src/engine/EngineWorld.h @@ -1,6 +1,7 @@ #pragma once #include "engine/CommandBuffer.h" +#include "engine/ResourceStore.h" #include "engine/WorldQuery.h" namespace safecrowd::engine { @@ -13,6 +14,8 @@ class EngineWorld { public: [[nodiscard]] WorldQuery& query() noexcept { return query_; } [[nodiscard]] const WorldQuery& query() const noexcept { return query_; } + [[nodiscard]] WorldResources& resources() noexcept { return resources_; } + [[nodiscard]] const WorldResources& resources() const noexcept { return resources_; } [[nodiscard]] WorldCommands& commands() noexcept { return commands_; } private: @@ -25,14 +28,16 @@ class EngineWorld { }; EngineWorld() = delete; - explicit EngineWorld(ConstructionToken, EcsCore& core, CommandBuffer& buffer) - : query_(core), commands_(buffer) {} + explicit EngineWorld(ConstructionToken, EcsCore& core, ResourceStore& resources, + CommandBuffer& buffer) + : query_(core), resources_(resources), commands_(buffer) {} friend class EngineRuntime; friend class internal::EngineWorldFactory; - WorldQuery query_; - WorldCommands commands_; + WorldQuery query_; + WorldResources resources_; + WorldCommands commands_; }; } // namespace safecrowd::engine diff --git a/src/engine/ResourceStore.h b/src/engine/ResourceStore.h new file mode 100644 index 0000000..6ea0361 --- /dev/null +++ b/src/engine/ResourceStore.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace safecrowd::engine { + +class ResourceStore { +public: + template + void set(T resource) { + resources_[std::type_index(typeid(T))] = std::move(resource); + } + + template + [[nodiscard]] bool contains() const { + return resources_.contains(std::type_index(typeid(T))); + } + + template + [[nodiscard]] T& get() { + return getInternal(); + } + + template + [[nodiscard]] const T& get() const { + return getInternal(); + } + +private: + template + [[nodiscard]] T& getInternal() const { + const auto it = resources_.find(std::type_index(typeid(T))); + if (it == resources_.end()) { + throw std::runtime_error("Requested resource is not present in ResourceStore"); + } + + auto* value = std::any_cast(&it->second); + if (value == nullptr) { + throw std::runtime_error("Stored resource type does not match requested type"); + } + + return *value; + } + + mutable std::unordered_map resources_; +}; + +class WorldResources { +public: + explicit WorldResources(ResourceStore& store) + : store_(store) {} + + template + void set(T resource) { + store_.set(std::move(resource)); + } + + template + [[nodiscard]] bool contains() const { + return store_.contains(); + } + + template + [[nodiscard]] T& get() { + return store_.get(); + } + + template + [[nodiscard]] const T& get() const { + return store_.get(); + } + +private: + ResourceStore& store_; +}; + +} // namespace safecrowd::engine diff --git a/src/engine/internal/EngineWorldFactory.h b/src/engine/internal/EngineWorldFactory.h index 4f0d001..b22306b 100644 --- a/src/engine/internal/EngineWorldFactory.h +++ b/src/engine/internal/EngineWorldFactory.h @@ -8,8 +8,9 @@ class EngineWorldFactory { public: EngineWorldFactory() = delete; - [[nodiscard]] static EngineWorld create(EcsCore& core, CommandBuffer& buffer) { - return EngineWorld(EngineWorld::ConstructionToken{}, core, buffer); + [[nodiscard]] static EngineWorld create(EcsCore& core, ResourceStore& resources, + CommandBuffer& buffer) { + return EngineWorld(EngineWorld::ConstructionToken{}, core, resources, buffer); } }; diff --git a/tests/EngineRuntimeTests.cpp b/tests/EngineRuntimeTests.cpp index e87969e..7c2a45f 100644 --- a/tests/EngineRuntimeTests.cpp +++ b/tests/EngineRuntimeTests.cpp @@ -10,6 +10,9 @@ namespace { struct Marker {}; +struct SharedCounter { + int value{0}; +}; class UpdateCounterSystem : public safecrowd::engine::EngineSystem { public: @@ -64,6 +67,16 @@ class RecordPhaseSystem : public safecrowd::engine::EngineSystem { } }; +class ResourceSetupSystem : public safecrowd::engine::EngineSystem { +public: + void configure(safecrowd::engine::EngineWorld& world) override { + world.resources().set(SharedCounter{7}); + } + + void update(safecrowd::engine::EngineWorld&, const safecrowd::engine::EngineStepContext&) override { + } +}; + } // namespace SC_TEST(EngineRuntimePlayAndStepUpdatesStats) { @@ -259,3 +272,35 @@ SC_TEST(EngineRuntimePauseAndStopResetLifecycleState) { SC_EXPECT_EQ(stats.fixedStepIndex, 0ULL); SC_EXPECT_NEAR(stats.alpha, 0.0, 1e-9); } + +SC_TEST(EngineRuntime_WorldResources_AccessibleThroughEngineWorld) { + safecrowd::engine::EngineRuntime runtime; + + runtime.addSystem(std::make_unique()); + runtime.initialize(); + + SC_EXPECT_TRUE(runtime.world().resources().contains()); + SC_EXPECT_EQ(runtime.world().resources().get().value, 7); +} + +SC_TEST(EngineRuntime_Stop_ClearsWorldResourcesBeforeNextRun) { + safecrowd::engine::EngineRuntime runtime; + + runtime.world().resources().set(SharedCounter{11}); + SC_EXPECT_TRUE(runtime.world().resources().contains()); + + runtime.stop(); + + SC_EXPECT_TRUE(!runtime.world().resources().contains()); +} + +SC_TEST(EngineRuntime_Initialize_ClearsExistingWorldResources) { + safecrowd::engine::EngineRuntime runtime; + + runtime.world().resources().set(SharedCounter{13}); + SC_EXPECT_TRUE(runtime.world().resources().contains()); + + runtime.initialize(); + + SC_EXPECT_TRUE(!runtime.world().resources().contains()); +} diff --git a/tests/ResourceStoreTests.cpp b/tests/ResourceStoreTests.cpp new file mode 100644 index 0000000..7eb8d6f --- /dev/null +++ b/tests/ResourceStoreTests.cpp @@ -0,0 +1,47 @@ +#include "TestSupport.h" + +#include +#include + +#include "engine/ResourceStore.h" + +namespace { + +struct SampleResource { + int value{0}; +}; + +} // namespace + +SC_TEST(ResourceStore_SetGetAndContainsWork) { + safecrowd::engine::ResourceStore store; + + store.set(SampleResource{42}); + + SC_EXPECT_TRUE(store.contains()); + SC_EXPECT_EQ(store.get().value, 42); +} + +SC_TEST(ResourceStore_GetMissingResourceThrows) { + safecrowd::engine::ResourceStore store; + + bool threw = false; + try { + (void)store.get(); + } catch (const std::runtime_error&) { + threw = true; + } + + SC_EXPECT_TRUE(threw); +} + +SC_TEST(WorldResources_DelegatesToUnderlyingStore) { + safecrowd::engine::ResourceStore store; + safecrowd::engine::WorldResources resources{store}; + + resources.set(std::string{"seeded"}); + + SC_EXPECT_TRUE(resources.contains()); + SC_EXPECT_EQ(resources.get(), std::string{"seeded"}); + SC_EXPECT_EQ(store.get(), std::string{"seeded"}); +} diff --git a/tests/SystemSchedulerTests.cpp b/tests/SystemSchedulerTests.cpp index 5cc00f0..8681127 100644 --- a/tests/SystemSchedulerTests.cpp +++ b/tests/SystemSchedulerTests.cpp @@ -7,6 +7,7 @@ #include "engine/CommandBuffer.h" #include "engine/EcsCore.h" +#include "engine/ResourceStore.h" #include "engine/SystemScheduler.h" #include "engine/internal/EngineWorldFactory.h" @@ -67,8 +68,10 @@ SC_TEST(SystemScheduler_ExecutesSystemsInPhaseOrder) { safecrowd::engine::SystemScheduler scheduler{core, buffer}; safecrowd::engine::EcsCore dummyCore; + safecrowd::engine::ResourceStore dummyResources; safecrowd::engine::CommandBuffer dummyBuffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(dummyCore, dummyBuffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create( + dummyCore, dummyResources, dummyBuffer); std::vector log; scheduler.registerSystem( @@ -105,8 +108,10 @@ SC_TEST(SystemScheduler_ExecutesSystemsInOrderWithinPhase) { safecrowd::engine::SystemScheduler scheduler{core, buffer}; safecrowd::engine::EcsCore dummyCore; + safecrowd::engine::ResourceStore dummyResources; safecrowd::engine::CommandBuffer dummyBuffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(dummyCore, dummyBuffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create( + dummyCore, dummyResources, dummyBuffer); std::vector log; scheduler.registerSystem( @@ -134,8 +139,10 @@ SC_TEST(SystemScheduler_PhaseIsolation_OtherPhaseSystemsNotExecuted) { safecrowd::engine::SystemScheduler scheduler{core, buffer}; safecrowd::engine::EcsCore dummyCore; + safecrowd::engine::ResourceStore dummyResources; safecrowd::engine::CommandBuffer dummyBuffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(dummyCore, dummyBuffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create( + dummyCore, dummyResources, dummyBuffer); std::vector log; scheduler.registerSystem( @@ -156,9 +163,11 @@ SC_TEST(SystemScheduler_PhaseIsolation_OtherPhaseSystemsNotExecuted) { SC_TEST(SystemScheduler_ConfigureFlushesCommandsBetweenSystems) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; safecrowd::engine::SystemScheduler scheduler{core, buffer}; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create( + core, resources, buffer); std::size_t configuredCount = 0; scheduler.registerSystem(std::make_unique(), {}); @@ -172,10 +181,12 @@ SC_TEST(SystemScheduler_ConfigureFlushesCommandsBetweenSystems) { SC_TEST(SystemScheduler_FlushesCommandBufferAfterPhase) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; safecrowd::engine::SystemScheduler scheduler{core, buffer}; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create( + core, resources, buffer); scheduler.registerSystem( std::make_unique(), diff --git a/tests/WorldQueryTests.cpp b/tests/WorldQueryTests.cpp index 9c88492..1fca7b9 100644 --- a/tests/WorldQueryTests.cpp +++ b/tests/WorldQueryTests.cpp @@ -21,12 +21,14 @@ struct Velocity { static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); SC_TEST(WorldQuery_ViewFiltersEntitiesBySignature) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, resources, buffer); const auto e1 = core.createEntity(); const auto e2 = core.createEntity(); @@ -44,8 +46,9 @@ SC_TEST(WorldQuery_ViewFiltersEntitiesBySignature) { SC_TEST(WorldQuery_ViewReturnsEmptyIfTypeNotRegistered) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, resources, buffer); static_cast(core.createEntity()); @@ -55,8 +58,9 @@ SC_TEST(WorldQuery_ViewReturnsEmptyIfTypeNotRegistered) { SC_TEST(WorldQuery_ViewExcludesDestroyedEntities) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, resources, buffer); const auto e1 = core.createEntity(); core.addComponent(e1, Position{}); @@ -72,8 +76,9 @@ SC_TEST(WorldQuery_ViewExcludesDestroyedEntities) { SC_TEST(WorldQuery_ContainsReflectsComponentPresence) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, resources, buffer); const auto e = core.createEntity(); core.addComponent(e, Position{}); @@ -84,8 +89,9 @@ SC_TEST(WorldQuery_ContainsReflectsComponentPresence) { SC_TEST(WorldQuery_GetReturnsComponentRef) { safecrowd::engine::EcsCore core; + safecrowd::engine::ResourceStore resources; safecrowd::engine::CommandBuffer buffer; - auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, buffer); + auto world = safecrowd::engine::internal::EngineWorldFactory::create(core, resources, buffer); const auto e = core.createEntity(); core.addComponent(e, Position{3.0f, 4.0f});