├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── bin └── img │ ├── intro.png │ ├── menu.png │ └── play.png └── src ├── Application.cpp ├── Application.hpp ├── IntroState.cpp ├── IntroState.hpp ├── MenuState.cpp ├── MenuState.hpp ├── PlayState.cpp ├── PlayState.hpp ├── State.cpp ├── State.hpp ├── StateMachine.cpp ├── StateMachine.hpp └── main.cpp /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.platform.name }} ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.platform.os }} 9 | 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | platform: 14 | - { name: Windows VS2017, os: windows-2016 } 15 | - { name: Windows VS2019, os: windows-latest } 16 | - { name: Linux GCC, os: ubuntu-latest } 17 | - { name: Linux Clang, os: ubuntu-latest, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } 18 | - { name: MacOS XCode, os: macos-latest } 19 | config: 20 | - { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE } 21 | - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } 22 | 23 | steps: 24 | - name: Install Linux Dependencies 25 | if: runner.os == 'Linux' 26 | run: sudo apt-get update && sudo apt-get install libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev 27 | 28 | - name: SmallGameEngine - Checkout Code 29 | uses: actions/checkout@v2 30 | 31 | - name: SFML - Checkout Code 32 | uses: actions/checkout@v2 33 | with: 34 | repository: SFML/SFML 35 | path: SFML 36 | 37 | - name: SFML - Configure CMake 38 | shell: bash 39 | run: cmake -S $GITHUB_WORKSPACE/SFML -B $GITHUB_WORKSPACE/SFML/build -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/SFML/install -DCMAKE_VERBOSE_MAKEFILE=ON ${{matrix.platform.flags}} ${{matrix.config.flags}} 40 | 41 | - name: SFML - Build 42 | shell: bash 43 | run: cmake --build $GITHUB_WORKSPACE/SFML/build --config Release --target install 44 | 45 | - name: SmallGameEngine - Configure CMake 46 | shell: bash 47 | run: cmake -S $GITHUB_WORKSPACE -B $GITHUB_WORKSPACE/build -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install -DCMAKE_VERBOSE_MAKEFILE=ON -DSFML_DIR=$GITHUB_WORKSPACE/SFML/install/lib/cmake/SFML ${{matrix.platform.flags}} ${{matrix.config.flags}} 48 | 49 | - name: SmallGameEngine - Build 50 | shell: bash 51 | run: cmake --build $GITHUB_WORKSPACE/build --config Release --target install -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2 | [Xx]64/ 3 | [Dd]ebug*/ 4 | [Rr]elease*/ 5 | out/ 6 | build/ 7 | ipch/ 8 | *.ipch 9 | *.obj 10 | *.tlog 11 | *.manifest 12 | *.lastbuildstate 13 | *.log 14 | *.idb 15 | *.pdb 16 | *.opensdf 17 | *.sdf 18 | *.sln 19 | *.suo 20 | *.vcxproj 21 | *.vcxproj.filters 22 | *.vcxproj.user 23 | *.exe 24 | *.ilk 25 | *.pdb 26 | *.dll 27 | *.lib 28 | *.exe 29 | CMakeSettings.json 30 | 31 | # Visual Studio Code 32 | .vs/ 33 | 34 | # Code::Blocks 35 | *.cbp 36 | *.depend 37 | *.layout 38 | 39 | # OS junk files 40 | [Tt]humbs.db 41 | *.DS_Store -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(SmallGameEngine LANGUAGES CXX) 4 | 5 | # Set options 6 | option(SMALLGAMEENGINE_STATIC_STD_LIBS "Use statically linked standard/runtime libraries? This option must match the one used for SFML." OFF) 7 | 8 | # SmallGameEngine uses C++17 features 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_EXTENSIONS OFF) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | 13 | # Request static SFML libraries when building statically 14 | if(NOT BUILD_SHARED_LIBS) 15 | set(SFML_STATIC_LIBRARIES true) 16 | endif() 17 | 18 | # Find SFML 19 | find_package(SFML 2.5 COMPONENTS graphics REQUIRED) 20 | 21 | # Tell CMake to build a executable 22 | add_executable(SmallGameEngine 23 | src/main.cpp 24 | src/Application.hpp 25 | src/Application.cpp 26 | src/State.hpp 27 | src/State.cpp 28 | src/StateMachine.hpp 29 | src/StateMachine.cpp 30 | src/IntroState.hpp 31 | src/IntroState.cpp 32 | src/MenuState.hpp 33 | src/MenuState.cpp 34 | src/PlayState.hpp 35 | src/PlayState.cpp 36 | ) 37 | 38 | # Make sure that the runtime library gets linked statically 39 | if(SMALLGAMEENGINE_STATIC_STD_LIBS) 40 | if(NOT SFML_STATIC_LIBRARIES) 41 | message("\n-> If you check SMALLGAMEENGINE_STATIC_STD_LIBS, you also need to check SFML_STATIC_LIBRARIES.") 42 | message("-> It would lead to multiple runtime environments which results in undefined behavior.\n") 43 | elseif(WIN32 AND MSVC) 44 | set_property(TARGET SmallGameEngine PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 45 | elseif(CMAKE_COMPILER_IS_GNUCXX) 46 | # Note: Doesn't work for TDM compiler, since it's compiling the runtime libs statically by default 47 | target_compile_options(SmallGameEngine PRIVATE -static) 48 | endif() 49 | endif() 50 | 51 | # Link SFML 52 | target_link_libraries(SmallGameEngine sfml-graphics) 53 | 54 | # Install executable 55 | install(TARGETS SmallGameEngine 56 | RUNTIME DESTINATION .) 57 | 58 | # Install game data 59 | install(DIRECTORY bin/img 60 | DESTINATION .) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # SmallGameEngine Licenses 2 | 3 | *This software is available under 2 licenses -- choose whichever you prefer.* 4 | 5 | ## Public Domain 6 | 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | ## MIT License 31 | 32 | Copyright (c) 2012 - 2021 Lukas Dürrenberger 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all 42 | copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 50 | SOFTWARE. 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmallGameEngine 2 | 3 | This small game engine, or if you look at it in more detail it's actually just a state machine, is the result of a [thread in the SFML forum](https://en.sfml-dev.org/forums/index.php?topic=8536.0), where someone pointed to a [tutorial on a state machine](http://gamedevgeek.com/tutorials/managing-game-states-in-c/) programmed in C++ and intended for the graphics [Simple DirectMedia Library (SDL)](https://www.libsdl.org/). I've taken the question as a challenge and transformed the source code to make use of the [Simple and Fast Multimedia Library (SFML)](https://www.sfml-dev.org/) instead. Meanwhile I've added quite a few changes to not only provide a more modern approach to C++ by using features from the new C++17 standard, as well as utilizing idioms likes [RAII](https://bromeon.ch/articles/raii.html), but also to encapsulate classes a bit better. 4 | 5 | ## License 6 | 7 | This software is available under 2 licenses -- choose whichever you prefer. 8 | For further details take a look at the [LICENSE](https://github.com/eXpl0it3r/SmallGameEngine/blob/master/LICENSE) file. 9 | 10 | - Public Domain 11 | - MIT 12 | 13 | ## Credits 14 | 15 | A big thanks for [the tutorial](http://gamedevgeek.com/tutorials/managing-game-states-in-c/) in the first place goes out to [Anthony Lewis](https://anthonylewis.com/about/), make sure to checkout his [development blog](http://gamedevgeek.com/) and his [personal blog](https://anthonylewis.com/). 16 | Thanks to [kurayama](https://en.sfml-dev.org/forums/index.php?action=profile;u=2646) for the suggestion on using a template based `Build()` function rather than the `Instance()` functions embedded in every state class. -------------------------------------------------------------------------------- /bin/img/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eXpl0it3r/SmallGameEngine/1f2b0d762e641036cf61f1f234bf10ea6fdd181f/bin/img/intro.png -------------------------------------------------------------------------------- /bin/img/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eXpl0it3r/SmallGameEngine/1f2b0d762e641036cf61f1f234bf10ea6fdd181f/bin/img/menu.png -------------------------------------------------------------------------------- /bin/img/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eXpl0it3r/SmallGameEngine/1f2b0d762e641036cf61f1f234bf10ea6fdd181f/bin/img/play.png -------------------------------------------------------------------------------- /src/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.hpp" 2 | #include "IntroState.hpp" 3 | 4 | void Application::run() 5 | { 6 | // Create render window 7 | m_window.create({ 640, 480 }, "Engine Test v3.0", sf::Style::Titlebar | sf::Style::Close); 8 | m_window.setFramerateLimit(30); 9 | 10 | // Initialize the engine 11 | m_machine.run(StateMachine::build(m_machine, m_window, true)); 12 | 13 | // Main loop 14 | while (m_machine.running()) 15 | { 16 | m_machine.nextState(); 17 | m_machine.update(); 18 | m_machine.draw(); 19 | } 20 | 21 | // Leaving the scope of 'Application' will cleanup the engine 22 | } 23 | -------------------------------------------------------------------------------- /src/Application.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "StateMachine.hpp" 4 | 5 | #include 6 | 7 | class Application 8 | { 9 | public: 10 | void run(); 11 | 12 | private: 13 | StateMachine m_machine; 14 | sf::RenderWindow m_window; 15 | }; 16 | -------------------------------------------------------------------------------- /src/IntroState.cpp: -------------------------------------------------------------------------------- 1 | #include "IntroState.hpp" 2 | #include "PlayState.hpp" 3 | #include "StateMachine.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | IntroState::IntroState(StateMachine& machine, sf::RenderWindow& window, const bool replace) 12 | : State{ machine, window, replace } 13 | , m_alpha{ 0, 0, 0, 255 } // Start off opaque 14 | { 15 | if (!m_backgroundTexture.loadFromFile("img/intro.png")) 16 | { 17 | throw std::runtime_error{ "Was unable to load image 'img/intro.png'" }; 18 | } 19 | 20 | m_background.setTexture(m_backgroundTexture, true); 21 | 22 | // Fill the fader surface with black 23 | m_fader.setFillColor(m_alpha); 24 | m_fader.setSize(static_cast(m_backgroundTexture.getSize())); 25 | 26 | std::cout << "IntroState Init\n"; 27 | } 28 | 29 | void IntroState::pause() 30 | { 31 | std::cout << "IntroState Pause\n"; 32 | } 33 | 34 | void IntroState::resume() 35 | { 36 | std::cout << "IntroState Resume\n"; 37 | } 38 | 39 | void IntroState::update() 40 | { 41 | for (auto event = sf::Event{}; m_window.pollEvent(event);) 42 | { 43 | switch (event.type) 44 | { 45 | case sf::Event::Closed: 46 | m_machine.quit(); 47 | break; 48 | 49 | case sf::Event::KeyPressed: 50 | { 51 | switch (event.key.code) 52 | { 53 | case sf::Keyboard::Space: 54 | m_next = StateMachine::build(m_machine, m_window, true); 55 | break; 56 | 57 | case sf::Keyboard::Escape: 58 | m_machine.quit(); 59 | break; 60 | 61 | default: 62 | break; 63 | } 64 | break; 65 | } 66 | 67 | default: 68 | break; 69 | } 70 | } 71 | 72 | if (m_alpha.a != 0) 73 | { 74 | m_alpha.a--; 75 | } 76 | 77 | m_fader.setFillColor(m_alpha); 78 | } 79 | 80 | void IntroState::draw() 81 | { 82 | // Clear the previous drawing 83 | m_window.clear(); 84 | 85 | m_window.draw(m_background); 86 | 87 | // No need to draw if it's transparent 88 | if (m_alpha.a != 0) 89 | { 90 | m_window.draw(m_fader); 91 | } 92 | 93 | m_window.display(); 94 | } 95 | -------------------------------------------------------------------------------- /src/IntroState.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "State.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class StateMachine; 11 | 12 | namespace sf 13 | { 14 | class RenderWindow; 15 | } 16 | 17 | class IntroState final : public State 18 | { 19 | public: 20 | IntroState(StateMachine& machine, sf::RenderWindow& window, bool replace = true); 21 | 22 | void pause() override; 23 | void resume() override; 24 | 25 | void update() override; 26 | void draw() override; 27 | 28 | private: 29 | sf::Texture m_backgroundTexture; 30 | sf::Sprite m_background; 31 | sf::RectangleShape m_fader; 32 | sf::Color m_alpha; 33 | }; 34 | -------------------------------------------------------------------------------- /src/MenuState.cpp: -------------------------------------------------------------------------------- 1 | #include "StateMachine.hpp" 2 | #include "MenuState.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | MenuState::MenuState(StateMachine& machine, sf::RenderWindow& window, const bool replace) 10 | : State{ machine, window, replace } 11 | { 12 | if (!m_backgroundTexture.loadFromFile("img/menu.png")) 13 | { 14 | throw std::runtime_error{ "Was unable to load image 'img/menu.png'" }; 15 | } 16 | 17 | m_background.setTexture(m_backgroundTexture, true); 18 | 19 | std::cout << "MenuState Init\n"; 20 | } 21 | 22 | void MenuState::pause() 23 | { 24 | std::cout << "MenuState Pause\n"; 25 | } 26 | 27 | void MenuState::resume() 28 | { 29 | std::cout << "MenuState Resume\n"; 30 | } 31 | 32 | void MenuState::update() 33 | { 34 | for (auto event = sf::Event{}; m_window.pollEvent(event);) 35 | { 36 | switch (event.type) 37 | { 38 | case sf::Event::Closed: 39 | m_machine.quit(); 40 | break; 41 | 42 | case sf::Event::KeyPressed: 43 | switch (event.key.code) 44 | { 45 | case sf::Keyboard::Escape: 46 | m_machine.lastState(); 47 | break; 48 | 49 | default: 50 | break; 51 | } 52 | break; 53 | 54 | default: 55 | break; 56 | } 57 | } 58 | } 59 | 60 | void MenuState::draw() 61 | { 62 | // Clear the previous drawing 63 | m_window.clear(); 64 | m_window.draw(m_background); 65 | m_window.display(); 66 | } 67 | -------------------------------------------------------------------------------- /src/MenuState.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "State.hpp" 4 | 5 | #include 6 | #include 7 | 8 | class StateMachine; 9 | 10 | namespace sf 11 | { 12 | class RenderWindow; 13 | } 14 | 15 | class MenuState final : public State 16 | { 17 | public: 18 | MenuState(StateMachine& machine, sf::RenderWindow& window, bool replace = true); 19 | 20 | void pause() override; 21 | void resume() override; 22 | 23 | void update() override; 24 | void draw() override; 25 | 26 | private: 27 | sf::Texture m_backgroundTexture; 28 | sf::Sprite m_background; 29 | }; 30 | -------------------------------------------------------------------------------- /src/PlayState.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "StateMachine.hpp" 6 | #include "PlayState.hpp" 7 | #include "MenuState.hpp" 8 | 9 | #include 10 | #include 11 | 12 | PlayState::PlayState(StateMachine& machine, sf::RenderWindow& window, const bool replace) 13 | : State{ machine, window, replace } 14 | { 15 | if (!m_backgroundTexture.loadFromFile("img/play.png")) 16 | { 17 | throw std::runtime_error{ "Was unable to load image 'img/play.png'" }; 18 | } 19 | 20 | m_background.setTexture(m_backgroundTexture, true); 21 | 22 | std::cout << "PlayState Init\n"; 23 | } 24 | 25 | void PlayState::pause() 26 | { 27 | std::cout << "PlayState Pause\n"; 28 | } 29 | 30 | void PlayState::resume() 31 | { 32 | std::cout << "PlayState Resume\n"; 33 | } 34 | 35 | void PlayState::update() 36 | { 37 | for (auto event = sf::Event{}; m_window.pollEvent(event);) 38 | { 39 | switch (event.type) 40 | { 41 | case sf::Event::Closed: 42 | m_machine.quit(); 43 | break; 44 | 45 | case sf::Event::KeyPressed: 46 | switch (event.key.code) 47 | { 48 | case sf::Keyboard::Escape: 49 | m_machine.quit(); 50 | break; 51 | 52 | case sf::Keyboard::M: 53 | m_next = StateMachine::build(m_machine, m_window, false); 54 | break; 55 | 56 | default: 57 | break; 58 | } 59 | break; 60 | 61 | default: 62 | break; 63 | } 64 | } 65 | } 66 | 67 | void PlayState::draw() 68 | { 69 | // Clear the previous drawing 70 | m_window.clear(); 71 | m_window.draw(m_background); 72 | m_window.display(); 73 | } 74 | -------------------------------------------------------------------------------- /src/PlayState.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "State.hpp" 4 | 5 | #include 6 | #include 7 | 8 | class StateMachine; 9 | 10 | namespace sf 11 | { 12 | class RenderWindow; 13 | } 14 | 15 | class PlayState final : public State 16 | { 17 | public: 18 | PlayState(StateMachine& machine, sf::RenderWindow& window, bool replace = true); 19 | 20 | void pause() override; 21 | void resume() override; 22 | 23 | void update() override; 24 | void draw() override; 25 | 26 | private: 27 | sf::Texture m_backgroundTexture; 28 | sf::Sprite m_background; 29 | }; 30 | -------------------------------------------------------------------------------- /src/State.cpp: -------------------------------------------------------------------------------- 1 | #include "State.hpp" 2 | 3 | State::State(StateMachine& machine, sf::RenderWindow &window, const bool replace) 4 | : m_machine{ machine } 5 | , m_window{ window } 6 | , m_replacing{ replace } 7 | { 8 | 9 | } 10 | 11 | std::unique_ptr State::next() 12 | { 13 | return std::move( m_next ); 14 | } 15 | 16 | bool State::isReplacing() const 17 | { 18 | return m_replacing; 19 | } 20 | -------------------------------------------------------------------------------- /src/State.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class StateMachine; 6 | 7 | namespace sf 8 | { 9 | class RenderWindow; 10 | } 11 | 12 | class State 13 | { 14 | public: 15 | State(StateMachine& machine, sf::RenderWindow& window, bool replace = true); 16 | virtual ~State() = default; 17 | 18 | State(const State&) = delete; 19 | State& operator=(const State&) = delete; 20 | 21 | virtual void pause() = 0; 22 | virtual void resume() = 0; 23 | 24 | virtual void update() = 0; 25 | virtual void draw() = 0; 26 | 27 | std::unique_ptr next(); 28 | 29 | [[nodiscard]] bool isReplacing() const; 30 | 31 | protected: 32 | StateMachine& m_machine; 33 | sf::RenderWindow& m_window; 34 | 35 | bool m_replacing; 36 | 37 | std::unique_ptr m_next; 38 | }; 39 | -------------------------------------------------------------------------------- /src/StateMachine.cpp: -------------------------------------------------------------------------------- 1 | #include "StateMachine.hpp" 2 | #include "State.hpp" 3 | 4 | #include 5 | 6 | StateMachine::StateMachine() 7 | : m_resume{ false } 8 | , m_running{ false } 9 | { 10 | std::cout << "StateMachine Init\n"; 11 | } 12 | 13 | void StateMachine::run(std::unique_ptr state) 14 | { 15 | m_running = true; 16 | 17 | m_states.push(std::move(state)); 18 | } 19 | 20 | void StateMachine::nextState() 21 | { 22 | if (m_resume) 23 | { 24 | // Cleanup the current state 25 | if (!m_states.empty()) 26 | { 27 | m_states.pop(); 28 | } 29 | 30 | // Resume previous state 31 | if (!m_states.empty()) 32 | { 33 | m_states.top()->resume(); 34 | } 35 | 36 | m_resume = false; 37 | } 38 | 39 | // There needs to be a state 40 | if (!m_states.empty()) 41 | { 42 | auto temp = m_states.top()->next(); 43 | 44 | // Only change states if there's a next one existing 45 | if (temp != nullptr) 46 | { 47 | // Replace the running state 48 | if (temp->isReplacing()) 49 | { 50 | m_states.pop(); 51 | } 52 | // Pause the running state 53 | else 54 | { 55 | m_states.top()->pause(); 56 | } 57 | 58 | m_states.push(std::move(temp)); 59 | } 60 | } 61 | } 62 | 63 | void StateMachine::lastState() 64 | { 65 | m_resume = true; 66 | } 67 | 68 | void StateMachine::update() 69 | { 70 | // Let the state update the game 71 | m_states.top()->update(); 72 | } 73 | 74 | void StateMachine::draw() 75 | { 76 | // Let the state draw the screen 77 | m_states.top()->draw(); 78 | } 79 | 80 | bool StateMachine::running() const 81 | { 82 | return m_running; 83 | } 84 | 85 | void StateMachine::quit() 86 | { 87 | m_running = false; 88 | } 89 | -------------------------------------------------------------------------------- /src/StateMachine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "State.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace sf 11 | { 12 | class RenderWindow; 13 | } 14 | 15 | class StateMachine 16 | { 17 | public: 18 | StateMachine(); 19 | 20 | void run(std::unique_ptr state); 21 | 22 | void nextState(); 23 | void lastState(); 24 | 25 | void update(); 26 | void draw(); 27 | 28 | [[nodiscard]] bool running() const; 29 | void quit(); 30 | 31 | template 32 | static std::unique_ptr build(StateMachine& machine, sf::RenderWindow& window, bool replace = true); 33 | 34 | private: 35 | // The stack of states 36 | std::stack> m_states; 37 | 38 | bool m_resume; 39 | bool m_running; 40 | }; 41 | 42 | template 43 | std::unique_ptr StateMachine::build(StateMachine& machine, sf::RenderWindow& window, bool replace) 44 | { 45 | auto new_state = std::unique_ptr{ nullptr }; 46 | 47 | try 48 | { 49 | new_state = std::make_unique(machine, window, replace); 50 | } 51 | catch(std::runtime_error& exception) 52 | { 53 | std::cout << "Creation of new state was unsuccessful\n"; 54 | std::cout << exception.what() << std::endl; 55 | } 56 | 57 | return new_state; 58 | } 59 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.hpp" 2 | 3 | int main() 4 | { 5 | auto app = Application{}; 6 | app.run(); 7 | } 8 | --------------------------------------------------------------------------------