From 45e61cc5c49f75b8d5512e9865ad1743945da096 Mon Sep 17 00:00:00 2001 From: Valentin Boettcher Date: Sun, 15 Mar 2020 15:57:29 +0100 Subject: [PATCH] open boundary conditons work --- assets/bg_tilemap.tga | Bin 0 -> 2844 bytes assets/raw/bg_tilemap.xcf | Bin 0 -> 3947 bytes components/components.hpp | 30 ++++++ definitions.h | 11 +++ factory.hpp | 17 ++++ maze_generator/maze_generator.cpp | 1 + maze_generator/maze_generator.hpp | 149 ++++++++++++++++++++++++++++++ mazetd.cpp | 47 ++++++++++ mazetd.hpp | 32 +++++++ meson.build | 21 +++++ subprojects/entt | 1 + subprojects/spdlog | 1 + tilemap/tilemap.cpp | 1 + tilemap/tilemap.hpp | 115 +++++++++++++++++++++++ 14 files changed, 426 insertions(+) create mode 100644 assets/bg_tilemap.tga create mode 100644 assets/raw/bg_tilemap.xcf create mode 100644 components/components.hpp create mode 100644 definitions.h create mode 100644 factory.hpp create mode 100644 maze_generator/maze_generator.cpp create mode 100644 maze_generator/maze_generator.hpp create mode 100644 mazetd.cpp create mode 100644 mazetd.hpp create mode 100644 meson.build create mode 160000 subprojects/entt create mode 160000 subprojects/spdlog create mode 100644 tilemap/tilemap.cpp create mode 100644 tilemap/tilemap.hpp diff --git a/assets/bg_tilemap.tga b/assets/bg_tilemap.tga new file mode 100644 index 0000000000000000000000000000000000000000..d5ee6242890cd350f201d4acd50f043fe95d7c0f GIT binary patch literal 2844 zcmZQz;9`IP2Zja)2L=U=b|(w-|LqYG;s5co_HVrRjDQk^0i%kMgA)=O@F1QJXZ3?v z`zP&1%~~6oqBeSZP`_PXDwiXxg;kER3yHyQ^?0rO-ll$ySkNMPOS&QrmcyJVOJ0 zic-i6WZ4(UKFhp7A0RzvEWre_sa2STulYP@?)=?5z5|Yv>5)7R$MWmI^DNjY@B!@y z*aVFRz8-`19k>YW3AhK=!Lw3x-c#@gv}0`D3)AT|nic3<9DKBsML`jb4%zbYg#}vCLhpjz%#i2>r$h!UQyq`&~9m3FB`ZkYksd3TEC=>z4_~o zF}Rw)EB{H;x(1i_wYU@padCFP29C<2%;pO1g?%M1gh5=K-LHY8vOp$m&%v2}F3yBO zT%6snfupie*>u!P^ihR`uc@uSX7sK9{baeP1l6QT- zKv(8N5Lj!zXc5F4d(RvKXFE0EdXw?HHDY)6gwfbn`|eRUZ|!|^2%PQIfa^`h@79Rj t*%wA*U+ud`-3(DV#=z|lQTa>5^(Ny- +#include +#include "../tilemap/tilemap.hpp" +#include "spdlog/fmt/ostr.h" + +struct Background {}; + + +/** + Tile component for discrete blocks. + **/ +struct Tile { + sf::Vector2i pos; + char index; + + template + friend OStream &operator<<(OStream &os, const Tile &c) { + return os << "Tile {pos={ x=" << c.pos.x << "; y=" << c.pos.y + << " }; index=" << static_cast(c.index) << "}"; + } +}; + +// class Sprite { +// Sprite(std::string texturepath); + +// }; + +#endif // COMPONENTS_H diff --git a/definitions.h b/definitions.h new file mode 100644 index 0000000..9e2443c --- /dev/null +++ b/definitions.h @@ -0,0 +1,11 @@ +#ifndef definitions_h +#define definitions_h 1 + +#include "entt/entt.hpp" + +// include spdlog, make namespace nicer +#include +namespace spd = spdlog; + + +#endif diff --git a/factory.hpp b/factory.hpp new file mode 100644 index 0000000..b941681 --- /dev/null +++ b/factory.hpp @@ -0,0 +1,17 @@ +#ifndef FACTORY_H +#define FACTORY_H 1 +#include +#include "definitions.h" +#include "components/components.hpp" +#include + +entt::entity create_bg_tile(entt::registry ®, char index, const sf::Vector2i &pos) { + auto e = reg.create(); + + reg.assign(e, pos, index); + reg.assign(e); + + return e; +} + +#endif diff --git a/maze_generator/maze_generator.cpp b/maze_generator/maze_generator.cpp new file mode 100644 index 0000000..d54e263 --- /dev/null +++ b/maze_generator/maze_generator.cpp @@ -0,0 +1 @@ +#include "maze_generator.hpp" diff --git a/maze_generator/maze_generator.hpp b/maze_generator/maze_generator.hpp new file mode 100644 index 0000000..d592d7e --- /dev/null +++ b/maze_generator/maze_generator.hpp @@ -0,0 +1,149 @@ +#ifndef MAZE_GENERATOR_H +#define MAZE_GENERATOR_H 1 +#include +#include +#include +#include +#include +#include "../factory.hpp" +#include + +void dump_maze(std::vector &maze, int width, int height) { + for (int y {0}; y < height; y++) { + for (int x {0}; x < width; x++) { + std::cout << (maze.at(x + y*width) ? " " : "█"); + } + + std::cout << '\n'; + } +} + +template +std::vector +generate_maze(entt::registry ®, + int width, int height, unsigned long seed) { + + // TODO: check width, height + using intpair = std::pair; + std::uniform_int_distribution uniform_dist{0, 4}; // Note: think of a better way + std::default_random_engine engine{seed}; + + std::vector maze(width*height, false); + std::stack stack; + + maze.at(0) = true; + stack.push(0); + + auto wrap_x = + [=](int x) { + return x < 0 ? (width + x) : (x % width); + }; + + auto wrap_y = + [=](int y) { + return y < 0 ? (height + y) : (y % height); + }; + + auto wrap_cell = + [=](std::pair in) { + return std::make_pair(wrap_x(in.first), wrap_y(in.second)); + }; + + auto get_cell = + [=](std::pair in) { + return in.first + width*in.second; + }; + + auto get_coords = + [=] (int current) { + auto x = (current % width); + return std::make_pair(x, (current - x) / width); + }; + + while (!stack.empty()) { + auto current = stack.top(); + auto [x, y] = get_coords(current); + stack.pop(); + + /* + 1 + 2 x 3 -- cell layout + 4 + + We use toroidal boundary conditions. + */ + + std::array neightbours + {{ {x, y - 2}, {x, y + 2}, {x - 2, y}, {x + 2, y} }}; + std::transform(neightbours.begin(), neightbours.end(), + neightbours.begin(), wrap_cell); + + std::array walls + {{ {x, y - 1}, {x, y + 1}, {x - 1, y}, {x + 1, y} }}; + std::transform(walls.begin(), walls.end(), + walls.begin(), wrap_cell); + + std::vector unvisited; + unvisited.reserve(4); + + for (char i {0}; i < 4; i++) { + if(!maze.at(get_cell(neightbours.at(i)))) { + unvisited.push_back(i); + } + } + + if (unvisited.empty()) + continue; + + stack.push(current); + int chosen = unvisited.at(uniform_dist(engine) % unvisited.size()); + + auto neighbour = get_cell(neightbours[chosen]); + maze.at(get_cell(walls[chosen])) = true; + maze.at(neighbour) = true; + stack.push(neighbour); + + + spd::debug("x={} y={} chosen=[ x={} y={} ] stack[{}]={}", + x, y, neightbours.at(chosen).first, neightbours.at(chosen).second, + stack.size() - 1, stack.top()); + dump_maze(maze, width, height); + } + + std::vector maze_entities; + maze_entities.reserve(width*height); + + for (int y {0}; y < height; y++) { + for (int x {0}; x < width; x++) { + auto e = create_bg_tile(reg, maze[x + y*width] ? 0 : 1, {x, y}); + maze_entities.push_back(e); + } + } + + return maze_entities; +} + + +std::vector +generate_maze(entt::registry ®, int width, int height) { + std::random_device rd; + return generate_maze(reg, width, height, rd()); +} + +// class MazeGenerator { +// public: +// MazeGenerator(const uint width, const uint height) +// : m_width{width}, m_height{height} {}; + +// void generate(int); + +// private: +// bool **m_maze; + +// const uint m_width; +// const uint m_height; + +// const std::random_device m_rand; +// }; + +#endif diff --git a/mazetd.cpp b/mazetd.cpp new file mode 100644 index 0000000..545a002 --- /dev/null +++ b/mazetd.cpp @@ -0,0 +1,47 @@ +#include "mazetd.hpp" +#include +#include "maze_generator/maze_generator.hpp" + +Game::Game() + : m_reg{}, m_game_state{Game::running}, + m_background_system{config.bg_tilemap_location, config.width, + config.height, sf::Vector2u{64, 64}, 2, + m_reg} +{ + m_reg.set(config); + + // test changing + auto maze = generate_maze(m_reg, config.width, config.height, 1); + m_background_system.update(); + + // create the window + sf::RenderWindow window(sf::VideoMode(512, 512), "Tilemap"); + window.setFramerateLimit(config.FPS); + while (window.isOpen()) + { + // handle events + sf::Event event; + while (window.pollEvent(event)) + { + if(event.type == sf::Event::Closed) + window.close(); + } + + // draw the map + window.clear(); + window.draw(m_background_system); + window.display(); + } + +} + +void set_up_logging() { + spd::set_level(config.log_level); +} + +int main() { + set_up_logging(); + Game game{}; + + return 0; +} diff --git a/mazetd.hpp b/mazetd.hpp new file mode 100644 index 0000000..3a7ddf3 --- /dev/null +++ b/mazetd.hpp @@ -0,0 +1,32 @@ +#ifndef MAZETD_H +#define MAZETD_H 1 + +#include "definitions.h" +#include +#include +#include +#include "tilemap/tilemap.hpp" +#include "factory.hpp" + +const struct config_t { + const unsigned int height{14}; + const unsigned int width{14}; + const spd::level::level_enum log_level{spd::level::debug}; + const std::string bg_tilemap_location{"./assets/bg_tilemap.tga"}; + const unsigned int FPS{60}; +} config; + +class Game { +public: + Game(); + +private: + entt::registry m_reg; + + enum { stopped, running, paused } m_game_state; + + TileMapSystem m_background_system; +}; + + +#endif // MAZETD_H diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..7ef6dda --- /dev/null +++ b/meson.build @@ -0,0 +1,21 @@ +project('mazetd', 'cpp', + version : '0.1', + default_options : ['warning_level=3', + 'cpp_std=c++17']) + +sfml_deps = [] +sfml_modules = ['window', 'graphics', 'system'] + +foreach mod : sfml_modules + sfml_deps += [dependency('sfml-' + mod)] +endforeach + +spdlog_proj = subproject('spdlog') +spdlog_dep = spdlog_proj.get_variable('spdlog_dep') + + +exe = executable('mazetd', ['mazetd.cpp'], + include_directories: include_directories('subprojects/entt/single_include/'), + dependencies : sfml_deps + [spdlog_dep]) + +test('basic', exe) diff --git a/subprojects/entt b/subprojects/entt new file mode 160000 index 0000000..043f9a5 --- /dev/null +++ b/subprojects/entt @@ -0,0 +1 @@ +Subproject commit 043f9a50257502f4c5f66a10824ffb1975d2a774 diff --git a/subprojects/spdlog b/subprojects/spdlog new file mode 160000 index 0000000..06d0299 --- /dev/null +++ b/subprojects/spdlog @@ -0,0 +1 @@ +Subproject commit 06d0299639aeabd3590844e276d7c1b31f11ade6 diff --git a/tilemap/tilemap.cpp b/tilemap/tilemap.cpp new file mode 100644 index 0000000..bafdf69 --- /dev/null +++ b/tilemap/tilemap.cpp @@ -0,0 +1 @@ +#include "tilemap.hpp" diff --git a/tilemap/tilemap.hpp b/tilemap/tilemap.hpp new file mode 100644 index 0000000..7f6597c --- /dev/null +++ b/tilemap/tilemap.hpp @@ -0,0 +1,115 @@ +#ifndef TILEMAP_H +#define TILEMAP_H 1 + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "../components/components.hpp" +#include "../definitions.h" + +template +class TileMapSystem : public sf::Drawable, public sf::Transformable { +public: + TileMapSystem(const std::string &tileset, const unsigned int width, + const unsigned int height, const sf::Vector2u tile_size, + const unsigned int tile_count, entt::registry ®) : + m_width{width}, m_height{height}, m_tile_size{tile_size}, + m_tile_count{tile_count}, m_reg{reg}, + m_observer{m_reg, entt::collector.replace().where()} + { + spd::debug("Creating {}", *this); + + if (!m_tileset.loadFromFile(tileset)) { + throw std::filesystem::filesystem_error + (("Could not load the tileset: " + tileset).c_str(), + std::error_code()); + } + + m_vertices.setPrimitiveType(sf::Quads); + m_vertices.resize(width * height * 4); + + m_reg.on_construct() + .template connect<&TileMapSystem::on_tile_create>(*this); + } + + ~TileMapSystem() { + m_reg.on_construct() + .template disconnect<&TileMapSystem::on_tile_create>(*this); + + } + + void update() { + for (const auto &entity : m_observer) { + const auto tile = m_reg.get(entity); + update(tile); + } + + m_observer.clear(); + } + + void update(const Tile &tile) { + auto pos = tile.pos; + + int tu = tile.index % (m_tileset.getSize().x / m_tile_size.x); + int tv = tile.index / (m_tileset.getSize().x / m_tile_size.x); + sf::Vertex* quad = &m_vertices[(pos.x + pos.y * m_width) * 4]; + + // define its 4 corners + quad[0].position = sf::Vector2f(pos.x * m_tile_size.x, pos.y * m_tile_size.y); + quad[1].position = sf::Vector2f((pos.x + 1) * m_tile_size.x, pos.y * m_tile_size.y); + quad[2].position = sf::Vector2f((pos.x + 1) * m_tile_size.x, (pos.y + 1) * m_tile_size.y); + quad[3].position = sf::Vector2f(pos.x * m_tile_size.x, (pos.y + 1) * m_tile_size.y); + + // define its 4 texture coordinates + quad[0].texCoords = sf::Vector2f(tu * m_tile_size.x, tv * m_tile_size.y); + quad[1].texCoords = sf::Vector2f((tu + 1) * m_tile_size.x, tv * m_tile_size.y); + quad[2].texCoords = sf::Vector2f((tu + 1) * m_tile_size.x, (tv + 1) * m_tile_size.y); + quad[3].texCoords = sf::Vector2f(tu * m_tile_size.x, (tv + 1) * m_tile_size.y); + } + + void on_tile_create(const entt::registry ®, const entt::entity &e) { + const auto tile = reg.get(e); + update(tile); + } + + template + friend OStream &operator<<(OStream &os, const TileMapSystem &c) { + return os << typeid(TileMapSystem).name() << " Width=" << c.m_width + << " Height=" << c.m_height ; + } + +private: + sf::VertexArray m_vertices; + sf::Texture m_tileset; + + const unsigned int m_width; + const unsigned int m_height; + const sf::Vector2u m_tile_size; + const unsigned int m_tile_count; + + entt::registry &m_reg; + entt::observer m_observer; + + virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const { + // apply the transform + states.transform *= getTransform(); + + // apply the tileset texture + states.texture = &m_tileset; + + // draw the vertex array + target.draw(m_vertices, states); + }; +}; + + + +#endif // TILEMAP_H