diff options
author | tcsullivan <tullivan99@gmail.com> | 2019-08-28 15:35:04 -0400 |
---|---|---|
committer | tcsullivan <tullivan99@gmail.com> | 2019-08-28 15:35:04 -0400 |
commit | 7a46fa2dd3dad3f038bf8e7339bc67abca428ae6 (patch) | |
tree | 767489e58ea257f3dec0d5b2ef5c0f3727f32a71 | |
parent | 3412f3bd9fc6cf9dbac0f0b5c8bc8a7ffe0c22a6 (diff) |
transition to game engine
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | src/engine.cpp | 75 | ||||
-rw-r--r-- | src/engine.hpp | 65 | ||||
-rw-r--r-- | src/gamerun.hpp | 76 | ||||
-rw-r--r-- | src/input.hpp | 88 | ||||
-rw-r--r-- | src/main.cpp | 101 | ||||
-rw-r--r-- | src/window.hpp | 95 |
7 files changed, 408 insertions, 97 deletions
@@ -35,14 +35,15 @@ CXXOBJ = $(patsubst $(CXXSRCDIR)/%.cpp, $(CXXOUTDIR)/%.o, $(CXXSRC)) EXEC = main -all: $(EXEC) +all: $(CXXOUTDIR) + @$(MAKE) $(EXEC) clean: @echo " CLEAN" @rm -f $(EXEC) @rm -rf out -$(EXEC): $(CXXOUTDIR) $(CXXOUTDIR)/$(CXXOBJ) +$(EXEC): $(CXXOUTDIR)/$(CXXOBJ) @echo " CXX/LD main" @$(CXX) $(CXXFLAGS) -o $(EXEC) $(CXXOBJ) $(LIBS) diff --git a/src/engine.cpp b/src/engine.cpp new file mode 100644 index 0000000..0bc647b --- /dev/null +++ b/src/engine.cpp @@ -0,0 +1,75 @@ +/** + * @file engine.cpp + * Manages all game systems. + * + * Copyright (C) 2019 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "engine.hpp" +#include "gamerun.hpp" +#include "input.hpp" +#include "window.hpp" + +int Engine::init(void) +{ + systems.add<GameRunSystem>(); + systems.add<InputSystem>(); + systems.add<WindowSystem>(); + + systems.configure(); + return 0; +} + +void Engine::logicLoop(void) +{ + using namespace std::chrono_literals; + + entityx::TimeDelta dt = 0; + + while (shouldRun()) { + systems.update<InputSystem>(dt); + std::this_thread::sleep_for(100ms); + } +} + +void Engine::renderLoop(void) +{ + while (shouldRun()) { + systems.update<WindowSystem>(0); + std::this_thread::yield(); + } +} + +void Engine::run(void) +{ + // Start logic thread + logicThread = std::thread([this](void) { + logicLoop(); + }); + + // Keep render loop on main thread + renderLoop(); + + // Done, bring logic thread back + logicThread.join(); +} + +bool Engine::shouldRun(void) +{ + auto grs = systems.system<GameRunSystem>(); + return grs ? grs->shouldRun() : true; +} + diff --git a/src/engine.hpp b/src/engine.hpp new file mode 100644 index 0000000..fb14093 --- /dev/null +++ b/src/engine.hpp @@ -0,0 +1,65 @@ +/** + * @file window.hpp + * Manages all game systems. + * + * Copyright (C) 2019 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef ENGINE_HPP_ +#define ENGINE_HPP_ + +#include <entityx/entityx.h> + +#include <thread> + +/** + * @class Engine + * Manages all game entities, events, and systems. + */ +class Engine +{ +private: + entityx::EventManager events; + entityx::EntityManager entities; + entityx::SystemManager systems; + + std::thread logicThread; + + void logicLoop(void); + void renderLoop(void); + + bool shouldRun(void); + +public: + Engine(void) : + entities(events), + systems(entities, events) {} + + /** + * Initializes the game engine. + * @return Zero on success, non-zero otherwise + */ + int init(void); + + /** + * Runs the game engine. + * Function returns when game is told to end/exit. + */ + void run(void); +}; + +#endif // ENGINE_HPP_ + diff --git a/src/gamerun.hpp b/src/gamerun.hpp new file mode 100644 index 0000000..efe6ed9 --- /dev/null +++ b/src/gamerun.hpp @@ -0,0 +1,76 @@ +/** + * @file window.hpp + * Handles key press for exiting the game. + * + * Copyright (C) 2019 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GAMERUN_HPP_ +#define GAMERUN_HPP_ + +#include "input.hpp" + +#include <entityx/entityx.h> + +#include <atomic> +#include <iostream> + +/** + * @class GameRunSystem + * Listens for a key event to tell the game to exit. + */ +class GameRunSystem : public entityx::System<GameRunSystem>, + public entityx::Receiver<GameRunSystem> { +private: + std::atomic_bool shouldRunFlag; + +public: + /** + * Configures the system to wait for the exit event. + */ + void configure([[maybe_unused]] entityx::EntityManager& entities, + entityx::EventManager& events) { + shouldRunFlag.store(true); + + events.subscribe<KeyUpEvent>(*this); + + std::cout << "Press escape to exit." << std::endl; + } + + // Unused + void update([[maybe_unused]] entityx::EntityManager& entities, + [[maybe_unused]] entityx::EventManager& events, + [[maybe_unused]] entityx::TimeDelta dt) final {} + + /** + * Receiver for key release events, used to listen for game exit key. + */ + void receive(const KeyUpEvent& kue) { + if (kue.sym == SDLK_ESCAPE) + shouldRunFlag.store(false); + } + + /** + * Checks if the game exit key has been pressed. + * @return True if the game should exit + */ + inline bool shouldRun(void) const { + return shouldRunFlag.load(); + } +}; + +#endif // GAMERUN_HPP_ + diff --git a/src/input.hpp b/src/input.hpp new file mode 100644 index 0000000..39d4045 --- /dev/null +++ b/src/input.hpp @@ -0,0 +1,88 @@ +/** + * @file window.hpp + * Handles user input received from SDL. + * + * Copyright (C) 2019 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef INPUT_HPP_ +#define INPUT_HPP_ + +#include <entityx/entityx.h> +#include <SDL2/SDL.h> + +/** + * @class KeyUpEvent + * Stores info regarding key releases. + */ +struct KeyUpEvent { + KeyUpEvent(const SDL_Keysym& keysym) : + sym(keysym.sym), mod(keysym.mod) {} + + SDL_Keycode sym; + Uint16 mod; +}; + +/** + * @class KeyDownEvent + * Stores info regarding key presses. + */ +struct KeyDownEvent { + KeyDownEvent(const SDL_Keysym& keysym) : + sym(keysym.sym), mod(keysym.mod) {} + + SDL_Keycode sym; + Uint16 mod; +}; + +/** + * @class InputSystem + * Listens for user input from SDL, and emits input events accordingly. + */ +class InputSystem : public entityx::System<InputSystem> { +public: + /** + * Prepares the system for running. + */ + void configure([[maybe_unused]] entityx::EntityManager& entities, + [[maybe_unused]] entityx::EventManager& events) final { + } + + /** + * Updates the system by checking for SDL events. + */ + void update([[maybe_unused]] entityx::EntityManager& entities, + [[maybe_unused]] entityx::EventManager& events, + [[maybe_unused]] entityx::TimeDelta dt) final { + for (SDL_Event event; SDL_PollEvent(&event);) { + switch (event.type) { + case SDL_KEYUP: + if (auto key = event.key; key.repeat == 0) + events.emit<KeyUpEvent>(key.keysym); + break; + case SDL_KEYDOWN: + if (auto key = event.key; key.repeat == 0) + events.emit<KeyDownEvent>(key.keysym); + break; + default: + break; + } + } + } +}; + +#endif // INPUT_HPP_ + diff --git a/src/main.cpp b/src/main.cpp index 5e5e278..a1c605e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,67 +18,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include <entityx/entityx.h> +#include "engine.hpp" #include <SDL2/SDL.h> -#include <atomic> -#include <chrono> #include <iostream> -#include <memory> -#include <thread> - -class Window { -private: - constexpr static const char *title = "gamedev2"; - constexpr static int width = 640; - constexpr static int height = 480; - - static std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> window; - static SDL_GLContext context; - - static void destroyWindow(SDL_Window *w) { - SDL_GL_DeleteContext(context); - SDL_DestroyWindow(w); - } - -public: - static int init(void) { - if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { - std::cerr << "SDL video failed to initialize: " - << SDL_GetError() << std::endl; - return -1; - } - - window.reset(SDL_CreateWindow(title, - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - width, height, - SDL_WINDOW_OPENGL)); - - if (window.get() == nullptr) { - std::cerr << "SDL window creation failed: " - << SDL_GetError() << std::endl; - return -1; - } - - context = SDL_GL_CreateContext(window.get()); - - return 0; - } - - static void render(void) { - SDL_GL_SwapWindow(window.get()); - } -}; - -std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> Window::window (nullptr, - Window::destroyWindow); -SDL_GLContext Window::context; - -std::atomic_bool shouldRun; - -static void renderLoop(void); -static void logicLoop(void); int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) { @@ -91,46 +35,13 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) atexit(SDL_Quit); } - // Create our window - Window::init(); + // Create the engine + Engine engine; + engine.init(); - // Start game - shouldRun.store(true); - std::thread logic (logicLoop); - renderLoop(); - logic.join(); + // Go go go! + engine.run(); return 0; } -void renderLoop(void) -{ - while (shouldRun.load()) { - Window::render(); - std::this_thread::yield(); - } -} - -void logicLoop(void) -{ - using namespace std::chrono_literals; - - std::cout << "Press escape to exit." << std::endl; - - while (shouldRun.load()) { - for (SDL_Event event; SDL_PollEvent(&event);) { - switch (event.type) { - case SDL_KEYUP: - // Exit game on escape - if (event.key.keysym.sym == SDLK_ESCAPE) - shouldRun.store(false); - break; - default: - break; - } - } - - std::this_thread::sleep_for(100ms); - } -} - diff --git a/src/window.hpp b/src/window.hpp new file mode 100644 index 0000000..ef6632a --- /dev/null +++ b/src/window.hpp @@ -0,0 +1,95 @@ +/** + * @file window.hpp + * Manages window creation. + * + * Copyright (C) 2019 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WINDOW_HPP_ +#define WINDOW_HPP_ + +#include <entityx/entityx.h> +#include <SDL2/SDL.h> + +/** + * @class WindowSystem + * Handles the game's window. + */ +class WindowSystem : public entityx::System<WindowSystem> { +private: + constexpr static const char *title = "gamedev2"; + constexpr static int width = 640; + constexpr static int height = 480; + + std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> window; + SDL_GLContext context; + +public: + WindowSystem(void) : + window(nullptr, SDL_DestroyWindow) {} + + ~WindowSystem(void) { + SDL_GL_DeleteContext(context); + } + + /** + * Creates and initializes the window. + * @return Zero on success, non-zero on error + */ + int init(void) { + if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { + std::cerr << "SDL video failed to initialize: " + << SDL_GetError() << std::endl; + return -1; + } + + // Create window, managed by the unique_ptr + window.reset(SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, + SDL_WINDOW_OPENGL)); + + if (window.get() == nullptr) { + std::cerr << "SDL window creation failed: " + << SDL_GetError() << std::endl; + return -1; + } + + context = SDL_GL_CreateContext(window.get()); + + return 0; + } + + /** + * Prepares the system for running. + */ + void configure([[maybe_unused]] entityx::EntityManager& entities, + [[maybe_unused]] entityx::EventManager& events) final { + init(); + } + + /** + * Updates/refreshes the window. + */ + void update([[maybe_unused]] entityx::EntityManager& entities, + [[maybe_unused]] entityx::EventManager& events, + [[maybe_unused]] entityx::TimeDelta dt) final { + SDL_GL_SwapWindow(window.get()); + } +}; + +#endif // WINDOW_HPP_ + |