open boundary conditons work

This commit is contained in:
Valentin Boettcher 2020-03-15 15:57:29 +01:00
commit 45e61cc5c4
14 changed files with 426 additions and 0 deletions

BIN
assets/bg_tilemap.tga Normal file

Binary file not shown.

BIN
assets/raw/bg_tilemap.xcf Normal file

Binary file not shown.

30
components/components.hpp Normal file
View 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
View 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
View 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 &reg, char index, const sf::Vector2i &pos) {
auto e = reg.create();
reg.assign<Tile>(e, pos, index);
reg.assign<Background>(e);
return e;
}
#endif

View file

@ -0,0 +1 @@
#include "maze_generator.hpp"

View 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 &reg,
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 &reg, 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
View 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
View 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
View 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

@ -0,0 +1 @@
Subproject commit 043f9a50257502f4c5f66a10824ffb1975d2a774

1
subprojects/spdlog Submodule

@ -0,0 +1 @@
Subproject commit 06d0299639aeabd3590844e276d7c1b31f11ade6

1
tilemap/tilemap.cpp Normal file
View file

@ -0,0 +1 @@
#include "tilemap.hpp"

115
tilemap/tilemap.hpp Normal file
View 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 &reg) :
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 &reg, 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