diff --git a/.gitignore b/.gitignore index ca09e17..0bb7d4e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ imgui.ini bave*.log /spaced /notes.txt +/local_store diff --git a/CMakeLists.txt b/CMakeLists.txt index 88c1028..a5c5a1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ include(FetchContent) FetchContent_Declare( bgf GIT_REPOSITORY https://github.com/karnkaul/bgf - GIT_TAG v0.1.0 + GIT_TAG v0.1.3 SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/bgf" ) diff --git a/assets/images/background.png b/assets/images/background.png new file mode 100644 index 0000000..d9c3fd4 Binary files /dev/null and b/assets/images/background.png differ diff --git a/assets/images/player_ship.png b/assets/images/player_ship.png new file mode 100644 index 0000000..a465e7e Binary files /dev/null and b/assets/images/player_ship.png differ diff --git a/assets/particles/exhaust.json b/assets/particles/exhaust.json index fbf22b1..9d1a938 100644 --- a/assets/particles/exhaust.json +++ b/assets/particles/exhaust.json @@ -33,8 +33,8 @@ }, "lerp": { "tint": { - "lo": "#231d2aff", - "hi": "#231d2aff" + "lo": "#f48018ff", + "hi": "#ffffff00" }, "scale": { "lo": [ @@ -48,14 +48,14 @@ } }, "ttl": { - "lo": 2.000000, - "hi": 3.000000 + "lo": 0.300000, + "hi": 1.000000 }, "quad_size": [ - 80.000000, - 80.000000 + 20.000000, + 20.000000 ], - "count": 80, + "count": 200, "respawn": true } } \ No newline at end of file diff --git a/assets/styles.json b/assets/styles.json index 06607c0..fd3ff52 100644 --- a/assets/styles.json +++ b/assets/styles.json @@ -58,7 +58,7 @@ "sliders": { "default": { "progress_bar": "default", - "knob_diameter": 30, + "knob_diameter": 50, "knob_tint": "#9f2b68ff" } }, diff --git a/attribution.txt b/attribution.txt new file mode 100644 index 0000000..f4d3390 --- /dev/null +++ b/attribution.txt @@ -0,0 +1 @@ +SpaceShooterRedux: https://opengameart.org/content/space-shooter-redux, https://www.kenney.nl diff --git a/src/spaced/spaced/assets/asset_list.cpp b/src/spaced/spaced/assets/asset_list.cpp deleted file mode 100644 index c7edeb9..0000000 --- a/src/spaced/spaced/assets/asset_list.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include - -namespace spaced { -using bave::AsyncExec; -using bave::Loader; -using bave::Resources; -using bave::Services; - -AssetList::AssetList(Loader loader, Services const& services) : m_loader(std::move(loader)), m_resources(&services.get()) {} - -auto AssetList::add_texture(std::string uri, bool const mip_map) -> AssetList& { - if (uri.empty()) { return *this; } - m_textures.insert(Tex{.uri = std::move(uri), .mip_map = mip_map}); - return *this; -} - -auto AssetList::add_font(std::string uri) -> AssetList& { - if (uri.empty()) { return *this; } - m_fonts.insert(std::move(uri)); - return *this; -} - -auto AssetList::add_particle_emitter(std::string uri) -> AssetList& { - if (uri.empty()) { return *this; } - - auto const json = m_loader.load_json(uri); - if (!json) { return *this; } - - // emitters require textures (stage 0) to be loaded, and must be loaded in stage 1 - if (auto const& texture = json["texture"]) { add_texture(texture.as()); } - m_emitters.insert(std::move(uri)); - return *this; -} - -auto AssetList::add_audio_clip(std::string uri) -> AssetList& { - if (uri.empty()) { return *this; } - m_audio_clips.insert(std::move(uri)); - return *this; -} - -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 { - auto ret = std::vector{}; - ret.reserve(2); - auto asset_loader = AssetLoader{m_loader, m_resources}; - ret.push_back(build_stage_0(asset_loader)); - ret.push_back(build_stage_1(asset_loader)); - return ret; -} - -auto AssetList::build_stage_0(AssetLoader& asset_loader) const -> AsyncExec::Stage { - auto ret = AsyncExec::Stage{}; - for (auto const& texture : m_textures) { ret.push_back(asset_loader.make_load_texture(texture.uri, texture.mip_map)); } - for (auto const& font : m_fonts) { ret.push_back(asset_loader.make_load_font(font)); } - for (auto const& audio_clip : m_audio_clips) { ret.push_back(asset_loader.make_load_audio_clip(audio_clip)); } - return ret; -} - -auto AssetList::build_stage_1(AssetLoader& asset_loader) const -> AsyncExec::Stage { - auto ret = AsyncExec::Stage{}; - for (auto const& emitter : m_emitters) { ret.push_back(asset_loader.make_load_particle_emitter(emitter)); } - return ret; -} -} // namespace spaced diff --git a/src/spaced/spaced/assets/asset_list.hpp b/src/spaced/spaced/assets/asset_list.hpp deleted file mode 100644 index a2debc1..0000000 --- a/src/spaced/spaced/assets/asset_list.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace bave { -struct Resources; -} - -namespace spaced { -class AssetLoader; - -class AssetList { - public: - explicit AssetList(bave::Loader loader, bave::Services const& services); - - auto add_texture(std::string uri, bool mip_map = false) -> AssetList&; - auto add_font(std::string uri) -> AssetList&; - auto add_particle_emitter(std::string uri) -> AssetList&; - auto add_audio_clip(std::string uri) -> AssetList&; - - void add_manifest(AssetManifest manifest); - - [[nodiscard]] auto build_task_stages() const -> std::vector; - - private: - struct Tex { - std::string uri{}; - bool mip_map{}; - - // MacOS doesn't provide operator<=> for strings :/ - auto operator==(Tex const& rhs) const -> bool { return uri == rhs.uri; } - auto operator<(Tex const& rhs) const -> bool { return uri < rhs.uri; } - }; - - auto build_stage_0(AssetLoader& asset_loader) const -> bave::AsyncExec::Stage; - auto build_stage_1(AssetLoader& asset_loader) const -> bave::AsyncExec::Stage; - - bave::Loader m_loader; - bave::NotNull m_resources; - - std::set m_textures{}; - std::set m_fonts{}; - std::set m_emitters{}; - std::set m_audio_clips{}; -}; -} // namespace spaced diff --git a/src/spaced/spaced/assets/asset_loader.cpp b/src/spaced/spaced/assets/asset_loader.cpp deleted file mode 100644 index c995b61..0000000 --- a/src/spaced/spaced/assets/asset_loader.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include -#include -#include - -namespace spaced { -using bave::Loader; -using bave::Logger; -using bave::NotNull; -using bave::ParticleEmitter; -using bave::Resources; -using bave::Texture; - -struct AssetLoader::Impl { - Logger log{"AssetLoader"}; - Loader loader; - NotNull resources; - std::mutex mutex{}; -}; - -AssetLoader::AssetLoader(Loader loader, NotNull resources) : m_impl(new Impl{.loader = std::move(loader), .resources = resources}) {} - -auto AssetLoader::make_load_font(std::string uri, bool reload) -> LoadTask { - auto const load = [](Loader const& loader, std::string_view const uri) { return loader.load_font(uri); }; - return make_load_task(std::move(uri), reload, load); -} - -auto AssetLoader::make_load_texture(std::string uri, bool mip_map, bool reload) -> LoadTask { - auto const load = [mip_map](Loader const& loader, std::string_view const uri) { return loader.load_texture(uri, mip_map); }; - return make_load_task(std::move(uri), reload, load); -} - -auto AssetLoader::make_load_texture_atlas(std::string uri, bool mip_map, bool reload) -> LoadTask { - auto const load = [mip_map](Loader const& loader, std::string_view const uri) { return loader.load_texture_atlas(uri, mip_map); }; - return make_load_task(std::move(uri), reload, load); -} - -auto AssetLoader::make_load_particle_emitter(std::string uri, bool const reload) -> LoadTask { - auto const load = [impl = m_impl.get()](Loader const& loader, std::string_view const uri) -> std::shared_ptr { - auto const json = loader.load_json(uri); - if (!json) { return {}; } - - auto ret = std::make_shared(); - bave::from_json(json["config"], ret->config); - if (auto const& texture = json["texture"]) { - auto lock = std::scoped_lock{impl->mutex}; - ret->set_texture(impl->resources->get(texture.as_string())); - } - - impl->log.info("loaded ParticleEmitter: '{}'", uri); - return ret; - }; - return make_load_task(std::move(uri), reload, load); -} - -auto AssetLoader::make_load_audio_clip(std::string uri, bool const reload) -> LoadTask { - auto const load = [](Loader const& loader, std::string_view const uri) { return loader.load_audio_clip(uri); }; - return make_load_task(std::move(uri), reload, load); -} - -template -auto AssetLoader::make_load_task(std::string uri, bool reload, FuncT load) const -> LoadTask { - return [impl = m_impl, uri = std::move(uri), reload, load] { - auto lock = std::unique_lock{impl->mutex}; - if (!reload && impl->resources->contains(uri)) { return; } - - lock.unlock(); - auto asset = load(impl->loader, uri); - if (!asset) { return; } - - lock.lock(); - impl->resources->add(uri, std::move(asset)); - }; -} -} // namespace spaced diff --git a/src/spaced/spaced/assets/asset_loader.hpp b/src/spaced/spaced/assets/asset_loader.hpp deleted file mode 100644 index 22c9bf4..0000000 --- a/src/spaced/spaced/assets/asset_loader.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace spaced { -class AssetLoader { - public: - using LoadTask = std::function; - - explicit AssetLoader(bave::Loader loader, bave::NotNull resources); - - [[nodiscard]] auto make_load_font(std::string uri, bool reload = false) -> LoadTask; - [[nodiscard]] auto make_load_texture(std::string uri, bool mip_map = false, bool reload = false) -> LoadTask; - [[nodiscard]] auto make_load_texture_atlas(std::string uri, bool mip_map = false, bool reload = false) -> LoadTask; - [[nodiscard]] auto make_load_particle_emitter(std::string uri, bool reload = false) -> LoadTask; - [[nodiscard]] auto make_load_audio_clip(std::string uri, bool reload = false) -> LoadTask; - - private: - template - auto make_load_task(std::string uri, bool reload, FuncT load) const -> LoadTask; - - struct Impl; - std::shared_ptr m_impl{}; -}; -} // namespace spaced diff --git a/src/spaced/spaced/assets/asset_manifest.hpp b/src/spaced/spaced/assets/asset_manifest.hpp deleted file mode 100644 index 471c73f..0000000 --- a/src/spaced/spaced/assets/asset_manifest.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#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/game/controllers/player_controller.cpp b/src/spaced/spaced/game/controllers/player_controller.cpp index 8fcae45..18d1e1a 100644 --- a/src/spaced/spaced/game/controllers/player_controller.cpp +++ b/src/spaced/spaced/game/controllers/player_controller.cpp @@ -68,8 +68,9 @@ void PlayerController::stop_firing() { } auto PlayerController::is_in_move_area(glm::vec2 const position) const -> bool { - auto const n_pos = position.x / m_display->get_world_space().x; - return n_pos <= -n_move_area; + auto const width = m_display->get_world_space().x; + auto const n_pos = (position.x + 0.5f * width) / width; + return n_pos <= n_move_area; } void PlayerController::tick_gamepad(Seconds const dt) { diff --git a/src/spaced/spaced/game/controllers/player_controller.hpp b/src/spaced/spaced/game/controllers/player_controller.hpp index 72f0c2c..ec4726a 100644 --- a/src/spaced/spaced/game/controllers/player_controller.hpp +++ b/src/spaced/spaced/game/controllers/player_controller.hpp @@ -33,7 +33,7 @@ class PlayerController : public FollowController { float max_y{}; float min_y{}; - float n_move_area{0.25f}; // from left + float n_move_area{0.3f}; // from left float gamepad_sensitivity{2000.0f}; diff --git a/src/spaced/spaced/game/enemy.cpp b/src/spaced/spaced/game/enemy.cpp index 77cd6d3..048a9d4 100644 --- a/src/spaced/spaced/game/enemy.cpp +++ b/src/spaced/spaced/game/enemy.cpp @@ -13,8 +13,6 @@ using bave::Services; using bave::Shader; using bave::Styles; -namespace ui = bave::ui; - 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->play_area; diff --git a/src/spaced/spaced/game/hud.cpp b/src/spaced/spaced/game/hud.cpp index 2a6d3c5..848ca1f 100644 --- a/src/spaced/spaced/game/hud.cpp +++ b/src/spaced/spaced/game/hud.cpp @@ -1,20 +1,25 @@ #include #include +#include #include #include namespace spaced { +using bave::IDisplay; +using bave::Seconds; using bave::Services; using bave::Styles; using bave::TextHeight; namespace ui = bave::ui; -Hud::Hud(Services const& services) : ui::View(services), m_styles(&services.get()), m_area(services.get().hud_area) { +Hud::Hud(Services const& services) + : ui::View(services), m_display(&services.get()), m_layout(&services.get()), m_styles(&services.get()) { create_background(); create_score(services); block_input_events = false; + render_view = m_display->get_world_view(); } void Hud::set_score(std::int64_t const score) { m_score->text.set_string(fmt::format("{}", score)); } @@ -25,8 +30,8 @@ void Hud::create_background() { auto background = std::make_unique(); m_background = background.get(); background->set_outline_width(0.0f); - background->set_size(m_area.size()); - background->set_position(m_area.centre()); + background->set_size(m_layout->hud_area.size()); + background->set_position(m_layout->hud_area.centre()); background->set_tint(m_styles->rgbas["milk"]); push(std::move(background)); } @@ -37,7 +42,7 @@ void Hud::create_score(Services const& services) { auto make_text = [&] { auto text = std::make_unique(services); text->text.set_height(TextHeight{60}); - text->text.transform.position = m_area.centre(); + text->text.transform.position = m_layout->hud_area.centre(); text->text.tint = rgbas["grey"]; return text; }; @@ -45,16 +50,16 @@ void Hud::create_score(Services const& services) { auto text = make_text(); m_score = text.get(); text->text.set_string("9999999999"); - auto const text_bounds_size = text->text.get_bounds().size(); - text->text.transform.position.y -= 0.5f * text_bounds_size.y; + m_text_bounds_size = text->text.get_bounds().size(); + text->text.transform.position.y -= 0.5f * m_text_bounds_size.y; set_score(0); push(std::move(text)); text = make_text(); m_hi_score = text.get(); - text->text.transform.position.x = m_area.rb.x - 50.0f; - text->text.transform.position.y -= 0.5f * text_bounds_size.y; + text->text.transform.position.x = m_layout->hud_area.rb.x - 50.0f; + text->text.transform.position.y -= 0.5f * m_text_bounds_size.y; text->text.set_align(bave::Text::Align::eLeft); set_hi_score(0); diff --git a/src/spaced/spaced/game/hud.hpp b/src/spaced/spaced/game/hud.hpp index bc26dc3..25dadab 100644 --- a/src/spaced/spaced/game/hud.hpp +++ b/src/spaced/spaced/game/hud.hpp @@ -5,6 +5,8 @@ #include namespace spaced { +struct Layout; + class Hud : public bave::ui::View { public: explicit Hud(bave::Services const& services); @@ -16,8 +18,10 @@ class Hud : public bave::ui::View { void create_background(); void create_score(bave::Services const& services); + bave::NotNull m_display; + bave::NotNull m_layout; bave::NotNull m_styles; - bave::Rect<> m_area{}; + glm::vec2 m_text_bounds_size{}; bave::Ptr m_background{}; bave::Ptr m_score{}; diff --git a/src/spaced/spaced/game/player.cpp b/src/spaced/spaced/game/player.cpp index 79eb349..0f60cc9 100644 --- a/src/spaced/spaced/game/player.cpp +++ b/src/spaced/spaced/game/player.cpp @@ -14,26 +14,23 @@ namespace spaced { using bave::ParticleEmitter; using bave::PointerMove; using bave::PointerTap; +using bave::Rect; using bave::Resources; -using bave::RoundedQuad; using bave::Seconds; using bave::Services; using bave::Shader; -using bave::Styles; +using bave::Texture; Player::Player(Services const& services, std::unique_ptr controller) : m_services(&services), m_stats(&services.get()), m_controller(std::move(controller)) { auto const& layout = services.get(); ship.transform.position.x = layout.player_x; - auto rounded_quad = RoundedQuad{}; - rounded_quad.size = layout.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 texture = services.get().get("images/player_ship.png")) { ship.set_texture(texture); } + ship.set_auto_size(ship_size); + if (auto const exhaust = resources.get("particles/exhaust.json")) { m_exhaust = *exhaust; } m_exhaust.set_position(get_exhaust_position()); m_exhaust.pre_warm(); @@ -59,8 +56,9 @@ void Player::tick(State const& state, Seconds const dt) { auto const y_position = m_controller->tick(dt); set_y(y_position); + auto const hitbox = Rect<>::from_size(hitbox_size, ship.transform.position); for (auto const& target : state.targets) { - if (is_intersecting(target->get_bounds(), ship.get_bounds())) { + if (is_intersecting(target->get_bounds(), hitbox)) { on_death(dt); target->force_death(); return; diff --git a/src/spaced/spaced/game/player.hpp b/src/spaced/spaced/game/player.hpp index 015ca75..1bc6724 100644 --- a/src/spaced/spaced/game/player.hpp +++ b/src/spaced/spaced/game/player.hpp @@ -43,7 +43,9 @@ class Player : public bave::IDrawable { if constexpr (bave::debug_v) { do_inspect(); } } - bave::RoundedQuadShape ship{}; + bave::Sprite ship{}; + glm::vec2 ship_size{100.0f}; + glm::vec2 hitbox_size{75.0f}; Health health{}; private: diff --git a/src/spaced/spaced/game/tiled_bg.cpp b/src/spaced/spaced/game/tiled_bg.cpp new file mode 100644 index 0000000..3da9238 --- /dev/null +++ b/src/spaced/spaced/game/tiled_bg.cpp @@ -0,0 +1,55 @@ +#include + +namespace spaced { +using bave::RenderInstance; +using bave::Seconds; +using bave::Services; +using bave::Shader; + +TiledBg::TiledBg(Services const& services) : m_layout(&services.get()) {} + +void TiledBg::set_tile_size(glm::vec2 const size) { + auto shape = m_quad.get_shape(); + shape.size = size; + m_quad.set_shape(shape); + + auto const cols = static_cast(m_layout->world_space.x / size.x) + 1; + auto const rows = static_cast(m_layout->world_space.y / size.y) + 1; + auto const lt = 0.5f * glm::vec2{-m_layout->world_space.x + size.x, m_layout->world_space.y - size.y}; + + m_exit_x = lt.x - size.x; + + m_quad.instances.clear(); + auto instance = RenderInstance{}; + instance.transform.position = lt; + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols + 1; ++col) { + m_quad.instances.push_back(instance); + instance.transform.position.x += size.x; + } + instance.transform.position.x = lt.x; + instance.transform.position.y -= size.y; + } +} + +void TiledBg::tick(Seconds const dt) { + if (x_speed <= 0.0f) { return; } + + auto const dx = x_speed * dt.count(); + auto max_x = 0.0f; + for (auto& instance : m_quad.instances) { + instance.transform.position.x -= dx; + max_x = std::max(max_x, instance.transform.position.x); + } + for (auto& instance : m_quad.instances) { + if (instance.transform.position.x <= m_exit_x) { instance.transform.position.x = max_x + m_quad.get_shape().size.x; } + } +} + +void TiledBg::draw(Shader& shader) const { + static bool s_wf{false}; + if (s_wf) { shader.polygon_mode = vk::PolygonMode::eLine; } + m_quad.draw(shader); + if (s_wf) { shader.polygon_mode = vk::PolygonMode::eFill; } +} +} // namespace spaced diff --git a/src/spaced/spaced/game/tiled_bg.hpp b/src/spaced/spaced/game/tiled_bg.hpp new file mode 100644 index 0000000..1cc9f56 --- /dev/null +++ b/src/spaced/spaced/game/tiled_bg.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace spaced { +class TiledBg { + public: + explicit TiledBg(bave::Services const& services); + + void set_tile_size(glm::vec2 size); + void set_texture(std::shared_ptr texture) { m_quad.set_texture(std::move(texture)); } + + void tick(bave::Seconds dt); + void draw(bave::Shader& shader) const; + + float x_speed{10.0f}; + + private: + bave::NotNull m_layout; + bave::Instanced m_quad{}; + + float m_exit_x{}; +}; +} // namespace spaced diff --git a/src/spaced/spaced/game/world.cpp b/src/spaced/spaced/game/world.cpp index 4607c08..2cd6d08 100644 --- a/src/spaced/spaced/game/world.cpp +++ b/src/spaced/spaced/game/world.cpp @@ -19,6 +19,7 @@ using bave::Resources; using bave::Seconds; using bave::Services; using bave::Shader; +using bave::Texture; namespace { [[nodiscard]] auto make_player_controller(Services const& services) { @@ -39,14 +40,19 @@ namespace { World::World(bave::NotNull services, bave::NotNull scorer) : player(*services, make_player_controller(*services)), m_services(services), m_resources(&services->get()), m_audio(&services->get()), - m_stats(&services->get()), m_scorer(scorer) { + m_stats(&services->get()), m_scorer(scorer), m_background(*services) { m_enemy_factories["CreepFactory"] = std::make_unique(services); + + m_background.set_texture(services->get().get("images/background.png")); + m_background.set_tile_size(glm::vec2{300.0f}); + m_background.x_speed = 50.0f; } void World::tick(Seconds const dt) { bool const in_play = !player.health.is_dead(); if (in_play) { + m_background.tick(dt); for (auto& [_, factory] : m_enemy_factories) { if (auto enemy = factory->tick(dt)) { m_active_enemies.push_back(std::move(enemy)); } } @@ -74,6 +80,7 @@ void World::tick(Seconds const dt) { } void World::draw(Shader& shader) const { + m_background.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); } diff --git a/src/spaced/spaced/game/world.hpp b/src/spaced/spaced/game/world.hpp index 24720c9..3b581bf 100644 --- a/src/spaced/spaced/game/world.hpp +++ b/src/spaced/spaced/game/world.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace bave { struct Resources; @@ -41,6 +42,8 @@ class World : public ITargetProvider { bave::NotNull m_stats; bave::NotNull m_scorer; + TiledBg m_background; + std::unordered_map> m_enemy_factories{}; std::vector> m_active_enemies{}; diff --git a/src/spaced/spaced/prefs.cpp b/src/spaced/spaced/prefs.cpp index 223346f..bd8929b 100644 --- a/src/spaced/spaced/prefs.cpp +++ b/src/spaced/spaced/prefs.cpp @@ -45,7 +45,7 @@ Prefs::View::View(NotNull app, Services const& services) auto const& styles = services.get(); auto bg = std::make_unique(); - bg->set_size({400.0f, 400.0f}); + bg->set_size({600.0f, 600.0f}); bg->set_outline_width(5.0f); bg->set_corner_ratio(0.1f); bg->set_tint(styles.rgbas["milk"]); @@ -53,7 +53,7 @@ Prefs::View::View(NotNull app, Services const& services) auto text = std::make_unique(services); text->text.set_string("music"); - text->set_position({0.0f, 130.0f}); + text->set_position({0.0f, 230.0f}); push(std::move(text)); auto slider = std::make_unique(services); @@ -62,12 +62,12 @@ Prefs::View::View(NotNull app, Services const& services) m_prefs.music_gain = val; m_audio->set_music_gain(val); }; - slider->set_position({0.0f, 100.0f}); + slider->set_position({0.0f, 200.0f}); push(std::move(slider)); text = std::make_unique(services); text->text.set_string("sfx"); - text->set_position({0.0f, 30.0f}); + text->set_position({0.0f, 130.0f}); push(std::move(text)); slider = std::make_unique(services); @@ -76,13 +76,13 @@ Prefs::View::View(NotNull app, Services const& services) m_prefs.sfx_gain = val; m_audio->set_sfx_gain(val); }; - slider->set_position({0.0f, 0.0f}); + slider->set_position({0.0f, 100.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}); + button->set_position({0.0f, -200.0f}); push(std::move(button)); } diff --git a/src/spaced/spaced/scenes/game.cpp b/src/spaced/spaced/scenes/game.cpp index 5ab679d..8feaa80 100644 --- a/src/spaced/spaced/scenes/game.cpp +++ b/src/spaced/spaced/scenes/game.cpp @@ -1,10 +1,11 @@ +#include +#include #include #include #include #include #include #include -#include #include #include #include @@ -12,6 +13,9 @@ namespace spaced { using bave::Action; using bave::App; +using bave::AssetList; +using bave::AssetManifest; +using bave::AsyncExec; using bave::FocusChange; using bave::im_text; using bave::Key; @@ -27,12 +31,17 @@ using bave::Styles; namespace ui = bave::ui; -auto GameScene::get_manifest() -> AssetManifest { +namespace { +auto get_manifest() -> AssetManifest { return AssetManifest{ + .textures = + { + "images/player_ship.png", + "images/background.png", + }, .audio_clips = { "sfx/bubble.wav", - "music/menu.mp3", "music/game.mp3", }, .particle_emitters = @@ -43,9 +52,21 @@ auto GameScene::get_manifest() -> AssetManifest { }, }; } +} // namespace + +GameScene::GameScene(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app) { clear_colour = services.get().rgbas["mocha"]; } + +auto GameScene::build_load_stages() -> std::vector { + auto ret = std::vector{}; + auto asset_list = AssetList{make_loader(), get_services()}; + asset_list.add_manifest(get_manifest()); + ret.push_back(asset_list.build_load_stage()); + return ret; +} -GameScene::GameScene(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app), m_world(&services, this) { - clear_colour = services.get().rgbas["mocha"]; +void GameScene::on_loaded() { + auto const& services = get_services(); + m_world.emplace(&services, this); auto hud = std::make_unique(services); m_hud = hud.get(); @@ -53,28 +74,30 @@ GameScene::GameScene(App& app, Services const& services) : Scene(app, services, push_view(std::move(hud)); ++services.get().game.play_count; + + switch_track("music/game.mp3"); } -void GameScene::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 GameScene::on_key(KeyInput const& key_input) { if (key_input.key == Key::eEscape && key_input.action == Action::eRelease && key_input.mods == KeyMods{}) { get_switcher().switch_to(); } } -void GameScene::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 GameScene::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 GameScene::tick(Seconds const dt) { auto ft = bave::DeltaTime{}; - m_world.tick(dt); - if (m_world.player.health.is_dead() && !m_game_over_dialog_pushed) { on_game_over(); } + m_world->tick(dt); + if (m_world->player.health.is_dead() && !m_game_over_dialog_pushed) { on_game_over(); } if constexpr (bave::debug_v) { inspect(dt, ft.update()); } } -void GameScene::render(Shader& shader) const { m_world.draw(shader); } +void GameScene::render(Shader& shader) const { m_world->draw(shader); } void GameScene::add_score(std::int64_t const score) { m_score += score; @@ -107,7 +130,7 @@ void GameScene::inspect(Seconds const dt, Seconds const frame_time) { if (ImGui::Begin("Debug")) { if (ImGui::BeginTabBar("World")) { - m_world.inspect(); + m_world->inspect(); ImGui::EndTabBar(); } @@ -115,7 +138,7 @@ void GameScene::inspect(Seconds const dt, Seconds const frame_time) { im_text("score: {}", get_score()); ImGui::Separator(); - if (ImGui::Button("end game")) { m_world.player.on_death({}); } + if (ImGui::Button("end game")) { m_world->player.on_death({}); } ImGui::Separator(); im_text("dt: {:05.2f}", std::chrono::duration(dt).count()); diff --git a/src/spaced/spaced/scenes/game.hpp b/src/spaced/spaced/scenes/game.hpp index 4566bb2..8a58843 100644 --- a/src/spaced/spaced/scenes/game.hpp +++ b/src/spaced/spaced/scenes/game.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include #include @@ -10,11 +9,12 @@ namespace spaced { class GameScene : public bave::Scene, public IScorer { public: - static auto get_manifest() -> AssetManifest; - GameScene(bave::App& app, bave::Services const& services); private: + auto build_load_stages() -> std::vector final; + 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; @@ -23,8 +23,6 @@ class GameScene : public bave::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(); @@ -34,7 +32,7 @@ class GameScene : public bave::Scene, public IScorer { void inspect(bave::Seconds dt, bave::Seconds frame_time); GameSave m_save; - World m_world; + std::optional m_world{}; std::int64_t m_score{}; bave::Ptr m_hud{}; bool m_game_over_dialog_pushed{}; diff --git a/src/spaced/spaced/scenes/load_assets.cpp b/src/spaced/spaced/scenes/load_assets.cpp deleted file mode 100644 index 5482362..0000000 --- a/src/spaced/spaced/scenes/load_assets.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace spaced { -using bave::App; -using bave::AsyncExec; -using bave::Loader; -using bave::Resources; -using bave::Services; - -namespace { -auto make_load_stages(Loader loader, Services const& services) -> std::vector { - auto asset_list = AssetList{std::move(loader), services}; - 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, MenuScene::get_text_heights())); - return ret; -} -} // namespace - -LoadAssets::LoadAssets(App& app, Services const& services) : Scene(app, services, "LoadAssets") {} - -auto LoadAssets::build_load_stages() -> std::vector { return make_load_stages(make_loader(), get_services()); } - -void LoadAssets::on_loaded() { get_switcher().switch_to(); } -} // namespace spaced diff --git a/src/spaced/spaced/scenes/load_assets.hpp b/src/spaced/spaced/scenes/load_assets.hpp deleted file mode 100644 index 4a0f265..0000000 --- a/src/spaced/spaced/scenes/load_assets.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include - -namespace spaced { -class LoadAssets : public bave::Scene { - public: - explicit LoadAssets(bave::App& app, bave::Services const& services); - - private: - auto build_load_stages() -> std::vector final; - void on_loaded() final; -}; -} // namespace spaced diff --git a/src/spaced/spaced/scenes/menu.cpp b/src/spaced/spaced/scenes/menu.cpp index d1d5d83..41a4b36 100644 --- a/src/spaced/spaced/scenes/menu.cpp +++ b/src/spaced/spaced/scenes/menu.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,15 +12,28 @@ namespace spaced { using bave::App; +using bave::AssetList; +using bave::AsyncExec; using bave::Seconds; using bave::Services; using bave::TextHeight; namespace ui = bave::ui; -auto MenuScene::get_text_heights() -> std::vector { return {TextHeight{100}, TextHeight{60}, ui::Dialog::text_height_v}; } +MenuScene::MenuScene(App& app, Services const& services) : Scene(app, services, "Home") {} -MenuScene::MenuScene(App& app, Services const& services) : Scene(app, services, "Home") { create_ui(); } +auto MenuScene::build_load_stages() -> std::vector { + auto ret = std::vector{}; + auto asset_list = AssetList{make_loader(), get_services()}; + asset_list.add_audio_clip("music/menu.mp3"); + ret.push_back(asset_list.build_load_stage()); + return ret; +} + +void MenuScene::on_loaded() { + switch_track("music/menu.mp3"); + create_ui(); +} void MenuScene::create_ui() { auto m_header = std::make_unique(get_services()); diff --git a/src/spaced/spaced/scenes/menu.hpp b/src/spaced/spaced/scenes/menu.hpp index ec5e437..5d5ea94 100644 --- a/src/spaced/spaced/scenes/menu.hpp +++ b/src/spaced/spaced/scenes/menu.hpp @@ -4,13 +4,12 @@ namespace spaced { class MenuScene : public bave::Scene { public: - static auto get_text_heights() -> std::vector; - explicit MenuScene(bave::App& app, bave::Services const& services); private: - void create_ui(); + auto build_load_stages() -> std::vector final; + void on_loaded() final; - [[nodiscard]] auto get_music_uri() const -> std::string_view final { return "music/menu.mp3"; } + void create_ui(); }; } // namespace spaced diff --git a/src/spaced/spaced/spaced.cpp b/src/spaced/spaced/spaced.cpp index 7db77e3..a0fde84 100644 --- a/src/spaced/spaced/spaced.cpp +++ b/src/spaced/spaced/spaced.cpp @@ -6,31 +6,27 @@ #include #include #include -#include +#include #include #include #include #include #include +#include namespace spaced { namespace { using bave::App; -using bave::AudioClip; -using bave::AudioDevice; -using bave::AudioStreamer; +using bave::GameDriver; using bave::Gamepad; using bave::IAudio; using bave::IDisplay; -using bave::Loader; using bave::NotNull; using bave::Persistor; using bave::Rect; -using bave::RenderDevice; -using bave::RenderView; -using bave::Resources; using bave::Seconds; using bave::Styles; +using bave::TextHeight; struct GamepadProvider : IGamepadProvider { bave::App& app; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -64,39 +60,42 @@ struct PersistentStats : Stats { persistor.write_json(uri_v, json); } }; + +constexpr auto preload_text_heights_v = std::array{TextHeight{100}, TextHeight{60}}; + +constexpr auto gdci_v = bave::GameDriver::CreateInfo{ + .assets = + { + .main_font = + { + .uri = "fonts/CuteDino.otf", + .preload_heights = preload_text_heights_v, + }, + .spinner = "images/spinner.png", + .styles = "styles.json", + }, +}; } // namespace void Spaced::set_bindings([[maybe_unused]] Serializer& serializer) {} -Spaced::Spaced(App& app) : GameDriver(app) { +Spaced::Spaced(App& app) : GameDriver(app, gdci_v) { m_log.info("using MSAA: {}x", static_cast(app.get_render_device().get_sample_count())); - load_resources(); + save_styles(); set_layout(); create_services(); set_prefs(); set_scene(); } -void Spaced::load_resources() { - auto const loader = Loader{&get_app().get_data_store(), &get_app().get_render_device()}; - m_resources = &m_services.get(); - m_resources->main_font = loader.load_font("fonts/CuteDino.otf"); - m_resources->spinner = loader.load_texture("images/spinner.png", true); - - auto styles = std::make_unique(); - if (auto const json = loader.load_json("styles.json")) { - *styles = Styles::load(json); - m_log.info("loaded Styles from 'styles.json'"); - } - +void Spaced::save_styles() { if constexpr (bave::debug_v) { static bool s_save_styles{}; if (s_save_styles) { - auto const json = styles->save(); + auto const json = m_services.get().save(); json.to_file("styles.json"); } } - m_services.bind(std::move(styles)); } void Spaced::create_services() { @@ -112,17 +111,22 @@ void Spaced::create_services() { } void Spaced::set_layout() { - auto game_layout = std::make_unique(); - auto const& display = m_services.get(); - game_layout->world_space = display.get_world_space(); - auto const viewport = display.get_main_view().viewport; - auto const hud_size = glm::vec2{viewport.x, 100.0f}; - auto const hud_origin = glm::vec2{0.0f, 0.5f * (viewport.y - hud_size.y)}; - game_layout->hud_area = Rect<>::from_size(hud_size, hud_origin); - auto const play_size = glm::vec2{hud_size.x, viewport.y - hud_size.y}; - auto const play_origin = glm::vec2{0.0f, -0.5f * (viewport.y - play_size.y)}; - game_layout->play_area = Rect<>::from_size(play_size, play_origin); - m_services.bind(std::move(game_layout)); + static constexpr auto world_space_v = glm::vec2{1920.0f, 1080.0f}; + + auto layout = std::make_unique(); + m_layout = layout.get(); + auto& display = m_services.get(); + display.set_world_space(display.get_viewport_scaler().match_width(world_space_v)); + + layout->world_space = display.get_world_space(); + layout->player_x = -0.5f * layout->world_space.x + 0.2f * layout->world_space.x; + auto const hud_size = glm::vec2{layout->world_space.x, 100.0f}; + auto const hud_origin = glm::vec2{0.0f, 0.5f * (layout->world_space.y - hud_size.y)}; + layout->hud_area = Rect<>::from_size(hud_size, hud_origin); + auto const play_size = glm::vec2{hud_size.x, layout->world_space.y - hud_size.y}; + auto const play_origin = glm::vec2{0.0f, -0.5f * (layout->world_space.y - play_size.y)}; + layout->play_area = Rect<>::from_size(play_size, play_origin); + m_services.bind(std::move(layout)); } void Spaced::set_prefs() { @@ -132,5 +136,5 @@ void Spaced::set_prefs() { audio.set_sfx_gain(prefs.sfx_gain); } -void Spaced::set_scene() { get_switcher().switch_to(); } +void Spaced::set_scene() { get_switcher().switch_to(); } } // namespace spaced diff --git a/src/spaced/spaced/spaced.hpp b/src/spaced/spaced/spaced.hpp index e534ef8..827cff8 100644 --- a/src/spaced/spaced/spaced.hpp +++ b/src/spaced/spaced/spaced.hpp @@ -9,6 +9,7 @@ struct Resources; namespace spaced { class Serializer; struct SceneSwitcher; +struct Layout; class Spaced : public bave::GameDriver { public: @@ -17,7 +18,7 @@ class Spaced : public bave::GameDriver { private: static void set_bindings(Serializer& serializer); - void load_resources(); + void save_styles(); void create_services(); void set_layout(); void set_prefs(); @@ -25,5 +26,6 @@ class Spaced : public bave::GameDriver { bave::Logger m_log{"Spaced"}; bave::Ptr m_resources{}; + bave::Ptr m_layout{}; }; } // namespace spaced