Rendering code stripped out of the GUI code.

main
Zed A. Shaw 3 weeks ago
parent 009b1e63a7
commit 9397af2a11
  1. 1
      dinkyecs.hpp
  2. 192
      gui.cpp
  3. 43
      gui.hpp
  4. 1
      main.cpp
  5. 2
      meson.build
  6. 144
      render.cpp
  7. 53
      render.hpp
  8. 2
      sound.cpp
  9. 2
      sound.hpp

@ -8,7 +8,6 @@
#include <tuple> #include <tuple>
#include <queue> #include <queue>
namespace DinkyECS { namespace DinkyECS {
typedef unsigned long Entity; typedef unsigned long Entity;

@ -12,17 +12,13 @@
#include <ftxui/component/loop.hpp> #include <ftxui/component/loop.hpp>
#include <ftxui/screen/color.hpp> #include <ftxui/screen/color.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include "dbc.hpp" #include "dbc.hpp"
#include "gui.hpp" #include "gui.hpp"
#include "rand.hpp" #include "rand.hpp"
#include "systems.hpp" #include "systems.hpp"
#include "collider.hpp"
#include "events.hpp" #include "events.hpp"
#include "render.hpp"
using std::string; using std::string;
using namespace fmt; using namespace fmt;
@ -30,49 +26,34 @@ using namespace std::chrono_literals;
using namespace ftxui; using namespace ftxui;
using namespace Components; using namespace Components;
std::array<sf::Color, 10> VALUES{
sf::Color{1, 4, 2}, // black
sf::Color{9, 29, 16}, // dark dark
sf::Color{14, 50, 26}, // dark mid
sf::Color{0, 109, 44}, // dark light
sf::Color{63, 171, 92}, // mid
sf::Color{161, 217, 155}, // light dark
sf::Color{199, 233, 192}, // light mid
sf::Color{229, 245, 224}, // light light
sf::Color{255, 255, 255}, // white
sf::Color::Transparent, // white
};
sf::Color GUI::color(int val) {
return VALUES[size_t(val)];
}
sf::Color GUI::color(Value val) {
return VALUES[size_t(val)];
}
GUI::GUI(DinkyECS::World &world, Map& game_map) : GUI::GUI(DinkyECS::World &world, Map& game_map) :
$window(sf::VideoMode(VIDEO_X,VIDEO_Y), "Roguish"), $game_map(game_map),
$log({{"Welcome to the game!"}}),
$view_port{0,0},
$screen(SCREEN_X, SCREEN_Y), $screen(SCREEN_X, SCREEN_Y),
$map_screen(0,0), $map_screen(0,0),
$view_port{0,0},
$map_font_size(BASE_MAP_FONT_SIZE),
$line_spacing(0),
$sounds("./assets"),
$log({{"Welcome to the game!"}}),
$world(world), $world(world),
$game_map(game_map) $sounds("./assets"),
$renderer($canvas, $map_screen, $screen)
{ {
// this needs a config file soon // this needs a config file soon
$font.loadFromFile("./assets/text.otf"); $sounds.load("hit", "hit.wav");
resize_map(BASE_MAP_FONT_SIZE); resize_map(BASE_MAP_FONT_SIZE);
}
$sounds.load("hit", "hit.wav"); void GUI::resize_map(int new_size) {
if($renderer.resize_map(new_size)) {
auto bounds = $renderer.$base_glyph.bounds;
$view_port = {
size_t(std::ceil((VIDEO_X - GAME_MAP_POS) / bounds.width)),
size_t(std::ceil(VIDEO_Y / bounds.height))
};
$ui_text.setFont($font); // set canvas to best size
$ui_text.setPosition(0,0); $canvas = Canvas($view_port.x * 2, $view_port.y * 4);
$ui_text.setCharacterSize(UI_FONT_SIZE); $map_screen = Screen($view_port.x, $view_port.y);
$ui_text.setFillColor(color(Value::LIGHT_LIGHT)); }
} }
void GUI::create_renderer() { void GUI::create_renderer() {
@ -142,13 +123,15 @@ void GUI::handle_world_events() {
} }
bool GUI::handle_ui_events() { bool GUI::handle_ui_events() {
sf::Event event;
bool event_happened = false; bool event_happened = false;
sf::Event event;
auto player = $world.get_the<Player>(); auto player = $world.get_the<Player>();
auto& window = $renderer.$window;
int map_font_size = $renderer.$map_font_size;
while($window.pollEvent(event)) { while(window.pollEvent(event)) {
if(event.type == sf::Event::Closed) { if(event.type == sf::Event::Closed) {
$window.close(); window.close();
} else if(event.type == sf::Event::KeyPressed) { } else if(event.type == sf::Event::KeyPressed) {
auto& player_motion = $world.get<Motion>(player.entity); auto& player_motion = $world.get<Motion>(player.entity);
@ -165,9 +148,9 @@ bool GUI::handle_ui_events() {
player_motion.dy = 1; player_motion.dy = 1;
event_happened = true; event_happened = true;
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) { } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) {
resize_map($map_font_size + 10); resize_map(map_font_size + 10);
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) { } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) {
resize_map($map_font_size - 10); resize_map(map_font_size - 10);
} }
} }
} }
@ -176,21 +159,6 @@ bool GUI::handle_ui_events() {
} }
sf::Sprite &GUI::get_text_sprite(wchar_t tile) {
if(!$sprites.contains(tile)) {
sf::Glyph glyph = $font.getGlyph(tile, $map_font_size, false);
// WARNING! we actually have to do this here because SFML caches
// the glyphs on the font texture, so this gets loaded each time
// we get a new glyph from the font.
$font_texture = $font.getTexture($map_font_size);
sf::Sprite sprite($font_texture);
sprite.setTextureRect(glyph.textureRect);
$sprites[tile] = sprite;
}
return $sprites[tile];
}
void GUI::run_systems() { void GUI::run_systems() {
auto player = $world.get_the<Player>(); auto player = $world.get_the<Player>();
System::enemy_pathing($world, $game_map, player); System::enemy_pathing($world, $game_map, player);
@ -199,123 +167,19 @@ void GUI::run_systems() {
System::death($world); System::death($world);
} }
void GUI::resize_map(int new_size) {
if(MIN_FONT_SIZE < new_size && new_size < MAX_FONT_SIZE) {
$sprites.clear(); // need to reset the sprites for the new size
$map_font_size = new_size;
$base_glyph = $font.getGlyph(L'', $map_font_size, false);
auto bounds = $base_glyph.bounds;
$line_spacing = $font.getLineSpacing($map_font_size);
$view_port = {
size_t(std::ceil((VIDEO_X - GAME_MAP_POS) / bounds.width)),
size_t(std::ceil(VIDEO_Y / bounds.height))
};
// set canvas to best size
$canvas = Canvas($view_port.x * 2, $view_port.y * 4);
$map_screen = Screen($view_port.x, $view_port.y);
}
}
void GUI::draw_screen(bool clear, float map_off_x, float map_off_y) {
if(clear) $window.clear();
std::string screenout = $screen.ToString();
std::wstring main_screen_utf8 = $converter.from_bytes(screenout);
$ui_text.setString(main_screen_utf8);
$window.draw($ui_text);
std::string map_screenout = $map_screen.ToString();
std::wstring map_screen_utf8 = $converter.from_bytes(map_screenout);
float y = 0.0f;
float x = GAME_MAP_POS;
// make a copy so we don't modify the cached one
auto bg_sprite = get_text_sprite(L'');
auto bg_bounds = bg_sprite.getLocalBounds();
bg_sprite.setColor(sf::Color(20,20,20));
auto add_sprite = get_text_sprite(L'!');
bool has_add = false;
for(size_t i = 0; i < map_screen_utf8.size(); i++) {
wchar_t tile = map_screen_utf8[i];
if(tile == L'\n') {
// don't bother processing newlines, just skip
y += $line_spacing;
x = GAME_MAP_POS;
} else if(tile == L'\r') {
continue; // skip these, just windows junk
} else {
// it's a visual cell
bg_sprite.setPosition({x+map_off_x, y+map_off_y});
sf::Sprite &sprite = get_text_sprite(tile);
// should look into caching all this instead of calcing it each time
auto sp_bounds = sprite.getLocalBounds();
// calculate where to center the sprite, but only if it's smaller
auto width_delta = bg_bounds.width > sp_bounds.width ? (bg_bounds.width - sp_bounds.width) / 2 : 0;
auto height_delta = bg_bounds.height > sp_bounds.width ? (bg_bounds.height - sp_bounds.height) / 2 : 0;
// TODO: need to center it inside the bg_sprite
sprite.setPosition({x+width_delta+map_off_x, y+height_delta+map_off_y});
// get the entity combat and make them light gray if dead
if(tile == L'') {
sprite.setColor(sf::Color(80,80,80));
} else if(tile == L'') {
sprite.setColor(sf::Color::Blue);
} else if(tile == L'Ω') {
sprite.setColor(sf::Color::Red);
// HACK: just playing with adding multiple characters for drawing
add_sprite.setColor(sf::Color::Red);
add_sprite.setPosition({x-3,y-3});
has_add = true;
} else if(tile == L'#') {
sprite.setColor(sf::Color(5,5,5));
} else {
sprite.setColor(color(Value::MID));
}
// now draw the background sprite and sprite
// TODO: this can become a standard sprite description
$window.draw(bg_sprite);
$window.draw(sprite);
if(has_add) {
$window.draw(add_sprite);
has_add = false;
}
// next cell
x += $base_glyph.advance;
}
}
$window.display();
}
void GUI::shake() {
for(int i = 0; i < 10; ++i) {
int x = Random::uniform<int>(-10,10);
int y = Random::uniform<int>(-10,10);
// add x/y back to draw screen
draw_screen(true, x, y);
std::this_thread::sleep_for(1ms);
}
}
void GUI::render_scene() { void GUI::render_scene() {
$screen.Clear(); $screen.Clear();
$map_screen.Clear(); $map_screen.Clear();
Render($map_screen, $map_view->Render()); Render($map_screen, $map_view->Render());
Render($screen, $document->Render()); Render($screen, $document->Render());
$renderer.draw_screen();
draw_screen();
} }
int GUI::main() { int GUI::main() {
create_renderer(); create_renderer();
run_systems(); run_systems();
while($window.isOpen()) { while($renderer.$window.isOpen()) {
render_scene(); render_scene();
if(handle_ui_events()) { if(handle_ui_events()) {

@ -4,7 +4,6 @@
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Text.hpp> #include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/Sprite.hpp> #include <SFML/Graphics/Sprite.hpp>
#include <codecvt>
#include <ftxui/component/component.hpp> #include <ftxui/component/component.hpp>
#include <ftxui/screen/screen.hpp> #include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp> #include <ftxui/dom/canvas.hpp>
@ -14,27 +13,13 @@
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "components.hpp" #include "components.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "render.hpp"
using std::string; using std::string;
using ftxui::Canvas, ftxui::Component, ftxui::Screen; using ftxui::Canvas, ftxui::Component, ftxui::Screen;
constexpr int MIN_FONT_SIZE = 20;
constexpr int MAX_FONT_SIZE = 140;
constexpr int GAME_MAP_X = 90;
constexpr int GAME_MAP_Y = 90;
constexpr int GAME_MAP_POS = 600;
constexpr int SCREEN_X = 40; constexpr int SCREEN_X = 40;
constexpr int SCREEN_Y = 30; constexpr int SCREEN_Y = 30;
constexpr int VIDEO_X = 1600;
constexpr int VIDEO_Y = 900;
constexpr int UI_FONT_SIZE=30;
constexpr int BASE_MAP_FONT_SIZE=90;
enum class Value {
BLACK=0, DARK_DARK, DARK_MID,
DARK_LIGHT, MID, LIGHT_DARK, LIGHT_MID,
LIGHT_LIGHT, WHITE, TRANSPARENT
};
struct ActionLog { struct ActionLog {
std::deque<std::string> messages; std::deque<std::string> messages;
@ -49,42 +34,30 @@ struct ActionLog {
class GUI { class GUI {
string $status_text = "NOT DEAD"; string $status_text = "NOT DEAD";
Canvas $canvas;
Component $document; Component $document;
Component $map_view; Component $map_view;
Canvas $canvas; Map& $game_map;
sf::Font $font; ActionLog $log;
sf::Text $ui_text; Point $view_port;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
sf::RenderWindow $window;
Screen $screen; Screen $screen;
Screen $map_screen; Screen $map_screen;
sf::Texture $font_texture;
std::unordered_map<wchar_t, sf::Sprite> $sprites;
Point $view_port;
int $map_font_size;
sf::Glyph $base_glyph;
float $line_spacing;
SoundManager $sounds;
ActionLog $log;
DinkyECS::World& $world; DinkyECS::World& $world;
Map& $game_map; SoundManager $sounds;
SFMLRender $renderer;
public: public:
GUI(DinkyECS::World& world, Map& game_map); GUI(DinkyECS::World& world, Map& game_map);
// disable copying // disable copying
GUI(GUI &gui) = delete; GUI(GUI &gui) = delete;
sf::Color color(Value val); void resize_map(int new_size);
sf::Color color(int val);
void create_renderer(); void create_renderer();
void render_scene(); void render_scene();
bool handle_ui_events(); bool handle_ui_events();
void handle_world_events(); void handle_world_events();
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f); void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
void shake();
void run_systems(); void run_systems();
void resize_map(int new_size);
sf::Sprite &get_text_sprite(wchar_t tile);
int main(); int main();
}; };

@ -5,6 +5,7 @@
#include "components.hpp" #include "components.hpp"
#include "dbc.hpp" #include "dbc.hpp"
#include "collider.hpp" #include "collider.hpp"
#include "render.hpp"
/* /*
* This needs to be turned into a real world generator * This needs to be turned into a real world generator

@ -19,6 +19,7 @@ runtests = executable('runtests', [
'rand.cpp', 'rand.cpp',
'sound.cpp', 'sound.cpp',
'collider.cpp', 'collider.cpp',
'render.cpp',
'tests/fsm.cpp', 'tests/fsm.cpp',
'tests/dbc.cpp', 'tests/dbc.cpp',
'tests/map.cpp', 'tests/map.cpp',
@ -38,6 +39,7 @@ roguish = executable('roguish', [
'collider.cpp', 'collider.cpp',
'combat.cpp', 'combat.cpp',
'systems.cpp', 'systems.cpp',
'render.cpp',
], ],
dependencies: dependencies) dependencies: dependencies)

@ -0,0 +1,144 @@
#include "render.hpp"
#include <cmath>
std::array<sf::Color, 10> VALUES{
sf::Color{1, 4, 2}, // black
sf::Color{9, 29, 16}, // dark dark
sf::Color{14, 50, 26}, // dark mid
sf::Color{0, 109, 44}, // dark light
sf::Color{63, 171, 92}, // mid
sf::Color{161, 217, 155}, // light dark
sf::Color{199, 233, 192}, // light mid
sf::Color{229, 245, 224}, // light light
sf::Color{255, 255, 255}, // white
sf::Color::Transparent, // white
};
sf::Color SFMLRender::color(int val) {
return VALUES[size_t(val)];
}
sf::Color SFMLRender::color(Value val) {
return VALUES[size_t(val)];
}
SFMLRender::SFMLRender(Canvas &canvas, Screen &map_screen, Screen &screen) :
$window(sf::VideoMode(VIDEO_X,VIDEO_Y), "Roguish"),
$map_font_size(BASE_MAP_FONT_SIZE),
$line_spacing(0),
$canvas(canvas),
$map_screen(map_screen),
$screen(screen)
{
$font.loadFromFile("./assets/text.otf");
$ui_text.setFont($font);
$ui_text.setPosition(0,0);
$ui_text.setCharacterSize(UI_FONT_SIZE);
$ui_text.setFillColor(color(Value::LIGHT_LIGHT));
}
sf::Sprite &SFMLRender::get_text_sprite(wchar_t tile) {
if(!$sprites.contains(tile)) {
sf::Glyph glyph = $font.getGlyph(tile, $map_font_size, false);
// WARNING! we actually have to do this here because SFML caches
// the glyphs on the font texture, so this gets loaded each time
// we get a new glyph from the font.
$font_texture = $font.getTexture($map_font_size);
sf::Sprite sprite($font_texture);
sprite.setTextureRect(glyph.textureRect);
$sprites[tile] = sprite;
}
return $sprites[tile];
}
bool SFMLRender::resize_map(int new_size) {
if(MIN_FONT_SIZE < new_size && new_size < MAX_FONT_SIZE) {
$sprites.clear(); // need to reset the sprites for the new size
$map_font_size = new_size;
$base_glyph = $font.getGlyph(L'', $map_font_size, false);
$line_spacing = $font.getLineSpacing($map_font_size);
return true;
} else {
// something else here
return false;
}
}
void SFMLRender::draw_screen(bool clear, float map_off_x, float map_off_y) {
if(clear) $window.clear();
std::string screenout = $screen.ToString();
std::wstring main_screen_utf8 = $converter.from_bytes(screenout);
$ui_text.setString(main_screen_utf8);
$window.draw($ui_text);
std::string map_screenout = $map_screen.ToString();
std::wstring map_screen_utf8 = $converter.from_bytes(map_screenout);
float y = 0.0f;
float x = GAME_MAP_POS;
// make a copy so we don't modify the cached one
auto bg_sprite = get_text_sprite(L'');
auto bg_bounds = bg_sprite.getLocalBounds();
bg_sprite.setColor(sf::Color(20,20,20));
auto add_sprite = get_text_sprite(L'!');
bool has_add = false;
for(size_t i = 0; i < map_screen_utf8.size(); i++) {
wchar_t tile = map_screen_utf8[i];
if(tile == L'\n') {
// don't bother processing newlines, just skip
y += $line_spacing;
x = GAME_MAP_POS;
} else if(tile == L'\r') {
continue; // skip these, just windows junk
} else {
// it's a visual cell
bg_sprite.setPosition({x+map_off_x, y+map_off_y});
sf::Sprite &sprite = get_text_sprite(tile);
// should look into caching all this instead of calcing it each time
auto sp_bounds = sprite.getLocalBounds();
// calculate where to center the sprite, but only if it's smaller
auto width_delta = bg_bounds.width > sp_bounds.width ? (bg_bounds.width - sp_bounds.width) / 2 : 0;
auto height_delta = bg_bounds.height > sp_bounds.width ? (bg_bounds.height - sp_bounds.height) / 2 : 0;
// TODO: need to center it inside the bg_sprite
sprite.setPosition({x+width_delta+map_off_x, y+height_delta+map_off_y});
// get the entity combat and make them light gray if dead
if(tile == L'') {
sprite.setColor(sf::Color(80,80,80));
} else if(tile == L'') {
sprite.setColor(sf::Color::Blue);
} else if(tile == L'Ω') {
sprite.setColor(sf::Color::Red);
// HACK: just playing with adding multiple characters for drawing
add_sprite.setColor(sf::Color::Red);
add_sprite.setPosition({x-3,y-3});
has_add = true;
} else if(tile == L'#') {
sprite.setColor(sf::Color(5,5,5));
} else {
sprite.setColor(color(Value::MID));
}
// now draw the background sprite and sprite
// TODO: this can become a standard sprite description
$window.draw(bg_sprite);
$window.draw(sprite);
if(has_add) {
$window.draw(add_sprite);
has_add = false;
}
// next cell
x += $base_glyph.advance;
}
}
$window.display();
}

@ -0,0 +1,53 @@
#pragma once
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include "point.hpp"
#include <codecvt>
using ftxui::Canvas, ftxui::Screen;
constexpr int VIDEO_X = 1600;
constexpr int VIDEO_Y = 900;
constexpr int MIN_FONT_SIZE = 20;
constexpr int MAX_FONT_SIZE = 140;
constexpr int GAME_MAP_X = 90;
constexpr int GAME_MAP_Y = 90;
constexpr int GAME_MAP_POS = 600;
constexpr int UI_FONT_SIZE=30;
constexpr int BASE_MAP_FONT_SIZE=90;
enum class Value {
BLACK=0, DARK_DARK, DARK_MID,
DARK_LIGHT, MID, LIGHT_DARK, LIGHT_MID,
LIGHT_LIGHT, WHITE, TRANSPARENT
};
struct SFMLRender {
sf::RenderWindow $window;
int $map_font_size;
float $line_spacing;
std::unordered_map<wchar_t, sf::Sprite> $sprites;
sf::Font $font;
sf::Texture $font_texture;
sf::Glyph $base_glyph;
Canvas& $canvas;
Screen& $map_screen;
Screen& $screen;
sf::Text $ui_text;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
SFMLRender(Canvas &canvas, Screen &map_screen, Screen &screen);
// disable copy
SFMLRender(SFMLRender &other) = delete;
sf::Color color(int val);
sf::Color color(Value val);
sf::Sprite &get_text_sprite(wchar_t tile);
bool resize_map(int new_size);
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
};

@ -16,7 +16,7 @@ void SoundManager::load(const std::string name, const std::string sound_path) {
dbc::check(fs::exists(full_path), format("sound file {} does not exist", sound_path)); dbc::check(fs::exists(full_path), format("sound file {} does not exist", sound_path));
// create the buffer and keep in the buffer map // create the buffer and keep in the buffer map
std::shared_ptr<SoundPair> pair = std::make_shared<SoundPair>(); SoundPair* pair = new SoundPair();
$sounds[name] = pair; $sounds[name] = pair;
bool good = pair->buffer.loadFromFile(full_path.string()); bool good = pair->buffer.loadFromFile(full_path.string());

@ -12,7 +12,7 @@ struct SoundPair {
struct SoundManager { struct SoundManager {
std::filesystem::path $base_path; std::filesystem::path $base_path;
std::unordered_map<std::string, std::shared_ptr<SoundPair> > $sounds; std::unordered_map<std::string, SoundPair* > $sounds;
SoundManager(std::string base_path); SoundManager(std::string base_path);

Loading…
Cancel
Save