From 09133081362387af07567dda717332bc8908e02b Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Mon, 22 Apr 2024 14:31:34 +0530 Subject: [PATCH] YAGNI: Simplify asset workflow, remove reflected factories, etc. (#33) * [WIP] Simplify Enemy spawn flow, fix race in `Resources`. * Remove `EnemySpawner`, `EnemyFactoryBuilder`. * Remove `WorldSpec`, add `AssetManifest`. Hard-code manifest in game.cpp. * Refactor asset loading into a dedicated scene. * Remove `setup()`, can be done in constructors now. * Misc cleanup. --- assets/worlds/playground.json | 24 ------ .../spaced/{game => assets}/asset_list.cpp | 35 ++------- .../spaced/{game => assets}/asset_list.hpp | 4 +- .../spaced/{game => assets}/asset_loader.cpp | 2 +- .../spaced/{game => assets}/asset_loader.hpp | 0 src/spaced/spaced/assets/asset_manifest.hpp | 13 ++++ src/spaced/spaced/async_exec.cpp | 4 +- src/spaced/spaced/async_exec.hpp | 4 +- .../game/enemies/basic_creep_factory.cpp | 54 ------------- .../game/enemies/basic_creep_factory.hpp | 29 ------- src/spaced/spaced/game/enemies/creep.hpp | 2 +- .../spaced/game/enemies/creep_factory.cpp | 21 +++++ .../spaced/game/enemies/creep_factory.hpp | 16 ++++ src/spaced/spaced/game/enemy.cpp | 21 +++-- src/spaced/spaced/game/enemy.hpp | 11 ++- src/spaced/spaced/game/enemy_factory.cpp | 14 +++- src/spaced/spaced/game/enemy_factory.hpp | 23 ++---- .../spaced/game/enemy_factory_builder.cpp | 23 ------ .../spaced/game/enemy_factory_builder.hpp | 18 ----- src/spaced/spaced/game/enemy_spawner.cpp | 64 --------------- src/spaced/spaced/game/enemy_spawner.hpp | 31 -------- src/spaced/spaced/game/player.cpp | 42 +++++----- src/spaced/spaced/game/player.hpp | 4 - src/spaced/spaced/game/world.cpp | 77 +++++++++++++------ src/spaced/spaced/game/world.hpp | 20 +++-- src/spaced/spaced/game/world_spec.hpp | 18 ----- src/spaced/spaced/resource_map.hpp | 2 + src/spaced/spaced/scene.cpp | 60 +-------------- src/spaced/spaced/scene.hpp | 13 ---- src/spaced/spaced/scenes/game.cpp | 30 +++++--- src/spaced/spaced/scenes/game.hpp | 7 +- src/spaced/spaced/scenes/home.cpp | 11 +-- src/spaced/spaced/scenes/home.hpp | 4 +- src/spaced/spaced/scenes/load_assets.cpp | 41 ++++++++++ src/spaced/spaced/scenes/load_assets.hpp | 20 +++++ src/spaced/spaced/spaced.cpp | 6 +- 36 files changed, 280 insertions(+), 488 deletions(-) delete mode 100644 assets/worlds/playground.json rename src/spaced/spaced/{game => assets}/asset_list.cpp (67%) rename src/spaced/spaced/{game => assets}/asset_list.hpp (92%) rename src/spaced/spaced/{game => assets}/asset_loader.cpp (98%) rename src/spaced/spaced/{game => assets}/asset_loader.hpp (100%) create mode 100644 src/spaced/spaced/assets/asset_manifest.hpp delete mode 100644 src/spaced/spaced/game/enemies/basic_creep_factory.cpp delete mode 100644 src/spaced/spaced/game/enemies/basic_creep_factory.hpp create mode 100644 src/spaced/spaced/game/enemies/creep_factory.cpp create mode 100644 src/spaced/spaced/game/enemies/creep_factory.hpp delete mode 100644 src/spaced/spaced/game/enemy_factory_builder.cpp delete mode 100644 src/spaced/spaced/game/enemy_factory_builder.hpp delete mode 100644 src/spaced/spaced/game/enemy_spawner.cpp delete mode 100644 src/spaced/spaced/game/enemy_spawner.hpp delete mode 100644 src/spaced/spaced/game/world_spec.hpp create mode 100644 src/spaced/spaced/scenes/load_assets.cpp create mode 100644 src/spaced/spaced/scenes/load_assets.hpp diff --git a/assets/worlds/playground.json b/assets/worlds/playground.json deleted file mode 100644 index d85d0db..0000000 --- a/assets/worlds/playground.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Playground", - "background_tint": "mocha", - "player": { - "tint": "black", - "exhaust_emitter": "particles/exhaust.json", - "death_emitter": "particles/explode.json" - }, - "enemy_factories": [ - { - "type_name": "BasicCreepFactory", - "tints": [ - "orange", - "milk" - ], - "spawn_rate": 2, - "initial_health": 2, - "death_emitter": "particles/explode.json", - "death_sfx": [ - "sfx/bubble.wav" - ] - } - ] -} \ No newline at end of file diff --git a/src/spaced/spaced/game/asset_list.cpp b/src/spaced/spaced/assets/asset_list.cpp similarity index 67% rename from src/spaced/spaced/game/asset_list.cpp rename to src/spaced/spaced/assets/asset_list.cpp index f720dc2..4d66598 100644 --- a/src/spaced/spaced/game/asset_list.cpp +++ b/src/spaced/spaced/assets/asset_list.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include namespace spaced { @@ -37,31 +37,12 @@ auto AssetList::add_audio_clip(std::string uri) -> AssetList& { return *this; } -auto AssetList::read_world_spec(std::string_view const uri) -> WorldSpec { - if (uri.empty()) { return {}; } - - auto const json = m_loader.load_json(uri); - if (!json) { return {}; } - - auto ret = WorldSpec{}; - ret.name = json["name"].as_string(); - ret.background_tint = json["background_tint"].as_string(); - - if (auto const& player = json["player"]) { - ret.player.tint = player["tint"].as_string(); - ret.player.exhaust_emitter = player["exhaust_emitter"].as_string(); - ret.player.death_emitter = player["death_emitter"].as_string(); - add_particle_emitter(ret.player.exhaust_emitter); - add_particle_emitter(ret.player.death_emitter); - } - - for (auto const& enemy_factory : json["enemy_factories"].array_view()) { - add_particle_emitter(enemy_factory["death_emitter"].as()); - for (auto const& death_sfx : enemy_factory["death_sfx"].array_view()) { add_audio_clip(death_sfx.as()); } - ret.enemy_factories.push_back(enemy_factory); - } - - return ret; +void AssetList::add_manifest(AssetManifest manifest) { + for (auto& uri : manifest.textures) { add_texture(std::move(uri), false); } + for (auto& uri : manifest.mip_mapped_textures) { add_texture(std::move(uri), true); } + for (auto& uri : manifest.fonts) { add_font(std::move(uri)); } + for (auto& uri : manifest.audio_clips) { add_audio_clip(std::move(uri)); } + for (auto& uri : manifest.particle_emitters) { add_particle_emitter(std::move(uri)); } } auto AssetList::build_task_stages() const -> std::vector { diff --git a/src/spaced/spaced/game/asset_list.hpp b/src/spaced/spaced/assets/asset_list.hpp similarity index 92% rename from src/spaced/spaced/game/asset_list.hpp rename to src/spaced/spaced/assets/asset_list.hpp index d77fcc1..8545a2d 100644 --- a/src/spaced/spaced/game/asset_list.hpp +++ b/src/spaced/spaced/assets/asset_list.hpp @@ -1,7 +1,7 @@ #pragma once #include +#include #include -#include #include #include @@ -18,7 +18,7 @@ class AssetList { auto add_particle_emitter(std::string uri) -> AssetList&; auto add_audio_clip(std::string uri) -> AssetList&; - auto read_world_spec(std::string_view uri) -> WorldSpec; + void add_manifest(AssetManifest manifest); [[nodiscard]] auto build_task_stages() const -> std::vector; diff --git a/src/spaced/spaced/game/asset_loader.cpp b/src/spaced/spaced/assets/asset_loader.cpp similarity index 98% rename from src/spaced/spaced/game/asset_loader.cpp rename to src/spaced/spaced/assets/asset_loader.cpp index 5e8399a..24ab1bf 100644 --- a/src/spaced/spaced/game/asset_loader.cpp +++ b/src/spaced/spaced/assets/asset_loader.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include namespace spaced { diff --git a/src/spaced/spaced/game/asset_loader.hpp b/src/spaced/spaced/assets/asset_loader.hpp similarity index 100% rename from src/spaced/spaced/game/asset_loader.hpp rename to src/spaced/spaced/assets/asset_loader.hpp diff --git a/src/spaced/spaced/assets/asset_manifest.hpp b/src/spaced/spaced/assets/asset_manifest.hpp new file mode 100644 index 0000000..471c73f --- /dev/null +++ b/src/spaced/spaced/assets/asset_manifest.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace spaced { +struct AssetManifest { + std::vector textures{}; + std::vector mip_mapped_textures{}; + std::vector fonts{}; + std::vector audio_clips{}; + std::vector particle_emitters{}; +}; +} // namespace spaced diff --git a/src/spaced/spaced/async_exec.cpp b/src/spaced/spaced/async_exec.cpp index fbb088c..00e50a5 100644 --- a/src/spaced/spaced/async_exec.cpp +++ b/src/spaced/spaced/async_exec.cpp @@ -6,14 +6,14 @@ namespace spaced { using namespace std::chrono_literals; -AsyncExec::AsyncExec(std::span tasks) { +AsyncExec::AsyncExec(std::vector tasks) { if (tasks.empty()) { return; } m_total = static_cast(tasks.size()); enqueue(tasks); } -AsyncExec::AsyncExec(std::span stages) { +AsyncExec::AsyncExec(std::vector stages) { if (stages.empty()) { return; } std::move(stages.begin(), stages.end(), std::back_inserter(m_stages)); m_total = std::accumulate(m_stages.begin(), m_stages.end(), 0, [](int count, auto const& tasks) { return static_cast(tasks.size()) + count; }); diff --git a/src/spaced/spaced/async_exec.hpp b/src/spaced/spaced/async_exec.hpp index 3cf2f10..66abe5e 100644 --- a/src/spaced/spaced/async_exec.hpp +++ b/src/spaced/spaced/async_exec.hpp @@ -14,8 +14,8 @@ class AsyncExec { struct Status; - explicit AsyncExec(std::span tasks); - explicit AsyncExec(std::span stages); + explicit AsyncExec(std::vector tasks); + explicit AsyncExec(std::vector stages); auto update() -> Status; diff --git a/src/spaced/spaced/game/enemies/basic_creep_factory.cpp b/src/spaced/spaced/game/enemies/basic_creep_factory.cpp deleted file mode 100644 index 45eddf5..0000000 --- a/src/spaced/spaced/game/enemies/basic_creep_factory.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace spaced { -using bave::NotNull; -using bave::ParticleEmitter; -using bave::random_in_range; -using bave::Seconds; - -BasicCreepFactory::BasicCreepFactory(NotNull services, NotNull listener, dj::Json const& json) - : IEnemyFactory(services), m_listener(listener) { - for (auto const& tint : json["tints"].array_view()) { tints.push_back(tint.as()); } - if (auto const in_death_emitter = services->get().get(json["death_emitter"].as_string())) { death_emitter = *in_death_emitter; } - spawn_rate = Seconds{json["spawn_rate"].as(spawn_rate.count())}; - initial_health = json["initial_health"].as(initial_health); - for (auto const& death_sfx : json["death_sfx"].array_view()) { m_death_sfx.push_back(death_sfx.as()); } -} - -auto BasicCreepFactory::spawn_enemy() -> std::unique_ptr { - auto ret = std::make_unique(get_services(), m_listener); - if (!tints.empty()) { - auto const& rgbas = get_services().get().rgbas; - auto const tint_index = random_in_range(std::size_t{}, tints.size() - 1); - ret->shape.tint = rgbas[tints.at(tint_index)]; - ret->health = initial_health; - } - return ret; -} - -auto BasicCreepFactory::tick(Seconds const dt) -> bool { - if (spawn_rate <= 0s) { return false; } - - m_elapsed += dt; - if (m_elapsed >= spawn_rate) { - m_elapsed = 0s; - return true; - } - return false; -} - -void BasicCreepFactory::do_inspect() { - if constexpr (bave::imgui_v) { - auto sr = spawn_rate.count(); - if (ImGui::DragFloat("spawn rate", &sr, 0.25f, 0.25f, 10.0f)) { spawn_rate = Seconds{sr}; } - - ImGui::Separator(); - } -} -} // namespace spaced diff --git a/src/spaced/spaced/game/enemies/basic_creep_factory.hpp b/src/spaced/spaced/game/enemies/basic_creep_factory.hpp deleted file mode 100644 index fd51d6a..0000000 --- a/src/spaced/spaced/game/enemies/basic_creep_factory.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include - -namespace spaced { -class BasicCreepFactory : public IEnemyFactory { - public: - static constexpr std::string_view type_v{"BasicCreepFactory"}; - - explicit BasicCreepFactory(bave::NotNull services, bave::NotNull listener, dj::Json const& json); - - [[nodiscard]] auto get_type_name() const -> std::string_view final { return type_v; } - [[nodiscard]] auto spawn_enemy() -> std::unique_ptr final; - [[nodiscard]] auto get_death_emitter() const -> bave::ParticleEmitter const& final { return death_emitter; } - - auto tick(bave::Seconds dt) -> bool final; - - std::vector tints{}; - bave::ParticleEmitter death_emitter{}; - bave::Seconds spawn_rate{2s}; - float initial_health{1.0f}; - - private: - void do_inspect() final; - - bave::NotNull m_listener; - bave::Seconds m_elapsed{}; -}; -} // namespace spaced diff --git a/src/spaced/spaced/game/enemies/creep.hpp b/src/spaced/spaced/game/enemies/creep.hpp index ab69b36..12c1c6f 100644 --- a/src/spaced/spaced/game/enemies/creep.hpp +++ b/src/spaced/spaced/game/enemies/creep.hpp @@ -4,7 +4,7 @@ namespace spaced { class Creep : public Enemy { public: - explicit Creep(Services const& services, bave::NotNull listener) : Enemy(services, listener, "Creep") {} + explicit Creep(Services const& services) : Enemy(services, "Creep") {} void tick(bave::Seconds dt, bool in_play) override; diff --git a/src/spaced/spaced/game/enemies/creep_factory.cpp b/src/spaced/spaced/game/enemies/creep_factory.cpp new file mode 100644 index 0000000..cea3595 --- /dev/null +++ b/src/spaced/spaced/game/enemies/creep_factory.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + +namespace spaced { +using bave::random_in_range; +using bave::Seconds; + +auto CreepFactory::spawn_enemy() -> std::unique_ptr { + auto ret = std::make_unique(get_services()); + if (!m_tints.empty()) { + auto const& rgbas = get_services().get().rgbas; + auto const tint_index = random_in_range(std::size_t{}, m_tints.size() - 1); + ret->shape.tint = rgbas[m_tints.at(tint_index)]; + } + ret->health = m_initial_health; + return ret; +} +} // namespace spaced diff --git a/src/spaced/spaced/game/enemies/creep_factory.hpp b/src/spaced/spaced/game/enemies/creep_factory.hpp new file mode 100644 index 0000000..1446c7c --- /dev/null +++ b/src/spaced/spaced/game/enemies/creep_factory.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include + +namespace spaced { +class CreepFactory : public EnemyFactory { + public: + using EnemyFactory::EnemyFactory; + + [[nodiscard]] auto spawn_enemy() -> std::unique_ptr final; + + private: + std::array m_tints{"orange", "milk"}; + float m_initial_health{2.0f}; +}; +} // namespace spaced diff --git a/src/spaced/spaced/game/enemy.cpp b/src/spaced/spaced/game/enemy.cpp index e285a2f..df89f08 100644 --- a/src/spaced/spaced/game/enemy.cpp +++ b/src/spaced/spaced/game/enemy.cpp @@ -11,41 +11,38 @@ using bave::RoundedQuad; using bave::Seconds; using bave::Shader; -Enemy::Enemy(Services const& services, bave::NotNull listener, std::string_view const type) - : health_bar(services), m_layout(&services.get()), m_listener(listener), m_type(type) { +Enemy::Enemy(Services const& services, std::string_view const type) : m_layout(&services.get()), m_health_bar(services), m_type(type) { static constexpr auto init_size_v = glm::vec2{100.0f}; auto const play_area = m_layout->get_play_area(); auto const y_min = play_area.rb.y + 0.5f * init_size_v.y; auto const y_max = play_area.lt.y - 0.5f * init_size_v.y - 50.0f; setup(init_size_v, random_in_range(y_min, y_max)); - health_bar.set_style(services.get().progress_bars["enemy"]); + m_health_bar.set_style(services.get().progress_bars["enemy"]); } auto Enemy::take_damage(float const damage) -> bool { if (is_destroyed()) { return false; } health.inflict_damage(damage); - if (health.is_dead()) { m_listener->on_death(EnemyDeath{.position = shape.transform.position, .points = points}); } return true; } void Enemy::force_death() { health = 0.0f; - health_bar.set_progress(0.0f); - m_listener->on_death(EnemyDeath{.position = shape.transform.position}); + m_health_bar.set_progress(0.0f); } void Enemy::tick(Seconds const dt, bool const /*in_play*/) { - health_bar.position = shape.transform.position; - health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f; - health_bar.size = {shape.get_shape().size.x, 10.0f}; - health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points()); - health_bar.tick(dt); + m_health_bar.position = shape.transform.position; + m_health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f; + m_health_bar.size = {shape.get_shape().size.x, 10.0f}; + m_health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points()); + m_health_bar.tick(dt); } void Enemy::draw(Shader& shader) const { shape.draw(shader); - health_bar.draw(shader); + m_health_bar.draw(shader); } void Enemy::setup(glm::vec2 max_size, float y_position) { diff --git a/src/spaced/spaced/game/enemy.hpp b/src/spaced/spaced/game/enemy.hpp index fe6880f..7927f7a 100644 --- a/src/spaced/spaced/game/enemy.hpp +++ b/src/spaced/spaced/game/enemy.hpp @@ -12,7 +12,7 @@ namespace spaced { class Enemy : public IDamageable, public bave::IDrawable { public: - explicit Enemy(Services const& services, bave::NotNull listener, std::string_view type); + explicit Enemy(Services const& services, std::string_view type); [[nodiscard]] auto get_bounds() const -> bave::Rect<> override { return shape.get_bounds(); } auto take_damage(float damage) -> bool override; @@ -28,22 +28,25 @@ class Enemy : public IDamageable, public bave::IDrawable { void setup(glm::vec2 max_size, float y_position); [[nodiscard]] auto get_layout() const -> ILayout const& { return *m_layout; } - [[nodiscard]] auto get_death_listener() const -> IEnemyDeathListener& { return *m_listener; } void inspect() { if constexpr (bave::debug_v) { do_inspect(); } } bave::RoundedQuadShape shape{}; - ui::ProgressBar health_bar; Health health{}; std::int64_t points{10}; + std::string death_emitter{"particles/explode.json"}; + std::vector death_sfx{"sfx/bubble.wav"}; + private: virtual void do_inspect(); bave::NotNull m_layout; - bave::NotNull m_listener; + + ui::ProgressBar m_health_bar; + std::string_view m_type{}; bool m_destroyed{}; }; diff --git a/src/spaced/spaced/game/enemy_factory.cpp b/src/spaced/spaced/game/enemy_factory.cpp index 062ad4b..97c4c20 100644 --- a/src/spaced/spaced/game/enemy_factory.cpp +++ b/src/spaced/spaced/game/enemy_factory.cpp @@ -3,8 +3,18 @@ namespace spaced { using bave::NotNull; +using bave::Seconds; -IEnemyFactory::IEnemyFactory(NotNull services) : m_services(services), m_audio(&services->get()) {} +EnemyFactory::EnemyFactory(NotNull services) : m_services(services), m_audio(&services->get()) {} -void IEnemyFactory::play_death_sfx() { m_audio->play_any_sfx(m_death_sfx); } +auto EnemyFactory::tick(Seconds const dt) -> std::unique_ptr { + if (spawn_rate <= 0s) { return {}; } + + m_elapsed += dt; + if (m_elapsed >= spawn_rate) { + m_elapsed = 0s; + return spawn_enemy(); + } + return {}; +} } // namespace spaced diff --git a/src/spaced/spaced/game/enemy_factory.hpp b/src/spaced/spaced/game/enemy_factory.hpp index 767e84c..c183b30 100644 --- a/src/spaced/spaced/game/enemy_factory.hpp +++ b/src/spaced/spaced/game/enemy_factory.hpp @@ -1,37 +1,26 @@ #pragma once #include #include -#include -#include namespace spaced { class IAudio; -class IEnemyFactory : public bave::Polymorphic { +class EnemyFactory : public bave::Polymorphic { public: - explicit IEnemyFactory(bave::NotNull services); + explicit EnemyFactory(bave::NotNull services); - [[nodiscard]] virtual auto get_type_name() const -> std::string_view = 0; [[nodiscard]] virtual auto spawn_enemy() -> std::unique_ptr = 0; - [[nodiscard]] virtual auto get_death_emitter() const -> bave::ParticleEmitter const& = 0; - void play_death_sfx(); + auto tick(bave::Seconds dt) -> std::unique_ptr; - virtual auto tick(bave::Seconds dt) -> bool = 0; - - void inspect() { - if constexpr (bave::debug_v) { do_inspect(); } - } - - protected: [[nodiscard]] auto get_services() const -> Services const& { return *m_services; } - std::vector m_death_sfx{}; + bave::Seconds spawn_rate{1s}; private: - virtual void do_inspect() {} - bave::NotNull m_services; bave::NotNull m_audio; + + bave::Seconds m_elapsed{}; }; } // namespace spaced diff --git a/src/spaced/spaced/game/enemy_factory_builder.cpp b/src/spaced/spaced/game/enemy_factory_builder.cpp deleted file mode 100644 index 05477c0..0000000 --- a/src/spaced/spaced/game/enemy_factory_builder.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -namespace spaced { -using bave::NotNull; - -EnemyFactoryBuilder::EnemyFactoryBuilder(NotNull services, NotNull listener) - : m_services(services), m_listener(listener) {} - -auto EnemyFactoryBuilder::build(dj::Json const& json) const -> std::unique_ptr { - auto const type_name = json["type_name"].as_string(); - - if (type_name == BasicCreepFactory::type_v) { return std::make_unique(m_services, m_listener, json); } - - return build_default(); -} - -auto EnemyFactoryBuilder::build_default() const -> std::unique_ptr { - auto ret = std::make_unique(m_services, m_listener, dj::Json{}); - ret->tints = {"orange", "milk"}; - return ret; -} -} // namespace spaced diff --git a/src/spaced/spaced/game/enemy_factory_builder.hpp b/src/spaced/spaced/game/enemy_factory_builder.hpp deleted file mode 100644 index 9ab6124..0000000 --- a/src/spaced/spaced/game/enemy_factory_builder.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include -#include - -namespace spaced { -class EnemyFactoryBuilder { - public: - explicit EnemyFactoryBuilder(bave::NotNull services, bave::NotNull listener); - - [[nodiscard]] auto build(dj::Json const& json) const -> std::unique_ptr; - - [[nodiscard]] auto build_default() const -> std::unique_ptr; - - private: - bave::NotNull m_services; - bave::NotNull m_listener; -}; -} // namespace spaced diff --git a/src/spaced/spaced/game/enemy_spawner.cpp b/src/spaced/spaced/game/enemy_spawner.cpp deleted file mode 100644 index 3510f70..0000000 --- a/src/spaced/spaced/game/enemy_spawner.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include -#include - -namespace spaced { -using bave::FixedString; -using bave::im_text; -using bave::ParticleEmitter; -using bave::Seconds; -using bave::Shader; - -EnemySpawner::EnemySpawner(std::unique_ptr factory) : m_factory(std::move(factory)) { - if (!m_factory) { throw std::runtime_error{"Null EnemyFactory passed to EnemySpawner"}; } -} - -void EnemySpawner::tick(Seconds const dt, bool const in_play) { - if (m_factory->tick(dt)) { spawn(); } - - for (auto const& enemy : m_enemies) { - enemy->tick(dt, in_play); - if (enemy->is_dead()) { explode_at(enemy->get_bounds().centre()); } - } - - for (auto& emitter : m_death_emitters) { emitter.tick(dt); } - - std::erase_if(m_enemies, [](auto const& enemy) { return enemy->is_destroyed(); }); - std::erase_if(m_death_emitters, [](ParticleEmitter const& emitter) { return emitter.active_particles() == 0; }); -} - -void EnemySpawner::draw(Shader& shader) const { - for (auto const& enemy : m_enemies) { enemy->draw(shader); } - for (auto const& emitter : m_death_emitters) { emitter.draw(shader); } -} - -void EnemySpawner::append_targets(std::vector>& out) const { - out.reserve(out.size() + m_enemies.size()); - for (auto const& enemy : m_enemies) { out.emplace_back(enemy.get()); } -} - -void EnemySpawner::explode_at(glm::vec2 const position) { - auto& emitter = m_death_emitters.emplace_back(m_factory->get_death_emitter()); - emitter.config.respawn = false; - emitter.set_position(position); - m_factory->play_death_sfx(); -} - -void EnemySpawner::do_inspect() { - if constexpr (bave::imgui_v) { - im_text("{}", m_factory->get_type_name()); - if (ImGui::Button("spawn")) { spawn(); } - - ImGui::Separator(); - m_factory->inspect(); - - for (std::size_t i = 0; i < m_enemies.size(); ++i) { - if (ImGui::TreeNode(FixedString{"{}", i}.c_str())) { - m_enemies.at(i)->inspect(); - ImGui::TreePop(); - } - } - } -} -} // namespace spaced diff --git a/src/spaced/spaced/game/enemy_spawner.hpp b/src/spaced/spaced/game/enemy_spawner.hpp deleted file mode 100644 index a40f306..0000000 --- a/src/spaced/spaced/game/enemy_spawner.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include - -namespace spaced { -class EnemySpawner { - public: - explicit EnemySpawner(std::unique_ptr factory); - - void tick(bave::Seconds dt, bool in_play); - void draw(bave::Shader& shader) const; - - void spawn() { m_enemies.push_back(m_factory->spawn_enemy()); } - - [[nodiscard]] auto get_enemies() const -> std::span const> { return m_enemies; } - void append_targets(std::vector>& out) const; - - void inspect() { - if constexpr (bave::debug_v) { do_inspect(); } - } - - private: - void explode_at(glm::vec2 position); - void do_inspect(); - - std::unique_ptr m_factory{}; - bave::ParticleEmitter m_death_emitter{}; - std::vector> m_enemies{}; - std::vector m_death_emitters{}; -}; -} // namespace spaced diff --git a/src/spaced/spaced/game/player.cpp b/src/spaced/spaced/game/player.cpp index 7ec3cfe..9ab4edc 100644 --- a/src/spaced/spaced/game/player.cpp +++ b/src/spaced/spaced/game/player.cpp @@ -16,7 +16,25 @@ using bave::RoundedQuad; using bave::Seconds; using bave::Shader; -Player::Player(Services const& services, std::unique_ptr controller) : m_services(&services), m_controller(std::move(controller)) { setup_ship(); } +Player::Player(Services const& services, std::unique_ptr controller) : m_services(&services), m_controller(std::move(controller)) { + auto const& layout = services.get(); + ship.transform.position.x = layout.get_player_x(); + auto rounded_quad = RoundedQuad{}; + rounded_quad.size = layout.get_player_size(); + rounded_quad.corner_radius = 20.0f; + ship.set_shape(rounded_quad); + + auto const& rgbas = services.get().rgbas; + ship.tint = rgbas["black"]; + + auto const& resources = services.get(); + if (auto const exhaust = resources.get("particles/exhaust.json")) { m_exhaust = *exhaust; } + m_exhaust.set_position(get_exhaust_position()); + m_exhaust.pre_warm(); + + if (auto const death = resources.get("particles/explode.json")) { m_death_source = *death; } + m_death_source.config.respawn = false; +} void Player::on_focus(bave::FocusChange const& /*focus_changed*/) { m_controller->untap(); } @@ -63,19 +81,6 @@ void Player::draw(Shader& shader) const { if (m_death) { m_death->draw(shader); } } -void Player::setup(WorldSpec::Player const& spec) { - auto const& rgbas = m_services->get().rgbas; - auto const& resources = m_services->get(); - ship.tint = rgbas[spec.tint]; - - if (auto const exhaust = resources.get(spec.exhaust_emitter)) { m_exhaust = *exhaust; } - m_exhaust.set_position(get_exhaust_position()); - m_exhaust.pre_warm(); - - if (auto const death = resources.get(spec.death_emitter)) { m_death_source = *death; } - m_death_source.config.respawn = false; -} - void Player::set_y(float const y) { ship.transform.position.y = y; } auto Player::get_muzzle_position() const -> glm::vec2 { return ship.transform.position + 0.5f * glm::vec2{ship.get_shape().size.x, 0.0f}; } @@ -87,15 +92,6 @@ void Player::set_controller(std::unique_ptr controller) { m_controller = std::move(controller); } -void Player::setup_ship() { - auto const& layout = m_services->get(); - ship.transform.position.x = layout.get_player_x(); - auto rounded_quad = RoundedQuad{}; - rounded_quad.size = layout.get_player_size(); - rounded_quad.corner_radius = 20.0f; - ship.set_shape(rounded_quad); -} - void Player::on_death() { health = 0.0f; m_death = m_death_source; diff --git a/src/spaced/spaced/game/player.hpp b/src/spaced/spaced/game/player.hpp index f4f2665..fdd0914 100644 --- a/src/spaced/spaced/game/player.hpp +++ b/src/spaced/spaced/game/player.hpp @@ -6,7 +6,6 @@ #include #include #include -#include namespace spaced { class Player : public bave::IDrawable { @@ -25,8 +24,6 @@ class Player : public bave::IDrawable { void tick(State const& state, bave::Seconds dt); void draw(bave::Shader& shader) const final; - void setup(WorldSpec::Player const& spec); - void set_y(float y); [[nodiscard]] auto get_y() const -> float { return ship.transform.position.y; } @@ -46,7 +43,6 @@ class Player : public bave::IDrawable { Health health{}; private: - void setup_ship(); void on_death(); void do_inspect(); diff --git a/src/spaced/spaced/game/world.cpp b/src/spaced/spaced/game/world.cpp index 15d5f2d..df80d07 100644 --- a/src/spaced/spaced/game/world.cpp +++ b/src/spaced/spaced/game/world.cpp @@ -1,6 +1,7 @@ #include -#include +#include #include +#include #include #include @@ -10,6 +11,7 @@ namespace spaced { using bave::FixedString; using bave::NotNull; +using bave::ParticleEmitter; using bave::random_in_range; using bave::Seconds; using bave::Shader; @@ -32,7 +34,10 @@ namespace { } // namespace World::World(bave::NotNull services, bave::NotNull scorer) - : player(*services, make_player_controller(*services)), m_services(services), m_scorer(scorer) {} + : player(*services, make_player_controller(*services)), m_services(services), m_resources(&services->get()), m_audio(&services->get()), + m_scorer(scorer) { + m_enemy_factories["CreepFactory"] = std::make_unique(services); +} void World::on_death(EnemyDeath const& death) { m_scorer->add_score(death.points); @@ -45,34 +50,56 @@ void World::on_death(EnemyDeath const& death) { void World::tick(Seconds const dt) { bool const in_play = !player.health.is_dead(); - m_targets.clear(); - for (auto& spawner : m_enemy_spawners) { - spawner.tick(dt, in_play); - spawner.append_targets(m_targets); + if (in_play) { + for (auto& [_, factory] : m_enemy_factories) { + if (auto enemy = factory->tick(dt)) { m_active_enemies.push_back(std::move(enemy)); } + } } - for (auto const& powerup : m_powerups) { powerup->tick(dt); } - std::erase_if(m_powerups, [](auto const& powerup) { return powerup->is_destroyed(); }); + for (auto const& enemy : m_active_enemies) { + enemy->tick(dt, in_play); + if (enemy->is_dead()) { on_death(*enemy, true); } + } + std::erase_if(m_active_enemies, [](auto const& enemy) { return enemy->is_destroyed(); }); + + for (auto& emitter : m_enemy_death_emitters) { emitter.tick(dt); } + std::erase_if(m_enemy_death_emitters, [](ParticleEmitter const& emitter) { return emitter.active_particles() == 0; }); - m_powerups_view.clear(); - for (auto const& powerup : m_powerups) { m_powerups_view.emplace_back(powerup.get()); } + for (auto const& powerup : m_active_powerups) { powerup->tick(dt); } + std::erase_if(m_active_powerups, [](auto const& powerup) { return powerup->is_destroyed(); }); - auto const player_state = Player::State{.targets = m_targets, .powerups = m_powerups_view}; + m_targets.clear(); + for (auto const& enemy : m_active_enemies) { m_targets.emplace_back(enemy.get()); } + m_powerups.clear(); + for (auto const& powerup : m_active_powerups) { m_powerups.emplace_back(powerup.get()); } + + auto const player_state = Player::State{.targets = m_targets, .powerups = m_powerups}; player.tick(player_state, dt); } void World::draw(Shader& shader) const { - for (auto const& spawner : m_enemy_spawners) { spawner.draw(shader); } - for (auto const& powerup : m_powerups) { powerup->draw(shader); } + for (auto const& enemy : m_active_enemies) { enemy->draw(shader); } + for (auto const& emitter : m_enemy_death_emitters) { emitter.draw(shader); } + for (auto const& powerup : m_active_powerups) { powerup->draw(shader); } player.draw(shader); } -void World::load(WorldSpec const& spec) { - m_enemy_spawners.clear(); - auto const factory = EnemyFactoryBuilder{m_services, this}; - for (auto const& factory_json : spec.enemy_factories) { m_enemy_spawners.emplace_back(factory.build(factory_json)); } +void World::on_death(Enemy const& enemy, bool const add_score) { + if (auto source = m_resources->get(enemy.death_emitter)) { + auto& emitter = m_enemy_death_emitters.emplace_back(*source); + emitter.config.respawn = false; + emitter.set_position(enemy.shape.transform.position); + } + + m_audio->play_any_sfx(enemy.death_sfx); - player.setup(spec.player); + if (add_score) { + m_scorer->add_score(enemy.points); + + // temp + if (random_in_range(0, 10) < 3) { debug_spawn_powerup(enemy.shape.transform.position); } + // temp + } } void World::do_inspect() { @@ -84,18 +111,18 @@ void World::do_inspect() { ImGui::EndTabItem(); } - if (ImGui::BeginTabItem("Enemy Spawners")) { - inspect_enemy_spawners(); + if (ImGui::BeginTabItem("Enemies")) { + inspect_enemies(); ImGui::EndTabItem(); } } } -void World::inspect_enemy_spawners() { +void World::inspect_enemies() { if constexpr (bave::imgui_v) { - for (std::size_t i = 0; i < m_enemy_spawners.size(); ++i) { - if (ImGui::TreeNode(FixedString{"[{}]", i}.c_str())) { - m_enemy_spawners.at(i).inspect(); + for (std::size_t i = 0; i < m_active_enemies.size(); ++i) { + if (ImGui::TreeNode(FixedString{"{}", i}.c_str())) { + m_active_enemies.at(i)->inspect(); ImGui::TreePop(); } } @@ -127,6 +154,6 @@ void World::debug_controller_type() { void World::debug_spawn_powerup(glm::vec2 const position) { auto powerup = std::make_unique(*m_services); powerup->shape.transform.position = position; - m_powerups.push_back(std::move(powerup)); + m_active_powerups.push_back(std::move(powerup)); } } // namespace spaced diff --git a/src/spaced/spaced/game/world.hpp b/src/spaced/spaced/game/world.hpp index ae81374..85283db 100644 --- a/src/spaced/spaced/game/world.hpp +++ b/src/spaced/spaced/game/world.hpp @@ -1,11 +1,12 @@ #pragma once -#include +#include #include #include #include -#include namespace spaced { +class Resources; + class World : public ITargetProvider, public IEnemyDeathListener { public: explicit World(bave::NotNull services, bave::NotNull scorer); @@ -17,7 +18,7 @@ class World : public ITargetProvider, public IEnemyDeathListener { void tick(bave::Seconds dt); void draw(bave::Shader& shader) const; - void load(WorldSpec const& spec); + void on_death(Enemy const& enemy, bool add_score); void inspect() { if constexpr (bave::debug_v) { do_inspect(); } @@ -27,18 +28,23 @@ class World : public ITargetProvider, public IEnemyDeathListener { private: void do_inspect(); - void inspect_enemy_spawners(); + void inspect_enemies(); void debug_controller_type(); void debug_spawn_powerup(glm::vec2 position); bave::NotNull m_services; + bave::NotNull m_resources; + bave::NotNull m_audio; bave::NotNull m_scorer; - std::vector m_enemy_spawners{}; - std::vector> m_powerups{}; + std::unordered_map> m_enemy_factories{}; + + std::vector> m_active_enemies{}; + std::vector m_enemy_death_emitters{}; + std::vector> m_active_powerups{}; std::vector> m_targets{}; - std::vector> m_powerups_view{}; + std::vector> m_powerups{}; }; } // namespace spaced diff --git a/src/spaced/spaced/game/world_spec.hpp b/src/spaced/spaced/game/world_spec.hpp deleted file mode 100644 index 5a99adb..0000000 --- a/src/spaced/spaced/game/world_spec.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include -#include - -namespace spaced { -struct WorldSpec { - struct Player { - std::string tint{}; - std::string exhaust_emitter{}; - std::string death_emitter{}; - }; - - std::string name{}; - std::string background_tint{}; - Player player{}; - std::vector enemy_factories{}; -}; -} // namespace spaced diff --git a/src/spaced/spaced/resource_map.hpp b/src/spaced/spaced/resource_map.hpp index aef6fe0..fb44e4b 100644 --- a/src/spaced/spaced/resource_map.hpp +++ b/src/spaced/spaced/resource_map.hpp @@ -35,6 +35,8 @@ class ResourceMap { void clear() { m_resources.clear(); } + [[nodiscard]] auto size() const -> std::size_t { return m_resources.size(); } + private: struct Base : bave::Polymorphic { std::type_index type_index; diff --git a/src/spaced/spaced/scene.cpp b/src/spaced/spaced/scene.cpp index 3f72ae7..5628373 100644 --- a/src/spaced/spaced/scene.cpp +++ b/src/spaced/spaced/scene.cpp @@ -8,36 +8,24 @@ using bave::KeyInput; using bave::PointerMove; using bave::PointerTap; using bave::Seconds; -using bave::Shader; Scene::Scene(App& app, Services const& services, std::string name) : m_log{std::move(name)}, m_app(app), m_services(services) { m_log.info("constructed"); } -void Scene::on_key_event(KeyInput const& key_input) { - if (is_loading()) { return; } - on_key(key_input); -} +void Scene::on_key_event(KeyInput const& key_input) { on_key(key_input); } -void Scene::on_focus_event(bave::FocusChange const& focus_change) { - if (is_loading()) { return; } - on_focus(focus_change); -} +void Scene::on_focus_event(bave::FocusChange const& focus_change) { on_focus(focus_change); } void Scene::on_move_event(PointerMove const& pointer_move) { - if (is_loading()) { return; } if (on_ui_event([pointer_move](ui::View& view) { view.on_move(pointer_move); })) { return; } on_move(pointer_move); } void Scene::on_tap_event(PointerTap const& pointer_tap) { - if (is_loading()) { return; } if (on_ui_event([pointer_tap](ui::View& view) { view.on_tap(pointer_tap); })) { return; } on_tap(pointer_tap); } -void Scene::on_scroll_event(bave::MouseScroll const& mouse_scroll) { - if (is_loading()) { return; } - on_scroll(mouse_scroll); -} +void Scene::on_scroll_event(bave::MouseScroll const& mouse_scroll) { on_scroll(mouse_scroll); } auto Scene::is_ui_blocking_input() const -> bool { return std::any_of(m_views.begin(), m_views.end(), [](auto const& view) { return view->block_input_events; }); @@ -49,8 +37,6 @@ void Scene::push_view(std::unique_ptr view) { } void Scene::tick_frame(Seconds const dt) { - update_loading(dt); - if (is_loading()) { return; } tick(dt); for (auto const& view : m_views) { view->tick(dt); } std::erase_if(m_views, [](auto const& view) { return view->is_destroyed(); }); @@ -59,25 +45,10 @@ void Scene::tick_frame(Seconds const dt) { void Scene::render_frame() const { auto shader = get_app().load_shader("shaders/default.vert", "shaders/default.frag"); if (!shader) { return; } - if (render_loading(*shader)) { return; } render(*shader); for (auto const& view : m_views) { view->render(*shader); } } -void Scene::add_load_tasks(std::span tasks) { - if (tasks.empty()) { return; } - - m_load.emplace(tasks); - m_loading_screen.emplace(m_services); -} - -void Scene::add_load_stages(std::vector task_stages) { - if (task_stages.empty()) { return; } - - m_load.emplace(task_stages); - m_loading_screen.emplace(m_services); -} - template auto Scene::on_ui_event(F per_view) -> bool { for (auto it = m_views.rbegin(); it != m_views.rend(); ++it) { @@ -87,29 +58,4 @@ auto Scene::on_ui_event(F per_view) -> bool { } return false; } - -void Scene::update_loading(Seconds const dt) { - if (!m_loading_screen) { return; } - - if (!m_load) { - m_loading_screen->update(dt, 1.0f); - return; - } - - auto const load_status = m_load->update(); - auto const progress = load_status.progress(); - m_loading_screen->update(dt, progress); - - if (load_status.is_complete()) { - on_loaded(); - m_loading_screen.reset(); - m_load.reset(); - } -} - -auto Scene::render_loading(Shader& shader) const -> bool { - if (!m_loading_screen) { return false; } - m_loading_screen->draw(shader); - return true; -} } // namespace spaced diff --git a/src/spaced/spaced/scene.hpp b/src/spaced/spaced/scene.hpp index c9e5b15..1f8a994 100644 --- a/src/spaced/spaced/scene.hpp +++ b/src/spaced/spaced/scene.hpp @@ -2,9 +2,7 @@ #include #include #include -#include #include -#include #include namespace spaced { @@ -23,7 +21,6 @@ class Scene : public bave::PolyPinned { [[nodiscard]] auto get_app() const -> bave::App& { return m_app; } [[nodiscard]] auto get_services() const -> Services const& { return m_services; } - [[nodiscard]] auto is_loading() const -> bool { return m_loading_screen.has_value(); } [[nodiscard]] auto is_ui_blocking_input() const -> bool; [[nodiscard]] auto make_loader() const -> bave::Loader { return bave::Loader{&m_app.get_data_store(), &m_app.get_render_device()}; } @@ -43,27 +40,17 @@ class Scene : public bave::PolyPinned { virtual void on_scroll(bave::MouseScroll const& /*mouse_scroll*/) {} virtual void on_drop(std::span /*paths*/) {} - virtual void on_loaded() {} virtual void tick(bave::Seconds /*dt*/) {} virtual void render(bave::Shader& /*shader*/) const {} - void add_load_tasks(std::span tasks); - void add_load_stages(std::vector task_stages); - bave::Logger m_log{}; private: template auto on_ui_event(F per_view) -> bool; - void update_loading(bave::Seconds dt); - auto render_loading(bave::Shader& shader) const -> bool; - bave::App& m_app; Services const& m_services; std::vector> m_views{}; - - std::optional m_load{}; - std::optional m_loading_screen{}; }; } // namespace spaced diff --git a/src/spaced/spaced/scenes/game.cpp b/src/spaced/spaced/scenes/game.cpp index dde2920..fa9093a 100644 --- a/src/spaced/spaced/scenes/game.cpp +++ b/src/spaced/spaced/scenes/game.cpp @@ -1,7 +1,6 @@ #include #include -#include -#include +#include #include #include #include @@ -21,17 +20,28 @@ using bave::Ptr; using bave::Seconds; using bave::Shader; -Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m_world(&services, this) { - auto asset_list = AssetList{make_loader(), get_services()}; - m_world_spec = asset_list.read_world_spec("worlds/playground.json"); - clear_colour = services.get().rgbas[m_world_spec.background_tint]; - add_load_stages(asset_list.build_task_stages()); +auto Game::get_manifest() -> AssetManifest { + return AssetManifest{ + .textures = + { + "images/foam_bubble.png", + }, + .audio_clips = + { + "sfx/bubble.wav", + }, + .particle_emitters = + { + "particles/exhaust.json", + "particles/explode.json", + }, + }; } -void Game::on_loaded() { - m_world.load(m_world_spec); +Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m_world(&services, this) { + clear_colour = services.get().rgbas["mocha"]; - auto hud = std::make_unique(get_services()); + auto hud = std::make_unique(services); m_hud = hud.get(); push_view(std::move(hud)); } diff --git a/src/spaced/spaced/scenes/game.hpp b/src/spaced/spaced/scenes/game.hpp index 41a347a..c9b9017 100644 --- a/src/spaced/spaced/scenes/game.hpp +++ b/src/spaced/spaced/scenes/game.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include #include @@ -9,11 +9,11 @@ namespace spaced { class Game : public Scene, public IScorer { public: + static auto get_manifest() -> AssetManifest; + Game(bave::App& app, Services const& services); private: - void on_loaded() final; - void on_focus(bave::FocusChange const& focus_change) final; void on_key(bave::KeyInput const& key_input) final; void on_move(bave::PointerMove const& pointer_move) final; @@ -28,7 +28,6 @@ class Game : public Scene, public IScorer { void inspect(bave::Seconds dt, bave::Seconds frame_time); World m_world; - WorldSpec m_world_spec{}; std::int64_t m_score{}; bave::Ptr m_hud{}; diff --git a/src/spaced/spaced/scenes/home.cpp b/src/spaced/spaced/scenes/home.cpp index 6d22b27..97ed4fc 100644 --- a/src/spaced/spaced/scenes/home.cpp +++ b/src/spaced/spaced/scenes/home.cpp @@ -8,18 +8,13 @@ #include namespace spaced { +using bave::App; using bave::Seconds; using bave::TextHeight; -Home::Home(bave::App& app, Services const& services) : Scene(app, services, "Home") { - auto const& resources = services.get(); - auto const tasks = std::array{ - util::create_font_atlas_task(resources.main_font, {TextHeight{100}, TextHeight{60}}), - }; - add_load_tasks(tasks); -} +auto Home::get_text_heights() -> std::vector { return {TextHeight{100}, TextHeight{60}}; } -void Home::on_loaded() { create_ui(); } +Home::Home(App& app, Services const& services) : Scene(app, services, "Home") { create_ui(); } void Home::create_ui() { auto m_header = std::make_unique(get_services()); diff --git a/src/spaced/spaced/scenes/home.hpp b/src/spaced/spaced/scenes/home.hpp index 937f616..be6028b 100644 --- a/src/spaced/spaced/scenes/home.hpp +++ b/src/spaced/spaced/scenes/home.hpp @@ -4,11 +4,11 @@ namespace spaced { class Home : public Scene { public: + static auto get_text_heights() -> std::vector; + explicit Home(bave::App& app, Services const& services); private: - void on_loaded() final; - void create_ui(); }; } // namespace spaced diff --git a/src/spaced/spaced/scenes/load_assets.cpp b/src/spaced/spaced/scenes/load_assets.cpp new file mode 100644 index 0000000..8aef89a --- /dev/null +++ b/src/spaced/spaced/scenes/load_assets.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace spaced { +using bave::App; +using bave::Loader; +using bave::Seconds; +using bave::Shader; + +namespace { +auto make_load_stages(Loader loader, Services const& services) -> std::vector { + auto asset_list = AssetList{std::move(loader), services}; + asset_list.add_manifest(Game::get_manifest()); + auto ret = asset_list.build_task_stages(); + auto& stage = ret.emplace_back(); + auto& resources = services.get(); + stage.push_back(util::create_font_atlas_task(resources.main_font, Home::get_text_heights())); + return ret; +} +} // namespace + +LoadAssets::LoadAssets(App& app, Services const& services) + : Scene(app, services, "LoadAssets"), m_loading_screen(services), m_load(make_load_stages(make_loader(), services)) {} + +void LoadAssets::on_loaded() { get_services().get().switch_to(); } + +void LoadAssets::tick(Seconds const dt) { + auto const load_status = m_load.update(); + auto const progress = load_status.progress(); + m_loading_screen.update(dt, progress); + + if (load_status.is_complete()) { on_loaded(); } +} + +void LoadAssets::render(Shader& shader) const { m_loading_screen.draw(shader); } +} // namespace spaced diff --git a/src/spaced/spaced/scenes/load_assets.hpp b/src/spaced/spaced/scenes/load_assets.hpp new file mode 100644 index 0000000..95cc0c3 --- /dev/null +++ b/src/spaced/spaced/scenes/load_assets.hpp @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include + +namespace spaced { +class LoadAssets : public Scene { + public: + explicit LoadAssets(bave::App& app, Services const& services); + + private: + void on_loaded(); + + void tick(bave::Seconds dt) final; + void render(bave::Shader& shader) const final; + + ui::LoadingScreen m_loading_screen; + AsyncExec m_load; +}; +} // namespace spaced diff --git a/src/spaced/spaced/spaced.cpp b/src/spaced/spaced/spaced.cpp index 8309dfd..1595b53 100644 --- a/src/spaced/spaced/spaced.cpp +++ b/src/spaced/spaced/spaced.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -132,9 +132,7 @@ void Spaced::tick() { auto const dt = get_app().get_dt(); m_layout->set_framebuffer_size(get_app().get_framebuffer_size()); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) if (m_scene_switcher->next_scene) { - m_resources->clear(); m_audio->stop_music(); m_scene = std::move(m_scene_switcher->next_scene); } @@ -201,7 +199,7 @@ void Spaced::create_services() { void Spaced::set_scene() { auto switcher = std::make_unique(get_app(), m_services); m_scene_switcher = switcher.get(); - switcher->switch_to(); + switcher->switch_to(); m_services.bind(std::move(switcher)); } } // namespace spaced