mirror of
https://github.com/vale981/mazetd
synced 2025-03-04 09:21:40 -05:00
open boundary conditons work
This commit is contained in:
commit
45e61cc5c4
14 changed files with 426 additions and 0 deletions
BIN
assets/bg_tilemap.tga
Normal file
BIN
assets/bg_tilemap.tga
Normal file
Binary file not shown.
BIN
assets/raw/bg_tilemap.xcf
Normal file
BIN
assets/raw/bg_tilemap.xcf
Normal file
Binary file not shown.
30
components/components.hpp
Normal file
30
components/components.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef COMPONENTS_H
|
||||
#define COMPONENTS_H 1
|
||||
#include <string>
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include "../tilemap/tilemap.hpp"
|
||||
#include "spdlog/fmt/ostr.h"
|
||||
|
||||
struct Background {};
|
||||
|
||||
|
||||
/**
|
||||
Tile component for discrete blocks.
|
||||
**/
|
||||
struct Tile {
|
||||
sf::Vector2i pos;
|
||||
char index;
|
||||
|
||||
template <typename OStream>
|
||||
friend OStream &operator<<(OStream &os, const Tile &c) {
|
||||
return os << "Tile {pos={ x=" << c.pos.x << "; y=" << c.pos.y
|
||||
<< " }; index=" << static_cast<int>(c.index) << "}";
|
||||
}
|
||||
};
|
||||
|
||||
// class Sprite {
|
||||
// Sprite(std::string texturepath);
|
||||
|
||||
// };
|
||||
|
||||
#endif // COMPONENTS_H
|
11
definitions.h
Normal file
11
definitions.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef definitions_h
|
||||
#define definitions_h 1
|
||||
|
||||
#include "entt/entt.hpp"
|
||||
|
||||
// include spdlog, make namespace nicer
|
||||
#include <spdlog/spdlog.h>
|
||||
namespace spd = spdlog;
|
||||
|
||||
|
||||
#endif
|
17
factory.hpp
Normal file
17
factory.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef FACTORY_H
|
||||
#define FACTORY_H 1
|
||||
#include <entt/entt.hpp>
|
||||
#include "definitions.h"
|
||||
#include "components/components.hpp"
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
|
||||
entt::entity create_bg_tile(entt::registry ®, char index, const sf::Vector2i &pos) {
|
||||
auto e = reg.create();
|
||||
|
||||
reg.assign<Tile>(e, pos, index);
|
||||
reg.assign<Background>(e);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#endif
|
1
maze_generator/maze_generator.cpp
Normal file
1
maze_generator/maze_generator.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "maze_generator.hpp"
|
149
maze_generator/maze_generator.hpp
Normal file
149
maze_generator/maze_generator.hpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#ifndef MAZE_GENERATOR_H
|
||||
#define MAZE_GENERATOR_H 1
|
||||
#include <random>
|
||||
#include <vector>
|
||||
#include <entt/entt.hpp>
|
||||
#include <random>
|
||||
#include <stack>
|
||||
#include "../factory.hpp"
|
||||
#include <iostream>
|
||||
|
||||
void dump_maze(std::vector<bool> &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<typename... MazeComponentTypes>
|
||||
std::vector<entt::entity>
|
||||
generate_maze(entt::registry ®,
|
||||
int width, int height, unsigned long seed) {
|
||||
|
||||
// TODO: check width, height
|
||||
using intpair = std::pair<int, int>;
|
||||
std::uniform_int_distribution<int> uniform_dist{0, 4}; // Note: think of a better way
|
||||
std::default_random_engine engine{seed};
|
||||
|
||||
std::vector<bool> maze(width*height, false);
|
||||
std::stack<int> 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<int, int> in) {
|
||||
return std::make_pair(wrap_x(in.first), wrap_y(in.second));
|
||||
};
|
||||
|
||||
auto get_cell =
|
||||
[=](std::pair<int, int> 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<intpair, 4> 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<intpair, 4> 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<char> 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<entt::entity> 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<entt::entity>
|
||||
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
|
47
mazetd.cpp
Normal file
47
mazetd.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "mazetd.hpp"
|
||||
#include <SFML/Graphics.hpp>
|
||||
#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_t>(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;
|
||||
}
|
32
mazetd.hpp
Normal file
32
mazetd.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef MAZETD_H
|
||||
#define MAZETD_H 1
|
||||
|
||||
#include "definitions.h"
|
||||
#include <SFML/Graphics/VertexArray.hpp>
|
||||
#include <string>
|
||||
#include <entt/entt.hpp>
|
||||
#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<Background> m_background_system;
|
||||
};
|
||||
|
||||
|
||||
#endif // MAZETD_H
|
21
meson.build
Normal file
21
meson.build
Normal file
|
@ -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)
|
1
subprojects/entt
Submodule
1
subprojects/entt
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 043f9a50257502f4c5f66a10824ffb1975d2a774
|
1
subprojects/spdlog
Submodule
1
subprojects/spdlog
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 06d0299639aeabd3590844e276d7c1b31f11ade6
|
1
tilemap/tilemap.cpp
Normal file
1
tilemap/tilemap.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "tilemap.hpp"
|
115
tilemap/tilemap.hpp
Normal file
115
tilemap/tilemap.hpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#ifndef TILEMAP_H
|
||||
#define TILEMAP_H 1
|
||||
|
||||
#include <SFML/Graphics/VertexArray.hpp>
|
||||
#include <SFML/Graphics/Transformable.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
#include <SFML/Graphics/Drawable.hpp>
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../components/components.hpp"
|
||||
#include "../definitions.h"
|
||||
|
||||
template <typename ComponentType>
|
||||
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<Tile>().where<ComponentType>()}
|
||||
{
|
||||
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<ComponentType>()
|
||||
.template connect<&TileMapSystem<ComponentType>::on_tile_create>(*this);
|
||||
}
|
||||
|
||||
~TileMapSystem() {
|
||||
m_reg.on_construct<ComponentType>()
|
||||
.template disconnect<&TileMapSystem<ComponentType>::on_tile_create>(*this);
|
||||
|
||||
}
|
||||
|
||||
void update() {
|
||||
for (const auto &entity : m_observer) {
|
||||
const auto tile = m_reg.get<Tile>(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<Tile>(e);
|
||||
update(tile);
|
||||
}
|
||||
|
||||
template <typename OStream>
|
||||
friend OStream &operator<<(OStream &os, const TileMapSystem<ComponentType> &c) {
|
||||
return os << typeid(TileMapSystem<ComponentType>).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
|
Loading…
Add table
Reference in a new issue