Implement state machine infrastructure

This commit is contained in:
NaiJi ✨ 2021-08-03 21:42:58 +03:00
parent 325d49270d
commit a0ad8e7ed6
9 changed files with 155 additions and 40 deletions

View File

@ -2,6 +2,7 @@
#define APPLICATION_H #define APPLICATION_H
#include <memory> #include <memory>
#include <array>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include <SFML/Window/Keyboard.hpp> #include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
@ -12,18 +13,6 @@
class Application class Application
{ {
public: public:
enum State
{
SPLASH_SCREEN,
MAIN_MENU,
GAME_PICKER,
GAME,
EDITOR_PICKER,
EDITOR,
SETTINGS
};
Application(); Application();
void run(); void run();
void input(); void input();
@ -31,13 +20,14 @@ public:
void draw(); void draw();
private: private:
std::stack<std::shared_ptr<GUIState>> _states; std::array<std::shared_ptr<GUIState>, GUIState::Tag::AMOUNT> _states;
std::vector<std::shared_ptr<GUIState>> _state_stack;
sf::RenderWindow _game_window; sf::RenderWindow _game_window;
std::shared_ptr<Game> _game; std::shared_ptr<Game> _game;
State _state;
void exec(); void exec();
void emplaceState(GUIState::Tag new_state);
}; };
#endif // APPLICATION_H #endif // APPLICATION_H

View File

@ -3,14 +3,29 @@
#include <stack> #include <stack>
#include <memory> #include <memory>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Drawable.hpp>
class GUIState : public sf::Drawable class GUIState
{ {
public: public:
enum Tag {
SPLASH_SCREEN,
MAIN_MENU,
GAME_PICKER,
GAME,
EDITOR_PICKER,
EDITOR,
SETTINGS,
AMOUNT
};
virtual ~GUIState() = default; virtual ~GUIState() = default;
virtual void input(const sf::Event& event) = 0; virtual void input(const sf::Event& event) = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0; virtual void draw() const = 0;
virtual void enter() = 0;
virtual void leave() = 0;
}; };

View File

@ -5,6 +5,7 @@
#include "classicgame/classicgraphicsmanager.h" #include "classicgame/classicgraphicsmanager.h"
#include "gui/mainmenu.h" #include "gui/mainmenu.h"
#include "gui/gamestate.h"
#include <iostream> #include <iostream>
@ -19,13 +20,19 @@ Application::Application() :
_game_window.setMouseCursorGrabbed(false); _game_window.setMouseCursorGrabbed(false);
_game_window.setVerticalSyncEnabled(true); _game_window.setVerticalSyncEnabled(true);
_states.push(std::make_shared<MainMenu>(_game_window)); MainMenu::Callbacks callbacks = {[&](){ emplaceState(GUIState::Tag::GAME); }};
const auto main_menu = std::make_shared<MainMenu>(_game_window, std::move(callbacks));
const auto game_state = std::make_shared<GameState>(_game_window, _game, GameState::Callbacks());
_states[GUIState::Tag::MAIN_MENU] = main_menu;
_states[GUIState::Tag::GAME] = game_state;
_state_stack.emplace_back(_states.at(GUIState::Tag::MAIN_MENU));
} }
void Application::run() void Application::run()
{ {
_game_window.display(); _game_window.display();
exec(); exec();
} }
@ -61,16 +68,8 @@ void Application::input()
_game_window.close(); _game_window.close();
break; break;
case sf::Event::KeyPressed:
case sf::Event::KeyReleased:
case sf::Event::MouseButtonReleased:
case sf::Event::MouseButtonPressed:
if (event.key.code == sf::Keyboard::Escape)
_game_window.close();
_states.top()->input(event);
break;
default: default:
_state_stack.back()->input(event);
break; break;
} }
} }
@ -78,12 +77,22 @@ void Application::input()
void Application::update() void Application::update()
{ {
_states.top()->update(); _state_stack.back()->update();
} }
void Application::draw() void Application::draw()
{ {
_game_window.clear(); _game_window.clear();
_game_window.draw(*_states.top());
for (const auto& state : _state_stack)
state->draw();
_game_window.display(); _game_window.display();
} }
void Application::emplaceState(GUIState::Tag new_state)
{
_state_stack.back()->leave();
_state_stack.emplace_back(_states.at(new_state));
_state_stack.back()->enter();
}

37
src/gui/gamestate.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "gamestate.h"
#include "widgets/button.h"
#include "widgets/group.h"
#include "game/game.h"
GameState::GameState(sf::RenderWindow& game_window, const std::shared_ptr<Game>& game,
Callbacks&& callbacks) :
_game(game),
_game_window(game_window),
_onLeaveGameCallback(callbacks.onLeaveGame)
{}
void GameState::input(const sf::Event& event)
{
_game->input({0, event});
}
void GameState::update()
{
_game->update();
}
void GameState::draw() const
{
_game->draw();
}
void GameState::enter()
{
_game->run();
}
void GameState::leave()
{
_onLeaveGameCallback();
}

37
src/gui/gamestate.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include "gui/state.h"
#include <SFML/Graphics/RenderWindow.hpp>
class Group;
class Game;
class GameState : public GUIState
{
public:
struct Callbacks
{
std::function<void(void)> onLeaveGame;
};
explicit GameState(sf::RenderWindow& game_window,
const std::shared_ptr<Game>& game,
Callbacks&& callbacks);
virtual void input(const sf::Event& event) override;
virtual void update() override;
virtual void draw() const override;
virtual void enter() override;
virtual void leave() override;
private:
std::shared_ptr<Game> _game;
sf::RenderWindow& _game_window;
std::function<void(void)> _onEnterGameCallback;
std::function<void(void)> _onLeaveGameCallback;
};

View File

@ -2,16 +2,13 @@
#include "widgets/button.h" #include "widgets/button.h"
#include "widgets/group.h" #include "widgets/group.h"
MainMenu::MainMenu(sf::RenderWindow& game_window) : MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks) :
_buttons(std::make_shared<Group>()), _buttons(std::make_shared<Group>()),
_game_window(game_window) _game_window(game_window)
{ {
std::shared_ptr<Button> button_start = std::make_shared<Button>("Start"); std::shared_ptr<Button> button_start = std::make_shared<Button>("Start");
button_start->setRect(sf::FloatRect(140, 140, 500, 100)); button_start->setRect(sf::FloatRect(140, 140, 500, 100));
button_start->setCallback([&]() button_start->setCallback(callbacks.onAppendGameState);
{
_game_window.close();
});
std::shared_ptr<Button> button_exit = std::make_shared<Button>("Exit"); std::shared_ptr<Button> button_exit = std::make_shared<Button>("Exit");
button_exit->setRect(sf::FloatRect(140, 140, 400, 100)); button_exit->setRect(sf::FloatRect(140, 140, 400, 100));
@ -35,7 +32,18 @@ void MainMenu::update()
_buttons->update(); _buttons->update();
} }
void MainMenu::draw(sf::RenderTarget& target, sf::RenderStates states) const void MainMenu::draw() const
{ {
target.draw(*_buttons, states); _game_window.draw(*_buttons);
} }
void MainMenu::enter()
{
_buttons->setVisibility();
}
void MainMenu::leave()
{
_buttons->setVisibility(false);
}

View File

@ -8,10 +8,19 @@ class Group;
class MainMenu : public GUIState class MainMenu : public GUIState
{ {
public: public:
MainMenu(sf::RenderWindow& game_window);
struct Callbacks
{
std::function<void(void)> onAppendGameState;
};
MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks);
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update() override; virtual void update() override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw() const override;
virtual void enter() override;
virtual void leave() override;
private: private:
std::shared_ptr<Group> _buttons; std::shared_ptr<Group> _buttons;

View File

@ -14,10 +14,18 @@ void Widget::update()
void Widget::draw(sf::RenderTarget& target, sf::RenderStates states) const void Widget::draw(sf::RenderTarget& target, sf::RenderStates states) const
{ {
if (!_is_visible)
return;
for (auto& child : _children) for (auto& child : _children)
child->draw(target, states); child->draw(target, states);
} }
void Widget::setVisibility(bool is_visible)
{
_is_visible = is_visible;
}
void Widget::addChild(const std::shared_ptr<Widget> &child) void Widget::addChild(const std::shared_ptr<Widget> &child)
{ {
child->_parent = shared_from_this(); child->_parent = shared_from_this();

View File

@ -18,10 +18,12 @@ public:
virtual void setPosition(const sf::Vector2f& position) = 0; virtual void setPosition(const sf::Vector2f& position) = 0;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0; virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0;
void setVisibility(bool is_visible = true);
void addChild(const std::shared_ptr<Widget>& child); void addChild(const std::shared_ptr<Widget>& child);
protected: protected:
std::vector<std::shared_ptr<Widget>> _children; std::vector<std::shared_ptr<Widget>> _children;
std::shared_ptr<Widget> _parent; std::shared_ptr<Widget> _parent;
bool _is_visible = true;
}; };