From df9139fbc6b8cf33a60b7cc7e503ffdd1a439d57 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sat, 27 Aug 2022 16:57:37 +0100 Subject: [PATCH] grid world test --- demo/CMakeLists.txt | 3 +- demo/demo/grid.cpp | 73 +++++++++++++++++ demo/demo/main.cpp | 27 +++---- demo/include/grid.hpp | 47 +++++++++++ include/fggl/entity/gridworld/zone.hpp | 107 +++++++++++++++++++++++++ include/fggl/gfx/paint.hpp | 52 ++++++++++++ 6 files changed, 293 insertions(+), 16 deletions(-) create mode 100644 demo/demo/grid.cpp create mode 100644 demo/include/grid.hpp create mode 100644 include/fggl/entity/gridworld/zone.hpp diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index 83349bf..ab4c435 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -7,7 +7,8 @@ add_executable(demo demo/GameScene.cpp demo/rollball.cpp demo/topdown.cpp - ) + demo/grid.cpp +) target_include_directories(demo diff --git a/demo/demo/grid.cpp b/demo/demo/grid.cpp new file mode 100644 index 0000000..7f7d99b --- /dev/null +++ b/demo/demo/grid.cpp @@ -0,0 +1,73 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 27/08/22. +// + +#include "grid.hpp" +#include "fggl/entity/gridworld/zone.hpp" + +namespace demo { + + GridScene::GridScene(fggl::App &app) : Game(app), m_grid(nullptr) { + } + + void GridScene::activate() { + Game::activate(); + fggl::debug::log(fggl::debug::Level::info, "GridScene::activate()"); + + // create the grid world + m_grid = std::make_unique<fggl::entity::grid::Area2D<255,255>>(m_tiles); + } + + void GridScene::deactivate() { + m_grid = nullptr; + } + + + constexpr float DRAW_SIZE = 64.0F; + constexpr float DRAW_HALF = DRAW_SIZE / 2.0F; + + static void render_grid(fggl::gfx::Paint& paint, fggl::entity::grid::Area2D<255, 255>& grid) { + for (int i=0; i <= 31; ++i) { + for (int j=0; j <= 31; ++j) { + auto& cell = grid.floorAt(i, j); + + float x = i * DRAW_SIZE; + float y = j * DRAW_SIZE; + auto colour = (i + j) % 2 == 0 ? fggl::gfx::colours::WHITE : fggl::gfx::colours::BLACK; + + fggl::gfx::Path2D tileGfx = fggl::gfx::make_rect({x,y}, {DRAW_HALF, DRAW_HALF}, colour); + paint.fill(tileGfx); + } + } + } + + void GridScene::render(fggl::gfx::Graphics &gfx) { + Game::render(gfx); + + fggl::gfx::Paint paint; + render_grid(paint, *m_grid); + + // draw test shapes to check grid alignment + for (int sides = 3; sides <= 25; ++sides) { + auto hexTest = fggl::gfx::make_shape(fggl::math::vec2{sides, 5} * DRAW_SIZE, DRAW_HALF, sides, {1.0F - (sides / 25.0F), 0.0f, sides / 25.0f}); + paint.fill(hexTest); + } + + gfx.draw2D(paint); + } + +} // namespace demo diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp index 3a0dc62..9425d8a 100644 --- a/demo/demo/main.cpp +++ b/demo/demo/main.cpp @@ -45,28 +45,24 @@ #include "GameScene.h" #include "rollball.hpp" #include "topdown.hpp" +#include "grid.hpp" static void setup_menu(fggl::App& app) { auto *menu = app.addState<fggl::scenes::BasicMenu>("menu"); // add some menu items for the game states - menu->add("terrain", [&app]() { - auto* audio = app.service<fggl::audio::AudioService>(); - audio->play("click.ogg", false); - app.change_state("game"); - }); + const std::array labels = {"terrain", "rollball", "Top Down", "Grid World"}; + const std::array scenes = {"game", "rollball", "topdown", "gridworld"}; - menu->add("rollball", [&app]() { - auto* audio = app.service<fggl::audio::AudioService>(); - audio->play("click.ogg", false); - app.change_state("rollball"); - }); + for (std::size_t i = 0; i < labels.size(); ++i) { + std::string sceneName = scenes.at(i); - menu->add("Top Down", [&app]() { - auto* audio = app.service<fggl::audio::AudioService>(); - audio->play("click.ogg", false); - app.change_state("topdown"); - }); + menu->add(labels.at(i), [&app, sceneName]() { + auto* audio = app.service<fggl::audio::AudioService>(); + audio->play("click.ogg", false); + app.change_state(sceneName); + }); + } menu->add("quit", [&app]() { auto* audio = app.service<fggl::audio::AudioService>(); @@ -117,6 +113,7 @@ int main(int argc, const char* argv[]) { app.addState<GameScene>("game"); app.addState<demo::RollBall>("rollball"); app.addState<demo::TopDown>("topdown"); + app.addState<demo::GridScene>("gridworld"); return app.run(argc, argv); } diff --git a/demo/include/grid.hpp b/demo/include/grid.hpp new file mode 100644 index 0000000..6c089b5 --- /dev/null +++ b/demo/include/grid.hpp @@ -0,0 +1,47 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 27/08/22. +// + +#ifndef FGGL_DEMO_INCLUDE_GRID_HPP +#define FGGL_DEMO_INCLUDE_GRID_HPP + +#include <memory> + +#include "fggl/scenes/game.hpp" +#include "fggl/entity/gridworld/zone.hpp" + +namespace demo { + + constexpr int GRID_SIZE = 255; + + class GridScene : public fggl::scenes::Game { + public: + explicit GridScene(fggl::App& app); + void activate() override; + void deactivate() override; + + //void update() override; + void render(fggl::gfx::Graphics& gfx) override; + private: + fggl::entity::grid::TileSet m_tiles; + std::unique_ptr<fggl::entity::grid::Area2D<GRID_SIZE,GRID_SIZE>> m_grid; + + }; + +} + +#endif //FGGL_DEMO_INCLUDE_GRID_HPP diff --git a/include/fggl/entity/gridworld/zone.hpp b/include/fggl/entity/gridworld/zone.hpp new file mode 100644 index 0000000..720b845 --- /dev/null +++ b/include/fggl/entity/gridworld/zone.hpp @@ -0,0 +1,107 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 20/08/22. +// + +#ifndef FGGL_ENTITY_GRIDWORLD_ZONE_HPP +#define FGGL_ENTITY_GRIDWORLD_ZONE_HPP + +#include <array> +#include <vector> + +#include "fggl/math/types.hpp" + +namespace fggl::entity::grid { + + template<typename T, uint32_t width, uint32_t height> + struct Grid { + public: + Grid() = default; + + inline T& at(math::vec2i pos) { + return m_cells[getCellIndex(pos)]; + } + + const T& at(math::vec2i pos) const { + return m_cells[getCellIndex(pos)]; + } + + inline bool inBounds(math::vec2i pos) const { + return 0 <= pos.x && pos.x <= size.x && + 0 <= pos.y && pos.y <= size.y; + } + + private: + constexpr static math::vec2i size = math::vec2i(width, height); + std::array<T, size.x * size.y> m_cells; + + + inline uint32_t getCellIndex(math::vec2i pos) const { + assert( inBounds(pos)); + return pos.y * size.x + pos.x; + } + }; + + struct FloorTile { + constexpr static uint8_t IMPOSSIBLE = 0; + uint8_t moveCost = IMPOSSIBLE; + }; + + struct TileSet { + std::vector<FloorTile> m_floors; + }; + + /** + * A 2D representation of a space. + * + * @tparam width the grid width in units + * @tparam height the grid height in units + */ + template<uint32_t width, uint32_t height> + struct Area2D { + public: + inline explicit Area2D(TileSet& tiles) : m_tiles(tiles) {} + + inline FloorTile& floorAt(uint32_t x, uint32_t y) { + return m_tiles.m_floors[ m_floors.at({x, y}) ]; + } + + inline bool canMove(math::vec2i pos) const { + return m_tiles.m_floors[m_floors.getCell(pos)] != 0; + } + + inline bool canMove(math::vec2i pos, math::vec2i dir) const { + return canMove(pos + dir) && !blocked(pos, dir); + } + + inline bool blocked(math::vec2i pos, math::vec2i dir) const { + if ( dir.x == -1 || dir.y == -1 ) { + return m_walls.getCell(pos) != 0; + } else { + m_walls.getCell(pos + dir) != 0; + } + } + + void neighbours(math::vec2i pos, std::vector<math::vec2i> &neighbours) const; + private: + TileSet& m_tiles; + Grid<uint32_t, width, height> m_floors; + Grid<uint32_t, width + 1, height + 1> m_walls; + }; + +} // namespace fggl::entity::gridworld + +#endif //FGGL_ENTITY_GRIDWORLD_ZONE_HPP diff --git a/include/fggl/gfx/paint.hpp b/include/fggl/gfx/paint.hpp index 58d94c6..06d10ba 100644 --- a/include/fggl/gfx/paint.hpp +++ b/include/fggl/gfx/paint.hpp @@ -23,6 +23,20 @@ namespace fggl::gfx { using RadianAngle = float; + namespace colours { + constexpr math::vec3 WHITE{1.0F, 1.0F, 1.0F}; + constexpr math::vec3 BLACK{0.0F, 0.0F, 0.0F}; + + constexpr math::vec3 RED{1.0F, 0.0F, 0.0F}; + constexpr math::vec3 GREEN{0.0F, 1.0F, 0.0F}; + constexpr math::vec3 BLUE{0.0F, 0.0F, 1.0F}; + + constexpr math::vec3 YELLOW{1.0F, 1.0F, 0.0F}; + constexpr math::vec3 CYAN{0.0F, 1.0F, 1.0F}; + constexpr math::vec3 MAGENTA{1.0F, 0.0F, 1.0F}; + } + + enum class PathType { MOVE, PATH, @@ -67,6 +81,44 @@ namespace fggl::gfx { std::vector<math::vec3> m_colours; }; + inline Path2D make_rect(math::vec2 center, math::vec2 radius, math::vec3 colour = {1.0f, 1.0f, 1.0f}) { + fggl::gfx::Path2D tileGfx(center); + tileGfx.colour(colour); + tileGfx.moveTo(center - radius); + tileGfx.pathTo({center.x + radius.x, center.y - radius.y}); + tileGfx.pathTo(center + radius); + tileGfx.pathTo({center.x - radius.x, center.y + radius.y}); + tileGfx.close(); + return tileGfx; + } + + struct ShapeOpts { + float angleOffset = 0.0F; + bool sinFirst = true; + }; + + inline Path2D make_shape(math::vec2 center, float radius, int sides, math::vec3 colour = {1.0f, 1.0f, 1.0f}, ShapeOpts opts = {}) { + double angle = (M_PI * 2.0) / sides; + + fggl::gfx::Path2D tileGfx(center); + tileGfx.colour(colour); + + for (int i=0; i < sides; ++i) { + float xPos = (float)(sin(i * angle + opts.angleOffset) * radius) + center.x; + float yPos = (float)(cos(i * angle + opts.angleOffset) * radius) + center.y; + if (!opts.sinFirst) { + std::swap(xPos, yPos); + } + if ( i == 0 ) { + tileGfx.moveTo( {xPos, yPos} ); + } else { + tileGfx.pathTo({xPos, yPos}); + } + } + tileGfx.close(); + return tileGfx; + } + enum class PaintType { FILL, STROKE -- GitLab