From a9a868d3f0db09a5161d88f79e0855bca74c5be2 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Wed, 5 Jun 2024 11:36:19 +0530 Subject: [PATCH 1/5] Add scene music, rename `Home` to `MenuScene`. --- src/spaced/spaced/scene.hpp | 2 ++ src/spaced/spaced/scenes/game.cpp | 35 +++++++++++-------- src/spaced/spaced/scenes/game.hpp | 6 ++-- src/spaced/spaced/scenes/load_assets.cpp | 8 ++--- .../spaced/scenes/{home.cpp => menu.cpp} | 10 +++--- .../spaced/scenes/{home.hpp => menu.hpp} | 6 ++-- src/spaced/spaced/spaced.cpp | 10 +++++- src/spaced/spaced/spaced.hpp | 2 ++ 8 files changed, 50 insertions(+), 29 deletions(-) rename src/spaced/spaced/scenes/{home.cpp => menu.cpp} (76%) rename src/spaced/spaced/scenes/{home.hpp => menu.hpp} (50%) diff --git a/src/spaced/spaced/scene.hpp b/src/spaced/spaced/scene.hpp index 1f8a994..cbf320d 100644 --- a/src/spaced/spaced/scene.hpp +++ b/src/spaced/spaced/scene.hpp @@ -18,6 +18,8 @@ class Scene : public bave::PolyPinned { void tick_frame(bave::Seconds dt); void render_frame() const; + [[nodiscard]] virtual auto get_music_uri() const -> std::string_view { return {}; } + [[nodiscard]] auto get_app() const -> bave::App& { return m_app; } [[nodiscard]] auto get_services() const -> Services const& { return m_services; } diff --git a/src/spaced/spaced/scenes/game.cpp b/src/spaced/spaced/scenes/game.cpp index 8eb7b65..142d639 100644 --- a/src/spaced/spaced/scenes/game.cpp +++ b/src/spaced/spaced/scenes/game.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,11 +23,13 @@ using bave::Ptr; using bave::Seconds; using bave::Shader; -auto Game::get_manifest() -> AssetManifest { +auto GameScene::get_manifest() -> AssetManifest { return AssetManifest{ .audio_clips = { "sfx/bubble.wav", + "music/menu.mp3", + "music/game.mp3", }, .particle_emitters = { @@ -38,7 +40,7 @@ auto Game::get_manifest() -> AssetManifest { }; } -Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app), m_world(&services, this) { +GameScene::GameScene(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app), m_world(&services, this) { clear_colour = services.get().rgbas["mocha"]; auto hud = std::make_unique(services); @@ -49,19 +51,19 @@ Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m ++services.get().game.play_count; } -void Game::on_focus(FocusChange const& focus_change) { m_world.player.on_focus(focus_change); } +void GameScene::on_focus(FocusChange const& focus_change) { m_world.player.on_focus(focus_change); } -void Game::on_key(KeyInput const& key_input) { +void GameScene::on_key(KeyInput const& key_input) { if (key_input.key == Key::eEscape && key_input.action == Action::eRelease && key_input.mods == KeyMods{}) { - get_services().get().switch_to(); + get_services().get().switch_to(); } } -void Game::on_move(PointerMove const& pointer_move) { m_world.player.on_move(pointer_move); } +void GameScene::on_move(PointerMove const& pointer_move) { m_world.player.on_move(pointer_move); } -void Game::on_tap(PointerTap const& pointer_tap) { m_world.player.on_tap(pointer_tap); } +void GameScene::on_tap(PointerTap const& pointer_tap) { m_world.player.on_tap(pointer_tap); } -void Game::tick(Seconds const dt) { +void GameScene::tick(Seconds const dt) { auto ft = bave::DeltaTime{}; m_world.tick(dt); @@ -70,19 +72,19 @@ void Game::tick(Seconds const dt) { if constexpr (bave::debug_v) { inspect(dt, ft.update()); } } -void Game::render(Shader& shader) const { m_world.draw(shader); } +void GameScene::render(Shader& shader) const { m_world.draw(shader); } -void Game::add_score(std::int64_t const score) { +void GameScene::add_score(std::int64_t const score) { m_score += score; m_hud->set_score(m_score); update_hi_score(); } -void Game::on_game_over() { +void GameScene::on_game_over() { auto dci = ui::DialogCreateInfo{ .size = {600.0f, 200.0f}, .content_text = "GAME OVER", - .main_button = {.text = "RESTART", .callback = [this] { get_services().get().switch_to(); }}, + .main_button = {.text = "RESTART", .callback = [this] { get_services().get().switch_to(); }}, .second_button = {.text = "QUIT", .callback = [this] { get_app().shutdown(); }}, }; @@ -91,13 +93,13 @@ void Game::on_game_over() { push_view(std::move(dialog)); } -void Game::update_hi_score() { +void GameScene::update_hi_score() { if (m_score <= m_save.get_hi_score()) { return; } m_save.set_hi_score(m_score); m_hud->set_hi_score(m_save.get_hi_score()); } -void Game::inspect(Seconds const dt, Seconds const frame_time) { +void GameScene::inspect(Seconds const dt, Seconds const frame_time) { if constexpr (bave::imgui_v) { m_debug.fps.tick(dt); @@ -118,6 +120,9 @@ void Game::inspect(Seconds const dt, Seconds const frame_time) { im_text("fps: {}", m_debug.fps.fps); ImGui::SliderInt("fps limit", &m_debug.fps.limit, 5, 1000); ImGui::Checkbox("fps lock", &m_debug.fps.lock); + + ImGui::Separator(); + if (ImGui::Button("reload scene")) { get_services().get().switch_to(); } } ImGui::End(); diff --git a/src/spaced/spaced/scenes/game.hpp b/src/spaced/spaced/scenes/game.hpp index 44419c9..3bd14d8 100644 --- a/src/spaced/spaced/scenes/game.hpp +++ b/src/spaced/spaced/scenes/game.hpp @@ -8,11 +8,11 @@ #include namespace spaced { -class Game : public Scene, public IScorer { +class GameScene : public Scene, public IScorer { public: static auto get_manifest() -> AssetManifest; - Game(bave::App& app, Services const& services); + GameScene(bave::App& app, Services const& services); private: void on_focus(bave::FocusChange const& focus_change) final; @@ -23,6 +23,8 @@ class Game : public Scene, public IScorer { void tick(bave::Seconds dt) final; void render(bave::Shader& shader) const final; + [[nodiscard]] auto get_music_uri() const -> std::string_view final { return "music/game.mp3"; } + [[nodiscard]] auto get_score() const -> std::int64_t final { return m_score; } void add_score(std::int64_t score) final; void on_game_over(); diff --git a/src/spaced/spaced/scenes/load_assets.cpp b/src/spaced/spaced/scenes/load_assets.cpp index 8aef89a..ec6e481 100644 --- a/src/spaced/spaced/scenes/load_assets.cpp +++ b/src/spaced/spaced/scenes/load_assets.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include #include @@ -15,11 +15,11 @@ 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()); + asset_list.add_manifest(GameScene::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())); + stage.push_back(util::create_font_atlas_task(resources.main_font, MenuScene::get_text_heights())); return ret; } } // namespace @@ -27,7 +27,7 @@ auto make_load_stages(Loader loader, Services const& services) -> std::vector().switch_to(); } +void LoadAssets::on_loaded() { get_services().get().switch_to(); } void LoadAssets::tick(Seconds const dt) { auto const load_status = m_load.update(); diff --git a/src/spaced/spaced/scenes/home.cpp b/src/spaced/spaced/scenes/menu.cpp similarity index 76% rename from src/spaced/spaced/scenes/home.cpp rename to src/spaced/spaced/scenes/menu.cpp index 8c817c4..d65db83 100644 --- a/src/spaced/spaced/scenes/home.cpp +++ b/src/spaced/spaced/scenes/menu.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -13,11 +13,11 @@ using bave::App; using bave::Seconds; using bave::TextHeight; -auto Home::get_text_heights() -> std::vector { return {TextHeight{100}, TextHeight{60}, ui::Dialog::text_height_v}; } +auto MenuScene::get_text_heights() -> std::vector { return {TextHeight{100}, TextHeight{60}, ui::Dialog::text_height_v}; } -Home::Home(App& app, Services const& services) : Scene(app, services, "Home") { create_ui(); } +MenuScene::MenuScene(App& app, Services const& services) : Scene(app, services, "Home") { create_ui(); } -void Home::create_ui() { +void MenuScene::create_ui() { auto m_header = std::make_unique(get_services()); m_header->text.set_height(TextHeight{100}).set_string("Home"); m_header->text.tint = bave::white_v; @@ -25,7 +25,7 @@ void Home::create_ui() { auto start = std::make_unique(get_services()); start->set_text("start"); start->set_position({0.0f, -200.0f}); - start->callback = [this]() { get_services().get().switch_to(); }; + start->callback = [this]() { get_services().get().switch_to(); }; auto quit = std::make_unique(get_services()); quit->set_text("quit"); diff --git a/src/spaced/spaced/scenes/home.hpp b/src/spaced/spaced/scenes/menu.hpp similarity index 50% rename from src/spaced/spaced/scenes/home.hpp rename to src/spaced/spaced/scenes/menu.hpp index be6028b..bfc7694 100644 --- a/src/spaced/spaced/scenes/home.hpp +++ b/src/spaced/spaced/scenes/menu.hpp @@ -2,13 +2,15 @@ #include namespace spaced { -class Home : public Scene { +class MenuScene : public Scene { public: static auto get_text_heights() -> std::vector; - explicit Home(bave::App& app, Services const& services); + explicit MenuScene(bave::App& app, Services const& services); private: void create_ui(); + + [[nodiscard]] auto get_music_uri() const -> std::string_view final { return "music/menu.mp3"; } }; } // namespace spaced diff --git a/src/spaced/spaced/spaced.cpp b/src/spaced/spaced/spaced.cpp index 1ef724d..3dc3661 100644 --- a/src/spaced/spaced/spaced.cpp +++ b/src/spaced/spaced/spaced.cpp @@ -161,7 +161,7 @@ void Spaced::tick() { m_layout->set_framebuffer_size(get_app().get_framebuffer_size()); if (m_scene_switcher->next_scene) { - m_audio->stop_music(); + switch_track(m_scene->get_music_uri(), m_scene_switcher->next_scene->get_music_uri()); m_scene = std::move(m_scene_switcher->next_scene); } @@ -234,4 +234,12 @@ void Spaced::set_scene() { switcher->switch_to(); m_services.bind(std::move(switcher)); } + +void Spaced::switch_track(std::string_view const from, std::string_view const to) const { + if (to.empty()) { + m_audio->stop_music(); + } else if (from != to) { + m_audio->play_music(to); + } +} } // namespace spaced diff --git a/src/spaced/spaced/spaced.hpp b/src/spaced/spaced/spaced.hpp index c8ac94a..f2645ab 100644 --- a/src/spaced/spaced/spaced.hpp +++ b/src/spaced/spaced/spaced.hpp @@ -30,6 +30,8 @@ class Spaced : public bave::Driver { void set_layout(); void set_scene(); + void switch_track(std::string_view from, std::string_view to) const; + bave::Logger m_log{"Spaced"}; Services m_services{}; bave::Ptr m_layout{}; From 03703f9fc070995f29ba50ecb0d42d25a449bde4 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Thu, 6 Jun 2024 16:53:11 +0530 Subject: [PATCH 2/5] Add `ui::Slider`. --- assets/styles.json | 7 ++ src/spaced/spaced/game/enemy.cpp | 3 +- src/spaced/spaced/services/styles.cpp | 2 + src/spaced/spaced/services/styles.hpp | 1 + src/spaced/spaced/ui/progress_bar.hpp | 6 +- src/spaced/spaced/ui/slider.cpp | 99 +++++++++++++++++++++++++++ src/spaced/spaced/ui/slider.hpp | 46 +++++++++++++ src/spaced/spaced/ui/style.hpp | 6 ++ src/spaced/spaced/util.cpp | 14 ++++ src/spaced/spaced/util.hpp | 3 + 10 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 src/spaced/spaced/ui/slider.cpp create mode 100644 src/spaced/spaced/ui/slider.hpp diff --git a/assets/styles.json b/assets/styles.json index 67571fe..50dd488 100644 --- a/assets/styles.json +++ b/assets/styles.json @@ -55,6 +55,13 @@ "content_text_tint": "#231d2aff" } }, + "sliders": { + "default": { + "progress_bar": "default", + "knob_diameter": 30, + "knob_tint": "#9f2b68ff" + } + }, "loading_screen": { "background_tint": "#231d2aff", "spinner": { diff --git a/src/spaced/spaced/game/enemy.cpp b/src/spaced/spaced/game/enemy.cpp index df89f08..e6e6a39 100644 --- a/src/spaced/spaced/game/enemy.cpp +++ b/src/spaced/spaced/game/enemy.cpp @@ -32,12 +32,11 @@ void Enemy::force_death() { m_health_bar.set_progress(0.0f); } -void Enemy::tick(Seconds const dt, bool const /*in_play*/) { +void Enemy::tick(Seconds const /*dt*/, bool const /*in_play*/) { 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 { diff --git a/src/spaced/spaced/services/styles.cpp b/src/spaced/spaced/services/styles.cpp index bb86e6b..f2065d9 100644 --- a/src/spaced/spaced/services/styles.cpp +++ b/src/spaced/spaced/services/styles.cpp @@ -36,6 +36,7 @@ auto Styles::load(dj::Json const& json) -> Styles { load_styles(ret.buttons, json["buttons"]); load_styles(ret.progress_bars, json["progress_bars"]); load_styles(ret.dialogs, json["dialogs"]); + load_styles(ret.sliders, json["sliders"]); if (auto const& loading_screen = json["loading_screen"]) { from_json(loading_screen, ret.loading_screen); } return ret; } @@ -46,6 +47,7 @@ auto Styles::save() const -> dj::Json { save_styles(buttons, ret, "buttons"); save_styles(progress_bars, ret, "progress_bars"); save_styles(dialogs, ret, "dialogs"); + save_styles(sliders, ret, "sliders"); to_json(ret["loading_screen"], loading_screen); return ret; } diff --git a/src/spaced/spaced/services/styles.hpp b/src/spaced/spaced/services/styles.hpp index 64c42ad..32a8d8a 100644 --- a/src/spaced/spaced/services/styles.hpp +++ b/src/spaced/spaced/services/styles.hpp @@ -13,6 +13,7 @@ struct Styles : IService { StringMap buttons{}; StringMap progress_bars{}; StringMap dialogs{}; + StringMap sliders{}; ui::LoadingScreenStyle loading_screen{}; static auto load(dj::Json const& json) -> Styles; diff --git a/src/spaced/spaced/ui/progress_bar.hpp b/src/spaced/spaced/ui/progress_bar.hpp index 02dd8a9..b86037f 100644 --- a/src/spaced/spaced/ui/progress_bar.hpp +++ b/src/spaced/spaced/ui/progress_bar.hpp @@ -1,11 +1,11 @@ #pragma once #include +#include #include #include -#include namespace spaced::ui { -class ProgressBar : public IWidget { +class ProgressBar : public IElement { public: using Style = ProgressBarStyle; @@ -26,6 +26,8 @@ class ProgressBar : public IWidget { [[nodiscard]] auto get_position() const -> glm::vec2 final { return position; } void set_position(glm::vec2 position) final { this->position = position; } + [[nodiscard]] auto get_bounds() const -> bave::Rect<> { return m_background.get_bounds(); } + private: Style m_style{}; diff --git a/src/spaced/spaced/ui/slider.cpp b/src/spaced/spaced/ui/slider.cpp new file mode 100644 index 0000000..ff68b67 --- /dev/null +++ b/src/spaced/spaced/ui/slider.cpp @@ -0,0 +1,99 @@ +#include +#include +#include + +namespace spaced::ui { +using bave::Action; +using bave::MouseButton; +using bave::PointerId; +using bave::PointerMove; +using bave::PointerTap; +using bave::Rect; +using bave::Shader; + +Slider::Slider(Services const& services) : m_styles(&services.get()), m_progress_bar(services) { + set_style(m_styles->sliders["default"]); + set_value(0.0f); +} + +auto Slider::get_size() const -> glm::vec2 { + auto ret = m_progress_bar.get_size(); + ret.y = m_knob.get_shape().diameter; + return ret; +} + +void Slider::set_position(glm::vec2 const position) { + m_progress_bar.set_position(position); + set_value(m_value); +} + +void Slider::set_value(float const value) { + update_value(std::clamp(value, 0.0f, 1.0f)); + auto const origin = m_progress_bar.get_position(); + auto const size = m_progress_bar.get_size(); + auto const left_x = origin.x - 0.5f * size.x; + auto const delta = m_value * size.x; + + m_knob.transform.position = glm::vec2{left_x + delta, origin.y}; + m_progress_bar.set_progress(m_value); + + if (on_change) { on_change(m_value); } +} + +void Slider::set_style(Style style) { + m_style = std::move(style); + m_knob.tint = m_style.knob_tint; + auto shape = m_knob.get_shape(); + shape.diameter = m_style.knob_diameter; + m_knob.set_shape(shape); + m_progress_bar.set_style(m_styles->progress_bars[m_style.progress_bar]); +} + +void Slider::draw(Shader& shader) const { + m_progress_bar.draw(shader); + m_knob.draw(shader); +} + +void Slider::on_tap(PointerTap const& pointer_tap) { + if (pointer_tap.pointer.id != PointerId::ePrimary || pointer_tap.button != MouseButton::eLeft) { return; } + switch (pointer_tap.action) { + case Action::ePress: m_pointer_down = hit_test(pointer_tap.pointer.position); break; + case Action::eRelease: m_pointer_down = false; break; + default: break; + } + + if (m_pointer_down) { track(pointer_tap.pointer.position); } +} + +void Slider::on_move(PointerMove const& pointer_move) { + if (pointer_move.pointer.id != PointerId::ePrimary) { return; } + if (!m_pointer_down) { return; } + track(pointer_move.pointer.position); +} + +auto Slider::hit_test(glm::vec2 const position) const -> bool { + auto const bar_rect = Rect<>::from_size(m_progress_bar.get_size(), m_progress_bar.get_position()); + if (bar_rect.contains(position)) { return true; } + if (m_knob.get_bounds().contains(position)) { return true; } + return false; +} + +void Slider::track(glm::vec2 const pointer) { + auto const pointer_x = pointer.x; + auto const origin_x = m_progress_bar.get_position().x; + auto const size_x = m_progress_bar.get_size().x; + auto const left_x = origin_x - 0.5f * size_x; + auto const right_x = origin_x + 0.5f * size_x; + auto const knob_x = std::clamp(pointer_x, left_x, right_x); + + m_knob.transform.position.x = knob_x; + update_value((knob_x - left_x) / size_x); + m_progress_bar.set_progress(m_value); +} + +void Slider::update_value(float const value) { + if (std::abs(m_value - value) < 0.001f) { return; } + m_value = value; + if (on_change) { on_change(m_value); } +} +} // namespace spaced::ui diff --git a/src/spaced/spaced/ui/slider.hpp b/src/spaced/spaced/ui/slider.hpp new file mode 100644 index 0000000..dfda708 --- /dev/null +++ b/src/spaced/spaced/ui/slider.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace spaced::ui { +class Slider : public IWidget { + public: + using Style = SliderStyle; + + explicit Slider(Services const& services); + + [[nodiscard]] auto get_size() const -> glm::vec2 final; + [[nodiscard]] auto get_position() const -> glm::vec2 final { return m_progress_bar.get_position(); } + void set_position(glm::vec2 position) final; + + [[nodiscard]] auto get_value() const -> float { return m_value; } + void set_value(float value); + + [[nodiscard]] auto get_style() const -> Style { return m_style; } + void set_style(Style style); + + std::function on_change{}; + + protected: + void draw(bave::Shader& shader) const final; + void on_move(bave::PointerMove const& pointer_move) final; + void on_tap(bave::PointerTap const& pointer_tap) final; + + [[nodiscard]] auto hit_test(glm::vec2 position) const -> bool; + void track(glm::vec2 pointer); + + void update_value(float value); + + bave::NotNull m_styles; + Style m_style{}; + + ui::ProgressBar m_progress_bar; + bave::CircleShape m_knob{}; + float m_value{}; + + bool m_pointer_down{}; +}; +} // namespace spaced::ui diff --git a/src/spaced/spaced/ui/style.hpp b/src/spaced/spaced/ui/style.hpp index 014ef53..f7405b3 100644 --- a/src/spaced/spaced/ui/style.hpp +++ b/src/spaced/spaced/ui/style.hpp @@ -46,4 +46,10 @@ struct DialogStyle { bave::Rgba outline_tint{bave::cyan_v}; bave::Rgba content_text_tint{bave::black_v}; }; + +struct SliderStyle { + std::string progress_bar{}; + float knob_diameter{30.0f}; + bave::Rgba knob_tint{bave::white_v}; +}; } // namespace spaced::ui diff --git a/src/spaced/spaced/util.cpp b/src/spaced/spaced/util.cpp index ce46c37..9886888 100644 --- a/src/spaced/spaced/util.cpp +++ b/src/spaced/spaced/util.cpp @@ -89,6 +89,20 @@ void util::from_json(dj::Json const& json, ui::DialogStyle& out) { from_json(json["content_text_tint"], out.content_text_tint); } +void util::to_json(dj::Json& out, ui::SliderStyle const& slider_style) { + using bave::to_json; + to_json(out["progress_bar"], slider_style.progress_bar); + to_json(out["knob_diameter"], slider_style.knob_diameter); + to_json(out["knob_tint"], slider_style.knob_tint); +} + +void util::from_json(dj::Json const& json, ui::SliderStyle& out) { + using bave::from_json; + from_json(json["progress_bar"], out.progress_bar); + from_json(json["knob_diameter"], out.knob_diameter); + from_json(json["knob_tint"], out.knob_tint); +} + auto util::create_font_atlas_task(std::shared_ptr font, std::vector heights) -> std::function { if (!font || heights.empty()) { return {}; } return [font = std::move(font), heights = std::move(heights)] { diff --git a/src/spaced/spaced/util.hpp b/src/spaced/spaced/util.hpp index be29deb..89bbbb4 100644 --- a/src/spaced/spaced/util.hpp +++ b/src/spaced/spaced/util.hpp @@ -20,5 +20,8 @@ void from_json(dj::Json const& json, ui::ProgressBarStyle& out); void to_json(dj::Json& out, ui::DialogStyle const& dialog_style); void from_json(dj::Json const& json, ui::DialogStyle& out); +void to_json(dj::Json& out, ui::SliderStyle const& slider_style); +void from_json(dj::Json const& json, ui::SliderStyle& out); + auto create_font_atlas_task(std::shared_ptr font, std::vector heights) -> std::function; } // namespace spaced::util From 521abc4d08c14633b60aa17aa0b381a8a09c04ad Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Thu, 13 Jun 2024 14:06:57 +0530 Subject: [PATCH 3/5] Fix mutable `ui::View` iteration: use cached views. --- src/spaced/spaced/scene.cpp | 12 ++++++++++-- src/spaced/spaced/scene.hpp | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/spaced/spaced/scene.cpp b/src/spaced/spaced/scene.cpp index 5628373..a339c55 100644 --- a/src/spaced/spaced/scene.cpp +++ b/src/spaced/spaced/scene.cpp @@ -38,7 +38,7 @@ void Scene::push_view(std::unique_ptr view) { void Scene::tick_frame(Seconds const dt) { tick(dt); - for (auto const& view : m_views) { view->tick(dt); } + for (auto const& view : cache_views()) { view->tick(dt); } std::erase_if(m_views, [](auto const& view) { return view->is_destroyed(); }); } @@ -51,11 +51,19 @@ void Scene::render_frame() const { template auto Scene::on_ui_event(F per_view) -> bool { - for (auto it = m_views.rbegin(); it != m_views.rend(); ++it) { + auto const cached_views = cache_views(); + for (auto it = cached_views.rbegin(); it != cached_views.rend(); ++it) { auto const& view = *it; per_view(*view); if (view->block_input_events) { return true; } } return false; } + +auto Scene::cache_views() -> std::span const> { + m_cached_views.clear(); + m_cached_views.reserve(m_views.size()); + for (auto const& view : m_views) { m_cached_views.push_back(view.get()); } + return m_cached_views; +} } // namespace spaced diff --git a/src/spaced/spaced/scene.hpp b/src/spaced/spaced/scene.hpp index cbf320d..77acfcd 100644 --- a/src/spaced/spaced/scene.hpp +++ b/src/spaced/spaced/scene.hpp @@ -51,8 +51,11 @@ class Scene : public bave::PolyPinned { template auto on_ui_event(F per_view) -> bool; + auto cache_views() -> std::span const>; + bave::App& m_app; Services const& m_services; std::vector> m_views{}; + std::vector> m_cached_views{}; }; } // namespace spaced From 0d6a85cdcfadfe98d72379916cad5f154f0ca5c6 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Thu, 13 Jun 2024 14:07:39 +0530 Subject: [PATCH 4/5] Fix redundant on change callbacks in `ui::Slider`. --- src/spaced/spaced/ui/slider.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/spaced/spaced/ui/slider.cpp b/src/spaced/spaced/ui/slider.cpp index ff68b67..390fab0 100644 --- a/src/spaced/spaced/ui/slider.cpp +++ b/src/spaced/spaced/ui/slider.cpp @@ -36,8 +36,6 @@ void Slider::set_value(float const value) { m_knob.transform.position = glm::vec2{left_x + delta, origin.y}; m_progress_bar.set_progress(m_value); - - if (on_change) { on_change(m_value); } } void Slider::set_style(Style style) { From 041fda138179dafe3f47ccc06faf71a464f1608a Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Thu, 13 Jun 2024 14:39:06 +0530 Subject: [PATCH 5/5] Add `Prefs` and its UI. --- src/spaced/spaced/prefs.cpp | 85 +++++++++++++++++++++++++++++++ src/spaced/spaced/prefs.hpp | 33 ++++++++++++ src/spaced/spaced/scenes/menu.cpp | 10 +++- src/spaced/spaced/spaced.cpp | 8 +++ src/spaced/spaced/spaced.hpp | 1 + 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/spaced/spaced/prefs.cpp create mode 100644 src/spaced/spaced/prefs.hpp diff --git a/src/spaced/spaced/prefs.cpp b/src/spaced/spaced/prefs.cpp new file mode 100644 index 0000000..da68d6d --- /dev/null +++ b/src/spaced/spaced/prefs.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spaced { +using bave::App; +using bave::NotNull; +using bave::Persistor; + +namespace { +constexpr std::string_view uri_v{"spaced/prefs.json"}; +} // namespace + +auto Prefs::load(App const& app) -> Prefs { + auto ret = Prefs{}; + auto const persistor = Persistor{app}; + if (persistor.exists(uri_v)) { + auto const json = persistor.read_json(uri_v); + from_json(json["music_gain"], ret.music_gain); + from_json(json["sfx_gain"], ret.sfx_gain); + } + return ret; +} + +void Prefs::save(App const& app) const { + auto json = dj::Json{}; + to_json(json["music_gain"], music_gain); + to_json(json["sfx_gain"], sfx_gain); + auto const persistor = Persistor{app}; + persistor.write_json(uri_v, json); +} + +Prefs::View::View(NotNull app, Services const& services) + : ui::View(services), m_app(app), m_audio(&services.get()), m_prefs(Prefs::load(*app)) { + auto const& styles = services.get(); + + auto bg = std::make_unique(); + bg->set_size({400.0f, 400.0f}); + bg->set_outline_width(5.0f); + bg->set_corner_ratio(0.1f); + bg->set_tint(styles.rgbas["milk"]); + push(std::move(bg)); + + auto text = std::make_unique(services); + text->text.set_string("music"); + text->set_position({0.0f, 130.0f}); + push(std::move(text)); + + auto slider = std::make_unique(services); + slider->set_value(m_prefs.music_gain); + slider->on_change = [this](float const val) { + m_prefs.music_gain = val; + m_audio->set_music_gain(val); + }; + slider->set_position({0.0f, 100.0f}); + push(std::move(slider)); + + text = std::make_unique(services); + text->text.set_string("sfx"); + text->set_position({0.0f, 30.0f}); + push(std::move(text)); + + slider = std::make_unique(services); + slider->set_value(m_prefs.sfx_gain); + slider->on_change = [this](float const val) { + m_prefs.sfx_gain = val; + m_audio->set_sfx_gain(val); + }; + slider->set_position({0.0f, 0.0f}); + push(std::move(slider)); + + auto button = std::make_unique(services); + button->set_text("close"); + button->callback = [this] { set_destroyed(); }; + button->set_position({0.0f, -100.0f}); + push(std::move(button)); +} + +Prefs::View::~View() { m_prefs.save(*m_app); } +} // namespace spaced diff --git a/src/spaced/spaced/prefs.hpp b/src/spaced/spaced/prefs.hpp new file mode 100644 index 0000000..be0863d --- /dev/null +++ b/src/spaced/spaced/prefs.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +namespace spaced { +class IAudio; + +struct Prefs { + class View; + + float music_gain{1.0f}; + float sfx_gain{1.0f}; + + static auto load(bave::App const& app) -> Prefs; + void save(bave::App const& app) const; +}; + +class Prefs::View : public ui::View { + public: + View(View const&) = delete; + View(View&&) = delete; + auto operator=(View const&) = delete; + auto operator=(View&&) = delete; + + explicit View(bave::NotNull app, Services const& services); + ~View() override; + + private: + bave::NotNull m_app; + bave::NotNull m_audio; + Prefs m_prefs; +}; +} // namespace spaced diff --git a/src/spaced/spaced/scenes/menu.cpp b/src/spaced/spaced/scenes/menu.cpp index d65db83..650c229 100644 --- a/src/spaced/spaced/scenes/menu.cpp +++ b/src/spaced/spaced/scenes/menu.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -20,13 +21,19 @@ MenuScene::MenuScene(App& app, Services const& services) : Scene(app, services, void MenuScene::create_ui() { auto m_header = std::make_unique(get_services()); m_header->text.set_height(TextHeight{100}).set_string("Home"); + m_header->set_position({0.0f, 100.0f}); m_header->text.tint = bave::white_v; auto start = std::make_unique(get_services()); start->set_text("start"); - start->set_position({0.0f, -200.0f}); + start->set_position({0.0f, -100.0f}); start->callback = [this]() { get_services().get().switch_to(); }; + auto options = std::make_unique(get_services()); + options->set_text("options"); + options->set_position({0.0f, -250.0f}); + options->callback = [this] { push_view(std::make_unique(&get_app(), get_services())); }; + auto quit = std::make_unique(get_services()); quit->set_text("quit"); quit->set_position({0.0f, -400.0f}); @@ -35,6 +42,7 @@ void MenuScene::create_ui() { auto view = std::make_unique(get_services()); view->push(std::move(m_header)); view->push(std::move(start)); + view->push(std::move(options)); view->push(std::move(quit)); push_view(std::move(view)); } diff --git a/src/spaced/spaced/spaced.cpp b/src/spaced/spaced/spaced.cpp index 3dc3661..21ddd17 100644 --- a/src/spaced/spaced/spaced.cpp +++ b/src/spaced/spaced/spaced.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,7 @@ Spaced::Spaced(App& app) : Driver(app), m_scene(std::make_unique(app, m_s load_resources(); set_layout(); create_services(); + set_prefs(); set_scene(); } @@ -228,6 +230,12 @@ void Spaced::create_services() { m_services.bind(std::move(stats)); } +void Spaced::set_prefs() { + auto const prefs = Prefs::load(get_app()); + m_audio->set_music_gain(prefs.music_gain); + m_audio->set_sfx_gain(prefs.sfx_gain); +} + void Spaced::set_scene() { auto switcher = std::make_unique(get_app(), m_services); m_scene_switcher = switcher.get(); diff --git a/src/spaced/spaced/spaced.hpp b/src/spaced/spaced/spaced.hpp index f2645ab..42de8b9 100644 --- a/src/spaced/spaced/spaced.hpp +++ b/src/spaced/spaced/spaced.hpp @@ -28,6 +28,7 @@ class Spaced : public bave::Driver { void load_resources(); void create_services(); void set_layout(); + void set_prefs(); void set_scene(); void switch_track(std::string_view from, std::string_view to) const;