diff --git a/lib/include/chomper/collectibles.hpp b/lib/include/chomper/collectibles.hpp new file mode 100644 index 0000000..c23fd4a --- /dev/null +++ b/lib/include/chomper/collectibles.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "chomper/player.hpp" +#include +#include +#include + +namespace chomper { +class Collectibles { + public: + explicit Collectibles(le::ITexture const& texture); + + void spawn(Player const& player); + + void draw(le::IRenderer& renderer) const; + + void eraseInstance(std::size_t index) { + if (index >= m_sprites.instances.size()) { + return; + } + m_sprites.instances.erase(m_sprites.instances.begin() + static_cast::difference_type>(index)); + } + + [[nodiscard]] std::span getInstances() const { + return m_sprites.instances; + } + + private: + void findEmptyTiles(Player const& player); + + std::vector m_emptyTiles{}; + le::Random m_random{}; + + le::drawable::InstancedSprite m_sprites{}; +}; +} // namespace chomper \ No newline at end of file diff --git a/lib/include/chomper/runtimes/game.hpp b/lib/include/chomper/runtimes/game.hpp index f1b563c..3accf0b 100644 --- a/lib/include/chomper/runtimes/game.hpp +++ b/lib/include/chomper/runtimes/game.hpp @@ -1,5 +1,5 @@ #pragma once -#include "chomper/collectible.hpp" +#include "chomper/collectibles.hpp" #include "chomper/engine.hpp" #include "chomper/player.hpp" #include "chomper/runtime.hpp" @@ -8,9 +8,6 @@ #include #include #include -#include -#include -#include namespace chomper::runtime { // driven by Engine, owner (whether indirectly) of all game things. @@ -32,10 +29,8 @@ class Game : public IRuntime, public klib::Pinned { void bindActions(); void createPlayer(); - void createCollectibleTexture(); + void createCollectibles(); - void findEmptyTiles(); - void spawnCollectibles(); void collideCollectibles(); void onGoBack(); @@ -47,12 +42,9 @@ class Game : public IRuntime, public klib::Pinned { le::input::ScopedActionMapping m_mapping; Actions m_actions{}; - le::Random m_random{}; - std::unordered_set m_occupied; - std::unique_ptr m_player{}; std::unique_ptr m_world{}; - std::vector m_collectibles{}; + std::unique_ptr m_collectibles{}; klib::Ptr m_collectibleTexture{}; std::vector m_emptyTiles{}; diff --git a/lib/src/collectibles.cpp b/lib/src/collectibles.cpp new file mode 100644 index 0000000..391b1dd --- /dev/null +++ b/lib/src/collectibles.cpp @@ -0,0 +1,66 @@ +#include "chomper/collectibles.hpp" +#include "chomper/world_size.hpp" +#include "chomper/world_space.hpp" +#include "le2d/renderer.hpp" + +namespace chomper { +namespace { +constexpr auto collectibleAmount_v = 10; +} + +Collectibles::Collectibles(le::ITexture const& texture) { + m_sprites.set_base_size(tileSize_v); + m_sprites.set_texture(&texture); +} + +void Collectibles::spawn(Player const& player) { + findEmptyTiles(player); + + for (auto i = m_sprites.instances.size(); i < collectibleAmount_v; i++) { + if (m_emptyTiles.empty()) { + return; + } + // find a random tile + auto random = m_random.next_index(m_emptyTiles.size()); + auto tile = m_emptyTiles[random]; + // remove said tile from the vector + std::erase_if(m_emptyTiles, [&](auto const& v) { + return v == m_emptyTiles[random]; + }); + // place the collectible on the tile + auto width = static_cast(worldSize_v.x); + m_sprites.instances.emplace_back(); + m_sprites.instances.back().transform.position = worldSpace::gridToWorld({tile % width, tile / width}); + } +} + +void Collectibles::findEmptyTiles(Player const& player) { + m_emptyTiles.clear(); + m_emptyTiles.reserve(static_cast(worldSize_v.x * worldSize_v.y)); + for (auto i = 0; i < static_cast(worldSize_v.x * worldSize_v.y); i++) { + m_emptyTiles.push_back(i); + } + + auto const removeTile = [this](int tile) { + auto it = std::ranges::find(m_emptyTiles, tile); + if (it != m_emptyTiles.end()) { + *it = m_emptyTiles.back(); + m_emptyTiles.pop_back(); + } + }; + + for (auto const& seg : player.getSegments()) { + auto p = worldSpace::worldToGrid(seg.transform.position); + removeTile(static_cast((p.y * worldSize_v.x) + p.x)); + } + + for (auto const& c : m_sprites.instances) { + auto p = worldSpace::worldToGrid(c.transform.position); + removeTile(static_cast((p.y * worldSize_v.x) + p.x)); + } +} + +void Collectibles::draw(le::IRenderer& renderer) const { + m_sprites.draw(renderer); +} +} // namespace chomper \ No newline at end of file diff --git a/lib/src/runtimes/game.cpp b/lib/src/runtimes/game.cpp index 525f9dc..8d9d0db 100644 --- a/lib/src/runtimes/game.cpp +++ b/lib/src/runtimes/game.cpp @@ -1,8 +1,6 @@ #include "chomper/runtimes/game.hpp" -#include "chomper/collectible.hpp" #include "chomper/im_util.hpp" #include "chomper/runtimes/entrypoint.hpp" -#include "chomper/world_size.hpp" #include "chomper/world_space.hpp" #include #include @@ -12,7 +10,6 @@ namespace { constexpr auto countdownParams_v = le::drawable::Text::Params{ .height = le::TextHeight{60}, }; -constexpr auto collectibleAmount_v = 10; } // namespace using ActionValue = le::input::action::Value; @@ -20,9 +17,9 @@ Game::Game(gsl::not_null engine) : m_engine(engine), m_mapping(&engine- createPlayer(); m_world = std::make_unique(m_engine); - createCollectibleTexture(); + createCollectibles(); - spawnCollectibles(); + m_collectibles->spawn(*m_player); m_countdownText.set_string(engine->getResources().getMainFont(), "3", countdownParams_v); } @@ -52,9 +49,7 @@ void Game::tick(kvf::Seconds const dt) { void Game::render(le::IRenderer& renderer) const { m_world->draw(renderer); - for (auto const& collectible : m_collectibles) { - collectible.draw(renderer); - } + m_collectibles->draw(renderer); m_player->draw(renderer); if (m_countdown.count() > 0) { m_countdownText.draw(renderer); @@ -89,67 +84,22 @@ void Game::createPlayer() { m_player = std::make_unique(m_mapping, m_engine); } -void Game::createCollectibleTexture() { +void Game::createCollectibles() { m_collectibleTexture = m_engine->getResources().load("images/apple.png"); -} - -void Game::findEmptyTiles() { - m_emptyTiles.clear(); - m_emptyTiles.reserve(static_cast(worldSize_v.x * worldSize_v.y)); - for (auto i = 0; i < static_cast(worldSize_v.x * worldSize_v.y); i++) { - m_emptyTiles.push_back(i); - } - - auto const removeTile = [this](int tile) { - auto it = std::ranges::find(m_emptyTiles, tile); - if (it != m_emptyTiles.end()) { - *it = m_emptyTiles.back(); - m_emptyTiles.pop_back(); - } - }; - - for (auto const& seg : m_player->getSegments()) { - auto p = worldSpace::worldToGrid(seg.transform.position); - removeTile(static_cast((p.y * worldSize_v.x) + p.x)); - } - - for (auto const& c : m_collectibles) { - auto p = c.getGridPosition(); - removeTile(static_cast((p.y * worldSize_v.x) + p.x)); - } -} - -void Game::spawnCollectibles() { - findEmptyTiles(); - - for (auto i = m_collectibles.size(); i < collectibleAmount_v; i++) { - if (m_emptyTiles.empty()) { - return; - } - // find a random tile - auto random = m_random.next_index(m_emptyTiles.size()); - auto tile = m_emptyTiles[random]; - // remove said tile from the vector - std::erase_if(m_emptyTiles, [&](auto const& v) { - return v == m_emptyTiles[random]; - }); - // place the collectible on the tile - auto width = static_cast(worldSize_v.x); - m_collectibles.emplace_back(*m_collectibleTexture, worldSpace::gridToWorld({tile % width, tile / width})); - } + m_collectibles = std::make_unique(*m_collectibleTexture); } void Game::collideCollectibles() { - auto it = std::ranges::find_if(m_collectibles, [&](auto const& collectible) { - return collectible.getGridPosition() == worldSpace::worldToGrid(m_player->getSegments().back().transform.position); + auto it = std::ranges::find_if(m_collectibles->getInstances(), [&](auto const& collectible) { + return worldSpace::worldToGrid(collectible.transform.position) == worldSpace::worldToGrid(m_player->getSegments().back().transform.position); }); - if (it == m_collectibles.end()) { + if (it == m_collectibles->getInstances().end()) { return; } - m_collectibles.erase(it); m_player->grow(); - spawnCollectibles(); + m_collectibles->eraseInstance(static_cast(std::distance(m_collectibles->getInstances().begin(), it))); + m_collectibles->spawn(*m_player); } void Game::onGoBack() {