├── .gitignore ├── src ├── ncurses │ └── README.md ├── sdl │ ├── README.md │ ├── game_loop.hpp │ ├── font_maker.hpp │ ├── controller.hpp │ ├── game_font_builder.cc │ ├── controller.cc │ ├── font_maker.cc │ ├── game_loop.cc │ ├── typography.cc │ ├── game_font_builder.hpp │ ├── renderer.cc │ ├── renderer.hpp │ └── typography.hpp ├── resources │ └── fonts │ │ ├── BitPotion.ttf │ │ └── PressStart2P.ttf ├── core │ ├── game_thread.cc │ ├── block.cc │ ├── random_position.cc │ ├── controller.hpp │ ├── font.cc │ ├── game_font.hpp │ ├── game_thread.hpp │ ├── block.hpp │ ├── game_loop.hpp │ ├── game_state.hpp │ ├── game_object.hpp │ ├── random_position.hpp │ ├── renderer.hpp │ ├── game.cc │ ├── allocation.inl │ ├── button_chooser.cc │ ├── vector2.inl │ ├── color.cc │ ├── font.hpp │ ├── food.hpp │ ├── food.cc │ ├── button_chooser.hpp │ ├── game.hpp │ ├── allocation.hpp │ ├── vector2.hpp │ └── color.hpp ├── main.cc ├── CMakeLists.txt ├── states │ ├── pause_state.hpp │ ├── main_state.cc │ ├── main_state.hpp │ ├── pause_state.cc │ ├── splash_screen_state.hpp │ ├── splash_screen_state.cc │ ├── end_game_state.hpp │ └── end_game_state.cc └── actor │ ├── snake.hpp │ └── snake.cc ├── .gitmodules ├── tests ├── order.cc ├── CMakeLists.txt └── core │ ├── color_test.cc │ ├── allocation_test.cc │ └── vector2_test.cc ├── .travis.yml ├── CMakeLists.txt ├── cmake ├── FindSDL2_ttf.cmake └── FindSDL2_image.cmake └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor -------------------------------------------------------------------------------- /src/ncurses/README.md: -------------------------------------------------------------------------------- 1 | # NCurses version 2 | 3 | comming soon.. -------------------------------------------------------------------------------- /src/sdl/README.md: -------------------------------------------------------------------------------- 1 | # SDL version 2 | 3 | It's a simple SDL implementation of the snake game -------------------------------------------------------------------------------- /src/resources/fonts/BitPotion.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihguy/cpp-snake-game/HEAD/src/resources/fonts/BitPotion.ttf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/googletest"] 2 | path = vendor/googletest 3 | url = https://github.com/v-borg/googletest 4 | -------------------------------------------------------------------------------- /src/resources/fonts/PressStart2P.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihguy/cpp-snake-game/HEAD/src/resources/fonts/PressStart2P.ttf -------------------------------------------------------------------------------- /tests/order.cc: -------------------------------------------------------------------------------- 1 | #include "gmock/gmock.h" 2 | #include "gtest/gtest.h" 3 | 4 | #include "core/color_test.cc" 5 | #include "core/vector2_test.cc" 6 | #include "core/allocation_test.cc" 7 | 8 | int main(int argc, char* argv[]) { 9 | ::testing::InitGoogleMock(&argc, argv); 10 | return RUN_ALL_TESTS(); 11 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | # For Ubuntu 14.04 C++11/15 support ... 4 | sudo: required 5 | dist: trusty 6 | 7 | compiler: 8 | - gcc 9 | 10 | before_script: 11 | - git submodule update --init --recursive 12 | - sudo apt-get install build-essential 13 | - sudo apt-get install cmake 14 | - mkdir build 15 | - cd build 16 | - cmake .. -DBUILD_TESTS_ONLY=On 17 | - make 18 | 19 | script: ./tests/order -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(cpp_snake_game_tests) 3 | 4 | include_directories(../ ../vendor/googletest/googletest/include ../vendor/googletest/googlemock/include) 5 | 6 | add_executable(order order.cc core/allocation_test.cc ../src/core/allocation.hpp ../src/core/allocation.inl ../src/core/color.hpp ../src/core/color.cc ../src/core/vector2.hpp ../src/core/vector2.inl) 7 | 8 | target_link_libraries(order gmock gtest) 9 | -------------------------------------------------------------------------------- /src/core/game_thread.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "game_thread.hpp" 4 | 5 | namespace Capstone 6 | { 7 | 8 | // This share the same mutex to all inherited classes 9 | std::mutex GameThread::m_mutex; 10 | 11 | GameThread::~GameThread () 12 | { 13 | // This set up thread barrier before this object is destroyed 14 | std::for_each(m_threads.begin (), m_threads.end (), [](std::thread &t) 15 | { 16 | t.join(); 17 | }); 18 | } 19 | 20 | } // namespace Capstone 21 | -------------------------------------------------------------------------------- /src/sdl/game_loop.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_GAME_LOOP_HPP 3 | #define CPP_SNAKE_GAME_SDL_GAME_LOOP_HPP 4 | 5 | #include "../core/game_loop.hpp" 6 | 7 | namespace SDL 8 | { 9 | 10 | /** 11 | * 12 | */ 13 | class GameLoop: public Capstone::GameLoop { 14 | public: 15 | GameLoop(std::size_t target_frame_duration); 16 | ~GameLoop () = default; 17 | 18 | bool execute (Capstone::Game& game, Capstone::Renderer &renderer, Capstone::Controller &controller) const override; 19 | 20 | protected: 21 | std::size_t m_frame_duration; 22 | }; 23 | } // namespace SDL 24 | 25 | #endif // CPP_SNAKE_GAME_SDL_GAME_LOOP_HPP 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(cpp_snake_game) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | option(BUILD_TESTS "Use BUILD_TESTS to compile the project and its test cases as well" OFF) # OFF by default 7 | option(BUILD_TESTS_ONLY "Use BUILD_TESTS_ONLY to compile only the test cases without the project" OFF) # OFF by default 8 | 9 | if (BUILD_TESTS OR BUILD_TESTS_ONLY) 10 | message("Building tests") 11 | set(BUILD_GMOCK ON) 12 | add_subdirectory(vendor/googletest) 13 | add_subdirectory(tests) 14 | endif() 15 | 16 | if (NOT BUILD_TESTS_ONLY) 17 | add_subdirectory(src) 18 | endif() 19 | 20 | unset(BUILD_TESTS CACHE) 21 | unset(BUILD_TESTS_ONLY CACHE) 22 | -------------------------------------------------------------------------------- /src/sdl/font_maker.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_FONT_MAKER_HPP 3 | #define CPP_SNAKE_GAME_SDL_FONT_MAKER_HPP 4 | 5 | #include 6 | #include "game_font_builder.hpp" 7 | 8 | namespace SDL 9 | { 10 | 11 | /** 12 | * This returns the correct FontDescription based on arguments 13 | * assigned to the function 14 | * 15 | * This function holds all font's name, color and size used 16 | * by SDL version of the game. 17 | * 18 | * @param theme The theme enum used to create the font pattern under the hoods 19 | * @param size The size enum used to create the font pattern under the hoods 20 | * @return 21 | */ 22 | FontDescription font_maker(const Capstone::FontTheme &theme, const Capstone::FontSize &size); 23 | 24 | } // namespace SDL 25 | 26 | #endif // CPP_SNAKE_GAME_SDL_FONT_MAKER_HPP 27 | -------------------------------------------------------------------------------- /src/core/block.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "block.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | Block::Block (): 8 | iAllocation::Allocation{{ 0 }, { 0 }} 9 | { 10 | 11 | } 12 | 13 | Block::Block (iVector2 size, iVector2 offset): 14 | iAllocation::Allocation{ size, offset } 15 | { 16 | 17 | } 18 | 19 | void Block::update (std::size_t delta_time) 20 | { 21 | 22 | } 23 | 24 | void Block::render (Renderer& renderer) 25 | { 26 | // Defines the color and display the current GameObject into the screen 27 | renderer.fill_color (color); 28 | renderer.fill(*this); 29 | } 30 | 31 | void Block::prepare (Renderer& renderer) 32 | { 33 | size = iVector2( 34 | renderer.get_screen ().x / renderer.get_grid ().x, 35 | renderer.get_screen ().y / renderer.get_grid ().y 36 | ); 37 | } 38 | 39 | } // namespace Capstone 40 | -------------------------------------------------------------------------------- /src/core/random_position.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "random_position.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | // Parametrized constructor 8 | // Create the random number to be used by the axes position 9 | RandomPosition::RandomPosition (const iVector2& delimiter): 10 | m_engine(m_dev()), 11 | m_random(0, RAND_MAX), 12 | delimiter(delimiter) 13 | { 14 | 15 | } 16 | int RandomPosition::get_x () 17 | { 18 | // This defines a random between zero and the value defined as max.x 19 | return m_random(m_engine) % (delimiter.x - 1); 20 | } 21 | 22 | int RandomPosition::get_y () 23 | { 24 | // This defines a random between zero and the value defined as max.y 25 | return m_random(m_engine) % (delimiter.y - 1); 26 | } 27 | 28 | iVector2 RandomPosition::get () 29 | { 30 | // This defines a random Vector2 between zero and the value defined 31 | // on delimiter variable 32 | return { 33 | get_x (), 34 | get_y () 35 | }; 36 | } 37 | 38 | } // namespace Capstone 39 | -------------------------------------------------------------------------------- /src/sdl/controller.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_CONTROLLER_HPP 3 | #define CPP_SNAKE_GAME_SDL_CONTROLLER_HPP 4 | 5 | #include "../core/controller.hpp" 6 | 7 | namespace SDL 8 | { 9 | 10 | /** 11 | * It's a simple input handler using some fixed keys to dispatch commands 12 | * like right, left up, down and start. It can be used to manipulate the 13 | * GameObject respecting the GameState rules. 14 | */ 15 | class Controller: public Capstone::Controller { 16 | public: 17 | 18 | /** 19 | * This handles the input dispatched by the user 20 | * 21 | * It defines if the Key pressed by the user is a valid command 22 | * and return a enum representing this command. 23 | * 24 | * @param running This boolean defines if the close should be closed 25 | * @return KeyPressed The command equivalent to key pressed by the user 26 | */ 27 | Capstone::KeyPressed handle_input (bool& running) override; 28 | }; 29 | } 30 | #endif // CPP_SNAKE_GAME_SDL_CONTROLLER_HPP 31 | -------------------------------------------------------------------------------- /src/core/controller.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_SNAKE_GAME_CORE_CONTROLLER_HPP 2 | #define CPP_SNAKE_GAME_CORE_CONTROLLER_HPP 3 | 4 | #include "game.hpp" 5 | 6 | namespace Capstone 7 | { 8 | 9 | class Game; 10 | 11 | /** 12 | * This defines the keys used to handle the game 13 | */ 14 | enum class KeyPressed 15 | { 16 | kUndefined, 17 | kUp, 18 | kLeft, 19 | kDown, 20 | kRight, 21 | kEnter, 22 | }; 23 | 24 | /** 25 | * 26 | */ 27 | class Controller 28 | { 29 | public: 30 | 31 | /** 32 | * This handles the input dispatched by the user 33 | * 34 | * It defines if the Key pressed by the user is a valid command 35 | * and return a enum representing this command. 36 | * 37 | * @param running This boolean defines if the close should be closed 38 | * @return KeyPressed The command equivalent to key pressed by the user 39 | */ 40 | virtual KeyPressed handle_input(bool& running) = 0; 41 | }; 42 | 43 | } // namespace Capstone 44 | 45 | #endif // CPP_SNAKE_GAME_CORE_CONTROLLER_HPP -------------------------------------------------------------------------------- /src/sdl/game_font_builder.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "game_font_builder.hpp" 3 | #include "typography.hpp" 4 | 5 | namespace SDL 6 | { 7 | 8 | // This defines each font pattern used on game 9 | void GameFontBuilder::set_font (const Capstone::FontTheme& theme, const Capstone::FontSize& size, const FontDescription font_description) 10 | { 11 | m_font_map[std::make_pair (theme, size)] = font_description; 12 | } 13 | 14 | // This returns each font pattern used on game 15 | std::unique_ptr GameFontBuilder::create (Capstone::FontTheme&& theme, Capstone::FontSize&& size) const 16 | { 17 | auto font = m_font_map.find(std::make_pair (theme, size)); 18 | 19 | // This throw and error if the font is not assigned to the map. 20 | if (font == m_font_map.end()) 21 | { 22 | throw std::runtime_error("A font is not correct assigned to the GameFontBuilder::set_font(...) method."); 23 | } 24 | 25 | auto font_description = font->second; 26 | auto font_instance = std::make_unique (new Typography(font_description.path, font_description.size)); 27 | font_instance->color = font_description.color; 28 | 29 | return std::move(font_instance); 30 | } 31 | } // namespace SDL 32 | -------------------------------------------------------------------------------- /src/sdl/controller.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "controller.hpp" 4 | 5 | namespace SDL 6 | { 7 | 8 | Capstone::KeyPressed Controller::handle_input (bool& running) 9 | { 10 | SDL_Event event; 11 | 12 | // Ensures the GameState in focus can manipulate their own events and, 13 | // allows `is_running` changes their value by SDL_QUIT event dispatch 14 | while (SDL_PollEvent (&event)) 15 | { 16 | if (event.type == SDL_QUIT) 17 | { 18 | running = false; 19 | } 20 | 21 | if (event.key.type == SDL_KEYDOWN) 22 | { 23 | // this defines the return as enter and the arrows as 24 | // "directional" inputs 25 | switch (event.key.keysym.sym) 26 | { 27 | case SDLK_KP_ENTER: 28 | case SDLK_RETURN: 29 | return Capstone::KeyPressed::kEnter; 30 | case SDLK_UP: 31 | return Capstone::KeyPressed::kUp; 32 | case SDLK_LEFT: 33 | return Capstone::KeyPressed::kLeft; 34 | case SDLK_DOWN: 35 | return Capstone::KeyPressed::kDown; 36 | case SDLK_RIGHT: 37 | return Capstone::KeyPressed::kRight; 38 | default: 39 | return Capstone::KeyPressed::kUndefined; 40 | } 41 | } 42 | } 43 | 44 | return Capstone::KeyPressed::kUndefined; 45 | } 46 | } // namespace SDL 47 | 48 | -------------------------------------------------------------------------------- /src/core/font.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "font.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | // It adds the third party logic to the default Font's behavior 8 | Font::Font (Typography *typography): 9 | m_typography{typography} 10 | { 11 | 12 | } 13 | 14 | // This deletes the Typography pointer so satisfy the Udacity's Project Specification 15 | Font::~Font () 16 | { 17 | delete m_typography; 18 | } 19 | 20 | // This updates the font using third party font logic. 21 | void Font::update (std::size_t delta_time) 22 | { 23 | m_typography->update (*this); 24 | } 25 | 26 | // This renders the font using third party font logic. 27 | void Font::render (Renderer& renderer) 28 | { 29 | m_typography->get (*this); 30 | } 31 | 32 | // To satisfy the SDL's font logic, the color and text font should be setted 33 | // before the `prepare` method 34 | void Font::prepare (Renderer& renderer) 35 | { 36 | // It ensures the font dimension can be accessed correctly on GameState's `prepare` method 37 | if (m_text.empty ()) 38 | { 39 | throw std::runtime_error("Font error: set_text() method should be invoked before preparing the class."); 40 | } 41 | 42 | m_typography->prepare (renderer); 43 | m_typography->create_texture (*this); 44 | } 45 | 46 | // This assign the text to be rendered 47 | void Font::set_text (const std::string& text) 48 | { 49 | m_text = text; 50 | } 51 | 52 | // This get the text to be rendered 53 | std::string Font::get_text () const 54 | { 55 | return m_text; 56 | } 57 | 58 | } // namespace Capstone 59 | -------------------------------------------------------------------------------- /src/sdl/font_maker.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "font_maker.hpp" 3 | 4 | namespace SDL 5 | { 6 | 7 | // It creates the font pattern used by SDL version of the game 8 | FontDescription font_maker (const Capstone::FontTheme& theme, const Capstone::FontSize& size) 9 | { 10 | FontDescription font_description; 11 | font_description.color = Capstone::Color::Black; 12 | 13 | switch(size) 14 | { 15 | case Capstone::FontSize::kSmall: 16 | font_description.path = "resources/fonts/BitPotion.ttf"; 17 | font_description.size = 50; 18 | 19 | if (theme == Capstone::FontTheme::kDark) 20 | { 21 | font_description.color = Capstone::Color(0xF0F7F4FF); 22 | } 23 | else 24 | { 25 | font_description.color = Capstone::Color(0x333333FF); 26 | } 27 | break; 28 | 29 | case Capstone::FontSize::kMedium: 30 | font_description.path = "resources/fonts/PressStart2P.ttf"; 31 | font_description.size = 30; 32 | 33 | if (theme == Capstone::FontTheme::kDark) 34 | { 35 | font_description.color = Capstone::Color(0x99E1D9FF); 36 | } 37 | break; 38 | 39 | case Capstone::FontSize::kLarge: 40 | font_description.path = "resources/fonts/PressStart2P.ttf"; 41 | font_description.size = 40; 42 | 43 | if (theme == Capstone::FontTheme::kDark) 44 | { 45 | font_description.color = Capstone::Color(0x99E1D9FF); 46 | } 47 | break; 48 | } 49 | 50 | return font_description; 51 | } 52 | 53 | } // namespace SDL -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "core/controller.hpp" 3 | #include "core/game.hpp" 4 | 5 | #include "sdl/renderer.hpp" 6 | #include "sdl/game_loop.hpp" 7 | #include "sdl/controller.hpp" 8 | #include "sdl/game_font_builder.hpp" 9 | #include "sdl/font_maker.hpp" 10 | #include "states/splash_screen_state.hpp" 11 | 12 | int main() { 13 | try 14 | { 15 | constexpr std::size_t kFramesPerSecond{60}; 16 | constexpr std::size_t kMsPerFrame{1000 / kFramesPerSecond}; 17 | 18 | auto renderer = std::make_unique (Capstone::iVector2{640, 640}, Capstone::iVector2{32, 32}); 19 | auto game_font = std::make_unique (); 20 | 21 | SDL::GameLoop game_loop{kMsPerFrame}; 22 | SDL::Controller controller{}; 23 | 24 | Capstone::FontTheme font_theme_list[2] = { Capstone::FontTheme::kLight, Capstone::FontTheme::kDark }; 25 | Capstone::FontSize font_size_list[4] = { Capstone::FontSize::kSmall, Capstone::FontSize::kMedium, Capstone::FontSize::kLarge }; 26 | 27 | for (const auto &theme : font_theme_list) 28 | { 29 | for (const auto &size : font_size_list) 30 | { 31 | game_font->set_font (theme, size, SDL::font_maker (theme, size)); 32 | } 33 | } 34 | 35 | Capstone::Game game{std::move (renderer), std::move(game_font)}; 36 | game.push_state (std::make_unique ()); 37 | 38 | return game.render (game_loop, controller); 39 | } 40 | catch (std::exception& error) 41 | { 42 | std::cerr << error.what () << std::endl; 43 | } 44 | } -------------------------------------------------------------------------------- /src/core/game_font.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_CORE_GAME_FONT_HPP 3 | #define CPP_SNAKE_CORE_GAME_FONT_HPP 4 | 5 | #include "font.hpp" 6 | 7 | namespace Capstone 8 | { 9 | 10 | /** 11 | * This defines the font size that could be used on game 12 | */ 13 | enum class FontSize 14 | { 15 | kSmall, 16 | kMedium, 17 | kLarge 18 | }; 19 | 20 | /** 21 | * This defines the themes that could be used on game 22 | */ 23 | enum class FontTheme 24 | { 25 | kDark, 26 | kLight 27 | }; 28 | 29 | /** 30 | * This class is used by Game to return the correct font using its 31 | * get_font method. 32 | * 33 | * It's useful to uncouple the logic of the current library to get 34 | * the font properly. 35 | * 36 | */ 37 | class GameFont { 38 | public: 39 | /** 40 | * This enforces the inherited class to offset a simple way to return 41 | * the expected font using the FontTheme and the FontSize. It allows 42 | * the GameState to use the font without "know" their real attributes 43 | * like color, size or the method used to create that. 44 | * 45 | * @param theme This allows an external class to defines the font 46 | * attributes such as color and font name 47 | * @param size This alloes an external class to defines the font 48 | * attributes such as size and font name 49 | * @return std::unique_ptr The smart pointer of the "configured" font 50 | */ 51 | virtual std::unique_ptr create(FontTheme &&theme, FontSize &&size) const = 0; 52 | }; 53 | 54 | } // namespace Capstone 55 | 56 | 57 | #endif // CPP_SNAKE_CORE_GAME_FONT_HPP 58 | -------------------------------------------------------------------------------- /src/core/game_thread.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_GAME_THREAD_HPP 3 | #define CPP_SNAKE_GAME_CORE_GAME_THREAD_HPP 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "vector2.hpp" 10 | #include "game_object.hpp" 11 | 12 | namespace Capstone 13 | { 14 | 15 | /** 16 | * This defines the possible status used to each object to interact 17 | * with Snake GameObject 18 | */ 19 | enum class SnakeStatus 20 | { 21 | kWalking, 22 | kColliding, 23 | kEating 24 | }; 25 | 26 | /** 27 | * This ensures the thread and mutex and a thread barrier for the 28 | * GameObject inherited from this class. 29 | */ 30 | class GameThread: public GameObject { 31 | public: 32 | 33 | /** 34 | * Destructor 35 | * 36 | * This creates a thread barrier for the inherited classes 37 | */ 38 | virtual ~GameThread(); 39 | 40 | /** 41 | * Get the status on collision 42 | * 43 | * It ensures the inherited class will returns the status on collision. 44 | * 45 | * @return SnakeStatus The status dispatched by the GameThread collision 46 | */ 47 | virtual const SnakeStatus get_status () const = 0; 48 | 49 | protected: 50 | /** 51 | * This holds all thread that have been launched within this 52 | * object 53 | */ 54 | std::vector m_threads; 55 | 56 | /** 57 | * This defines the mutex shared by all objects for protecting 58 | * them from unexpected external modifications 59 | */ 60 | static std::mutex m_mutex; 61 | }; 62 | } // namespace Capstone 63 | 64 | 65 | #endif // CPP_SNAKE_GAME_CORE_GAME_THREAD_HPP 66 | -------------------------------------------------------------------------------- /src/sdl/game_loop.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "game_loop.hpp" 5 | 6 | 7 | namespace SDL 8 | { 9 | 10 | 11 | GameLoop::GameLoop (std::size_t target_frame_duration): 12 | m_frame_duration{target_frame_duration} 13 | { 14 | 15 | } 16 | 17 | bool GameLoop::execute (Capstone::Game& game, Capstone::Renderer &renderer, Capstone::Controller &controller) const 18 | { 19 | Uint32 frame_start; 20 | Uint32 frame_end; 21 | Uint32 frame_duration; 22 | 23 | SDL_Event event; 24 | 25 | // Execute the game loop using a fixed time between each frame 26 | while (game.is_running) 27 | { 28 | frame_start = SDL_GetTicks (); 29 | 30 | // This ensures the GameState in focus will receive the elapsed time since 31 | // their last execution 32 | game.peek_state ()->update (m_frame_duration); 33 | 34 | // This allows the current GameState to manipulate their own events 35 | game.peek_state ()->handle_input (controller.handle_input (game.is_running)); 36 | 37 | // This assign the same instance of Capstone::Renderer for all GameState on stack 38 | game.peek_state ()->render (renderer); 39 | 40 | frame_end = SDL_GetTicks (); 41 | frame_duration = frame_end - frame_start; 42 | 43 | // Always the execution is faster than expected, wait until FPS specified 44 | if (frame_duration < m_frame_duration) 45 | { 46 | SDL_Delay (m_frame_duration - frame_duration); 47 | } 48 | } 49 | 50 | // If the game loop was broken the window should be close, so we return false 51 | return false; 52 | } 53 | } // namespace SDL 54 | -------------------------------------------------------------------------------- /src/core/block.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_BLOCK_HPP 3 | #define CPP_SNAKE_GAME_CORE_BLOCK_HPP 4 | 5 | #include "renderer.hpp" 6 | #include "allocation.hpp" 7 | #include "game_object.hpp" 8 | 9 | namespace Capstone 10 | { 11 | class Block: public iAllocation, public GameObject 12 | { 13 | public: 14 | /** 15 | * Default constructor 16 | */ 17 | Block(); 18 | 19 | /** 20 | * Parameterized constructor 21 | * 22 | * @param size The height and the width of the object 23 | * @param offset The horizontal and vertical offset of the object 24 | */ 25 | explicit Block(iVector2 size, iVector2 offset); 26 | 27 | /** 28 | * This method is implemented to supply GameObject interface, 29 | * but it's not used by Block class 30 | * 31 | * @param delta_time The elapsed time since the last game loop 32 | */ 33 | void update(std::size_t delta_time) override; 34 | 35 | /** 36 | * This renders the current GameObject 37 | * 38 | * It allows the current class to define the GameObject color and 39 | * display it on the screen. 40 | * 41 | * @param renderer The common Renderer instance 42 | */ 43 | void render(Renderer &renderer) override; 44 | 45 | /** 46 | * This method is implemented to supply GameObject interface, 47 | * but it's not used by Block class 48 | * 49 | * @param Renderer The common Renderer instance 50 | */ 51 | void prepare(Renderer &renderer) override; 52 | 53 | public: 54 | /** 55 | * This defines the color of the current GameObject. 56 | */ 57 | Color color; 58 | }; 59 | 60 | } // namespace Capstone 61 | 62 | #endif // CPP_SNAKE_GAME_CORE_BLOCK_HPP 63 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 4 | 5 | find_package (Threads REQUIRED) 6 | find_package(SDL2 REQUIRED) 7 | find_package(SDL2_image REQUIRED) 8 | find_package(SDL2_ttf REQUIRED) 9 | 10 | include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIR}) 11 | 12 | add_executable(${CMAKE_PROJECT_NAME} main.cc core/controller.hpp core/game.hpp core/game.cc core/renderer.hpp actor/snake.hpp actor/snake.cc core/color.hpp core/color.cc core/vector2.hpp core/vector2.inl core/allocation.hpp core/allocation.inl core/game_state.hpp core/game_object.hpp core/block.cc core/block.hpp states/main_state.cc states/main_state.hpp sdl/renderer.cc sdl/renderer.hpp core/game_loop.hpp sdl/game_loop.cc sdl/game_loop.hpp sdl/controller.cc sdl/controller.hpp core/random_position.cc core/random_position.hpp core/food.cc core/food.hpp core/game_thread.cc core/game_thread.hpp core/font.cc core/font.hpp core/game_font.hpp sdl/typography.cc sdl/typography.hpp sdl/game_font_builder.cc sdl/game_font_builder.hpp sdl/font_maker.cc sdl/font_maker.hpp states/splash_screen_state.cc states/splash_screen_state.hpp core/button_chooser.cc core/button_chooser.hpp states/pause_state.cc states/pause_state.hpp states/end_game_state.cc states/end_game_state.hpp) 13 | target_link_libraries(${CMAKE_PROJECT_NAME} ${SDL2_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} ${SDL2_TTF_LIBRARIES} pthread) 14 | 15 | add_custom_command(TARGET ${CMAKE_PROJECT_NAME} PRE_BUILD 16 | COMMAND ${CMAKE_COMMAND} -E copy_directory 17 | ${CMAKE_SOURCE_DIR}/src/resources $/resources) 18 | -------------------------------------------------------------------------------- /src/core/game_loop.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_GAME_LOOP_HPP 3 | #define CPP_SNAKE_GAME_CORE_GAME_LOOP_HPP 4 | 5 | #include "game.hpp" 6 | #include "renderer.hpp" 7 | #include "controller.hpp" 8 | 9 | namespace Capstone 10 | { 11 | 12 | class Game; 13 | class Controller; 14 | 15 | /** 16 | * This defines the interface to the main loop on game 17 | * 18 | * The idea behind this class is to decouple the logic of the game loop 19 | * from the main structure. This way, we can define a common interface 20 | * to be accessed by any graphical library such as SDL, SFML or even a 21 | * terminal control library like ncurses. 22 | */ 23 | class GameLoop 24 | { 25 | public: 26 | 27 | /** 28 | * Destructor 29 | */ 30 | virtual ~GameLoop () = default; 31 | 32 | /** 33 | * Execute the main loop of the game 34 | * 35 | * It allows the Game class to use the GameLoop defined in SDL, or any 36 | * other library, using the Renderer and Controller specific for their 37 | * structure. 38 | * 39 | * Decoupling the Renderer and the Controller, we can create two controllers, 40 | * for example: The first using some fixed input identifiers, and last one 41 | * using the input defined by the user, effortlessly. 42 | * 43 | * @param game The Game instance used to handle the GameState 44 | * @param renderer Define the common interface used to render the object. 45 | * @param controller This defines the input handler used by the game 46 | * @return boolean If false it closes the game window 47 | */ 48 | virtual bool execute(Game &game, Renderer &renderer, Controller &controller) const = 0; 49 | }; 50 | } // namespace Capstone 51 | 52 | #endif // CPP_SNAKE_GAME_CORE_GAME_LOOP_HPP 53 | -------------------------------------------------------------------------------- /src/core/game_state.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_SNAKE_GAME_CORE_GAME_STATE_HPP 2 | #define CPP_SNAKE_GAME_CORE_GAME_STATE_HPP 3 | 4 | #include "game.hpp" 5 | #include "game_object.hpp" 6 | #include "controller.hpp" 7 | 8 | namespace Capstone 9 | { 10 | 11 | // It ensures the classes can be accessed before it be defined properly. 12 | class Game; 13 | class Renderer; 14 | enum class KeyPressed; 15 | 16 | /** 17 | * It's the Abstract State from the State Machine design pattern. 18 | * The inherited class get the common structure necessary to be 19 | * accessed by Game class (Context). 20 | * 21 | * It maintains a reference to the same instance of Game class and 22 | * uses it to replace the current state, to push the next state or 23 | * to go back to the previous one. 24 | */ 25 | class GameState: public GameObject 26 | { 27 | public: 28 | /** 29 | * It allows the inherited class to define its own input handler 30 | * 31 | * The inherited class can define its own input handler 32 | * behavior. 33 | * 34 | * @param KeyPressed& The command dispatched that can be used to handle the GameObjects 35 | */ 36 | virtual void handle_input(const KeyPressed &key) = 0; 37 | 38 | /** 39 | * This sets the Game class to be accessed by the inherited class 40 | * 41 | * It allows the inherited class to "iterate" over GameState's 42 | * stack or to close the window assigning false to is_running variable. 43 | * 44 | * @param Game game The reference to the Game class instance 45 | */ 46 | void set_game_handler(Game& game) 47 | { 48 | m_game = &game; 49 | } 50 | 51 | protected: 52 | /** 53 | * This keeps the reference to the Game class on inherited class. 54 | */ 55 | Game *m_game; 56 | }; 57 | } // namespace Capstone 58 | 59 | #endif // CPP_SNAKE_GAME_CORE_GAME_STATE_HPP 60 | -------------------------------------------------------------------------------- /src/core/game_object.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_SNAKE_GAME_CORE_GAME_OBJECT_HPP 2 | #define CPP_SNAKE_GAME_CORE_GAME_OBJECT_HPP 3 | 4 | #include "renderer.hpp" 5 | 6 | namespace Capstone 7 | { 8 | 9 | /** 10 | * This defines the inherited class as a game object. 11 | * 12 | * The inherited class will get the pattern to encapsulate their behaviors. 13 | */ 14 | class GameObject 15 | { 16 | public: 17 | /** 18 | * This updates the GameObject's logic. 19 | * 20 | * It's invoked at every game loop cycle, respecting the interval time 21 | * assigned as arguments by the Game class. 22 | * 23 | * This method holds the responsibility to update the movement, color, 24 | * or any other variable necessary to create the GameObject behavior. 25 | * 26 | * @param delta_time The elapsed time since the last game loop. 27 | */ 28 | virtual void update(std::size_t delta_time) = 0; 29 | 30 | /** 31 | * This Renders GameObject properly. 32 | * 33 | * It's invoked at every game loop cycle to show the game object on 34 | * the screen. 35 | * 36 | * @param renderer The Renderer instance used to clear the last 37 | * frame and show the object on the screen. 38 | */ 39 | virtual void render(Renderer &renderer) = 0; 40 | 41 | /** 42 | * This prepares the GameObject to be rendered. 43 | * 44 | * As GameState doesn't hold the Renderer's reference, the following method 45 | * helps the inherited class to get access to that, before their first 46 | * execution. 47 | * 48 | * This method is invoked just once, when the state get focus for the first 49 | * time. 50 | * 51 | * @param renderer The Renderer instance to configure the GameObject. 52 | */ 53 | virtual void prepare(Renderer &renderer) = 0; 54 | }; 55 | 56 | } // namespace Capstone 57 | 58 | #endif //CPP_SNAKE_GAME_CORE_GAME_OBJECT_HPP 59 | -------------------------------------------------------------------------------- /src/core/random_position.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_RANDOM_POSITION_HPP 3 | #define CPP_SNAKE_GAME_CORE_RANDOM_POSITION_HPP 4 | 5 | #include 6 | #include 7 | #include "vector2.hpp" 8 | 9 | namespace Capstone 10 | { 11 | 12 | /** 13 | * Creates a random coordinate integer that can be used to define 14 | * the food or obstacles on the game. 15 | * 16 | * The minimal number is always zero and the max can be assigned 17 | * to the Vector2's variable called `max`. 18 | */ 19 | class RandomPosition 20 | { 21 | public: 22 | /** 23 | * Parametrized constructor 24 | * 25 | * @param delimiter This defines the delimiter variables 26 | */ 27 | explicit RandomPosition(const iVector2& delimiter); 28 | 29 | /** 30 | * Get a random number 31 | * 32 | * It returns a number between zero and the number specified 33 | * on x's coordinate delimiter. 34 | * 35 | * @return int The random number 36 | */ 37 | int get_x(); 38 | 39 | /** 40 | * Get a random number 41 | * 42 | * It returns a number between zero and the number specified 43 | * on y's coordinate delimiter. 44 | * 45 | * @return int The random number 46 | */ 47 | int get_y(); 48 | 49 | /** 50 | * Get a random Vector2 51 | * 52 | * It returns a random coordinate between zero and the number 53 | * specified on delimiter variable. 54 | */ 55 | iVector2 get(); 56 | 57 | public: 58 | 59 | /** 60 | * This defines the max length of each coordinate 61 | */ 62 | iVector2 delimiter; 63 | protected: 64 | 65 | /** 66 | * The variables used to create the default C++ random behavior 67 | */ 68 | std::random_device m_dev; 69 | std::mt19937 m_engine; 70 | std::uniform_int_distribution m_random; 71 | }; 72 | 73 | } // namespace Capstone 74 | 75 | #endif // CPP_SNAKE_GAME_CORE_RANDOM_POSITION_HPP 76 | -------------------------------------------------------------------------------- /src/core/renderer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_SNAKE_GAME_CORE_RENDERER_HPP 2 | #define CPP_SNAKE_GAME_CORE_RENDERER_HPP 3 | 4 | #include "color.hpp" 5 | #include "vector2.hpp" 6 | #include "allocation.hpp" 7 | 8 | // Todo: remover 9 | #include 10 | #include 11 | 12 | namespace Capstone 13 | { 14 | 15 | class GameState; 16 | 17 | /** 18 | * This defines the interface to access the main function on 19 | * the graphical library abstraction. 20 | */ 21 | class Renderer 22 | { 23 | public: 24 | 25 | /** 26 | * Destructor 27 | */ 28 | virtual ~Renderer () = default; 29 | 30 | /** 31 | * This clear the screen using the color assigned as argument 32 | * 33 | * @param color The Color used to paint the screen 34 | */ 35 | virtual void clear(const Color &color) const = 0; 36 | 37 | /** 38 | * This fill the color to be used by the object on screen 39 | * 40 | * @param color The color used by the GameObject 41 | */ 42 | virtual void fill_color(const Color &color) const = 0; 43 | 44 | /** 45 | * This defines fill the object with the color and prepares 46 | * it to be renderer by display method. 47 | * 48 | * @param allocation The place where the GameObject should be rendered 49 | */ 50 | virtual void fill(const iAllocation &allocation) const = 0; 51 | 52 | /** 53 | * This render the object properly 54 | * 55 | * It's the final step to ensures the GameObject will be displayed 56 | * on the screen. 57 | */ 58 | virtual void display() const = 0; 59 | 60 | /** 61 | * This returns the grid height and the width values 62 | * 63 | * @return iVector2 The height and width value in x and y coordinates 64 | */ 65 | virtual const iVector2 get_grid() const = 0; 66 | 67 | /** 68 | * This returns the screen height and the width values 69 | * 70 | * @return iVector2 The height and width value in x and y coordinates 71 | */ 72 | virtual const iVector2 get_screen() const = 0; 73 | }; 74 | 75 | } // namespace Capstone 76 | 77 | #endif // CPP_SNAKE_GAME_CORE_RENDERER_HPP -------------------------------------------------------------------------------- /src/core/game.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "game.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | // Default constructor 8 | Game::Game (std::unique_ptr renderer, std::unique_ptr fonts): 9 | m_renderer{std::move(renderer)}, 10 | m_fonts{std::move(fonts)}, 11 | is_running{true} 12 | { 13 | 14 | } 15 | 16 | // Destructor 17 | Game::~Game () 18 | { 19 | // Deletes all 20 | while (!m_states.empty ()) 21 | { 22 | pop_state (); 23 | } 24 | } 25 | 26 | // This creates the game loop 27 | bool Game::render (GameLoop &game_loop, Controller &controller) 28 | { 29 | // This allows each GameState to choose the next one. Moreover, it defines 30 | // the time between the frames and allow the GameState in focus to handle 31 | // their own inputs 32 | return game_loop.execute (*this, *m_renderer, controller); 33 | } 34 | 35 | void Game::push_state (std::unique_ptr state) 36 | { 37 | // It ensures the GameState will get access to the current game instance 38 | state->set_game_handler (*this); 39 | 40 | // This prepares the GameState in focus allowing it to get access 41 | // to the Renderer class 42 | state->prepare (*m_renderer); 43 | 44 | // It moves the smart pointer to the stack, keeping the order of execution 45 | m_states.push (std::move(state)); 46 | } 47 | 48 | void Game::pop_state () 49 | { 50 | // It deletes the GameState in focus 51 | m_states.pop (); 52 | } 53 | 54 | void Game::replace_state (std::unique_ptr state) 55 | { 56 | // It deletes the GameState in focus 57 | if (!m_states.empty ()) 58 | { 59 | pop_state (); 60 | } 61 | 62 | // It replace the GameState in focus by the assigned as argument 63 | push_state (std::move(state)); 64 | } 65 | 66 | // This returns the GameState in focus 67 | GameState *Game::peek_state () 68 | { 69 | // If the stack is empty, throws an error 70 | if (m_states.empty ()) 71 | { 72 | throw std::range_error("Attempt to access invalid Game State on Capstone::Game class. Capstone::Game::pop_state removed the last one"); 73 | } 74 | 75 | // It returns the current state 76 | return m_states.top ().get(); 77 | } 78 | 79 | const GameFont* Game::get_font () const 80 | { 81 | return m_fonts.get(); 82 | } 83 | 84 | } // namespace Capstone -------------------------------------------------------------------------------- /src/sdl/typography.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "typography.hpp" 5 | 6 | namespace SDL 7 | { 8 | 9 | Typography::Typography (const std::string& path, unsigned int size): 10 | m_texture{nullptr}, 11 | m_renderer{nullptr}, 12 | m_font{nullptr} 13 | { 14 | m_font = TTF_OpenFont (path.c_str (), size); 15 | 16 | if (m_font == nullptr) 17 | { 18 | printf("The font %s with size %d can not be loaded. SDL_ttf Error: %s", path.c_str (), size, TTF_GetError()); 19 | SDL_Quit(); 20 | } 21 | } 22 | 23 | // This destroys the SDL pointer used on class. 24 | Typography::~Typography () 25 | { 26 | TTF_CloseFont(m_font); 27 | SDL_DestroyTexture (m_texture); 28 | } 29 | 30 | // This converts the Capstone::Renderer to SDL::Renderer 31 | void Typography::prepare (Capstone::Renderer &renderer) 32 | { 33 | m_renderer = dynamic_cast(&renderer); 34 | } 35 | 36 | // This creates the texture using the Font's text 37 | void Typography::create_texture (Capstone::Font& font) 38 | { 39 | m_text = font.get_text (); 40 | m_color = font.color; 41 | 42 | if (m_texture != nullptr) 43 | { 44 | SDL_DestroyTexture (m_texture); 45 | } 46 | 47 | SDL_Color sdl_color { m_color.r, m_color.g, m_color.b, m_color.a }; 48 | SDL_Surface* surface = TTF_RenderText_Solid(m_font, m_text.c_str(), sdl_color); 49 | 50 | if(surface == nullptr) 51 | { 52 | printf ("Unable to render text surface! SDL_ttf Error: %s", TTF_GetError()); 53 | SDL_Quit(); 54 | } 55 | 56 | // create a image from surface 57 | m_texture = SDL_CreateTextureFromSurface (m_renderer->renderer, surface); 58 | 59 | font.size.x = surface->w; 60 | font.size.y = surface->h; 61 | 62 | SDL_FreeSurface (surface); 63 | } 64 | 65 | // It's not use the grid system to render the font, so the conventional 66 | // Allocation class is not used. 67 | void Typography::get (const Capstone::Font& font) const 68 | { 69 | SDL_Rect destination = { 70 | font.offset.x, 71 | font.offset.y, 72 | font.size.x, 73 | font.size.y, 74 | }; 75 | 76 | SDL_RenderCopy(m_renderer->renderer, m_texture, nullptr, &destination); 77 | } 78 | 79 | // This forces the texture redraw when the color or the text has changed 80 | void Typography::update (Capstone::Font& font) 81 | { 82 | if (font.color != m_color || font.get_text () != m_text) 83 | { 84 | create_texture(font); 85 | } 86 | } 87 | 88 | } // namespace SDL 89 | -------------------------------------------------------------------------------- /src/sdl/game_font_builder.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_GAME_FONT_BUILDER_HPP 3 | #define CPP_SNAKE_GAME_SDL_GAME_FONT_BUILDER_HPP 4 | 5 | #include 6 | #include "../core/game_font.hpp" 7 | 8 | namespace SDL 9 | { 10 | 11 | /** 12 | * This struct defines the behavior of each font used in SDL's version 13 | * of the game. 14 | */ 15 | struct FontDescription 16 | { 17 | std::string path; 18 | Capstone::Color color; 19 | unsigned int size; 20 | }; 21 | 22 | /** 23 | * This class defines a Builder-like design pattern. It allows the 24 | * Game class to create a font without to "know" the logic used in 25 | * the SDL version to do it. 26 | */ 27 | class GameFontBuilder: public Capstone::GameFont { 28 | public: 29 | /** 30 | * This method allows the current class map each font used on game. 31 | * 32 | * The FontDescription defines the font's behavior and assign it 33 | * to the font map. Using the map, the current class can create a 34 | * Font class with some fixed attributes, such as color, size and type. 35 | * 36 | * @param theme The theme enum used to create the font pattern under the hoods 37 | * @param size The size enum used to create the font pattern under the hoods 38 | * @param font_description The class that describes the font behavior. 39 | */ 40 | void set_font(const Capstone::FontTheme& theme, const Capstone::FontSize& size, const FontDescription font_description); 41 | 42 | /** 43 | * This creates a configured Font class using the SDL's logic 44 | * defined previously 45 | * 46 | * It uses the definition assigned by `set_font` method to return 47 | * a well configures Font's smart pointer on GameState through 48 | * the Game class. 49 | * 50 | * @param theme The theme enum used to create the font pattern under the hoods 51 | * @param size The size enum used to create the font pattern under the hoods 52 | * @return std::unique_ptr The smart pointer to the configured Font class 53 | */ 54 | std::unique_ptr create (Capstone::FontTheme&& theme, Capstone::FontSize&& size) const override; 55 | 56 | private: 57 | 58 | /** 59 | * Hold the info of the fonts configured by set_font method. 60 | */ 61 | std::map, FontDescription> m_font_map; 62 | }; 63 | } // namespace SDL 64 | 65 | #endif // CPP_SNAKE_GAME_SDL_GAME_FONT_BUILDER_HPP 66 | -------------------------------------------------------------------------------- /src/states/pause_state.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_STATES_PAUSE_STATE_HPP 3 | #define CPP_SNAKE_GAME_STATES_PAUSE_STATE_HPP 4 | 5 | #include "../core/game_state.hpp" 6 | #include "../core/button_chooser.hpp" 7 | 8 | namespace Capstone 9 | { 10 | 11 | /** 12 | * This GameState is used to allow the user to select between 13 | * resume the game or quit the window. 14 | */ 15 | class PauseState: public GameState 16 | { 17 | public: 18 | /** 19 | * This updates the GameObject's logic. 20 | * 21 | * It's invoked at every game loop cycle, respecting the interval time 22 | * assigned as arguments by the Game class. 23 | * 24 | * This method holds the responsibility to update the color of the button. 25 | * 26 | * @param delta_time The elapsed time since the last game loop. 27 | */ 28 | void update (std::size_t delta_time) override; 29 | 30 | /** 31 | * This Renders the GameObjects 32 | * 33 | * It's invoked at every game loop cycle to show the game object on 34 | * the screen. The font and buttons are rendered inside this method. 35 | * 36 | * @param renderer The Renderer instance used to clear the last 37 | * frame and show the object on the screen. 38 | */ 39 | void render (Renderer& renderer) override; 40 | 41 | 42 | /** 43 | * This prepares the GameObject to be rendered. 44 | * 45 | * As the GameState shouldn't hold the Renderer's reference, the following 46 | * method helps the current class to get access to that. The font and buttons 47 | * are prepared to be rendered properly. 48 | * 49 | * This method is invoked just once, when the state get focus for the first 50 | * time. 51 | * 52 | * @param renderer The Renderer instance to configure the GameObject. 53 | */ 54 | void prepare (Renderer& renderer) override; 55 | 56 | 57 | /** 58 | * It allows the current class to define its own input handler 59 | * 60 | * It ensures the ButtonChooser can use the user input to choose the 61 | * correct button in focus. 62 | * 63 | * @param key The command dispatched that can be used to handle the GameObjects 64 | */ 65 | void handle_input (const KeyPressed& key) override; 66 | 67 | protected: 68 | 69 | /** 70 | * The font used to display the "Pause Game" text 71 | */ 72 | std::unique_ptr m_title; 73 | 74 | /** 75 | * The button list used to define the options: resume and quit 76 | */ 77 | ButtonChooser m_button_chooser; 78 | }; 79 | } // namespace Capstone 80 | 81 | #endif // CPP_SNAKE_GAME_STATES_PAUSE_STATE_HPP 82 | -------------------------------------------------------------------------------- /src/states/main_state.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "main_state.hpp" 4 | #include "../sdl/typography.hpp" 5 | #include "pause_state.hpp" 6 | #include "end_game_state.hpp" 7 | 8 | namespace Capstone 9 | { 10 | 11 | // This updates the GameObject behavior 12 | void MainState::update (std::size_t delta_time) 13 | { 14 | if(!m_snake->alive || m_snake->body.size () > 64) 15 | { 16 | std::this_thread::sleep_for (std::chrono::milliseconds(800)); 17 | auto score = m_snake->body.size (); 18 | auto is_winner = m_snake->alive; 19 | m_snake->reset (); 20 | m_game->push_state (std::make_unique(score, is_winner)); 21 | } 22 | 23 | m_snake->update (delta_time); 24 | 25 | if(m_snake->get_status () == SnakeStatus::kEating) 26 | { 27 | m_food.replace(); 28 | m_snake->growing (); 29 | } 30 | 31 | if (m_score_font->get_text () != ("SCORE " + std::to_string (m_snake->body.size ()))) 32 | { 33 | m_score_font->set_text ("SCORE " + std::to_string (m_snake->body.size ())); 34 | } 35 | 36 | // The font update should be invoked after color and text changes 37 | m_score_font->update (delta_time); 38 | } 39 | 40 | // This renders the GameObjects 41 | void MainState::render (Renderer& renderer) 42 | { 43 | renderer.clear (Color(0x1E1E1EFF)); 44 | 45 | m_score_font->render (renderer); 46 | m_food.render (renderer); 47 | m_snake->render(renderer); 48 | 49 | renderer.display (); 50 | } 51 | 52 | // This prepared the game to be rendered 53 | void MainState::prepare (Renderer& renderer) 54 | { 55 | // This defines the actor 56 | m_snake = std::make_shared (); 57 | m_snake->prepare (renderer); 58 | 59 | // This defines the font 60 | m_score_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 61 | m_score_font->set_text ("SCORE " + std::to_string (m_snake->body.size ())); 62 | m_score_font->prepare (renderer); 63 | m_score_font->offset.x = renderer.get_screen ().x / 2 - m_score_font->size.x / 2; 64 | m_score_font->offset.y = renderer.get_screen ().y / 2 - m_score_font->size.y; 65 | 66 | // This defines the food 67 | m_food.set_target (m_snake); 68 | m_food.prepare (renderer); 69 | } 70 | 71 | // This creates the input handler of the GameState 72 | void MainState::handle_input (const KeyPressed &key) 73 | { 74 | // This allows the game to reinitialize when the snake has been collided 75 | // and the user dispatch `Enter` command 76 | if(key == KeyPressed::kEnter) 77 | { 78 | m_game->push_state (std::make_unique ()); 79 | } 80 | 81 | m_snake->handle_input (key); 82 | } 83 | } // namespace Capstone 84 | -------------------------------------------------------------------------------- /src/sdl/renderer.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "renderer.hpp" 5 | 6 | namespace SDL 7 | { 8 | 9 | Renderer::Renderer (const Capstone::iVector2& screen, const Capstone::iVector2& grid): 10 | m_grid{grid}, 11 | m_screen{screen} 12 | { 13 | if (SDL_Init(SDL_INIT_VIDEO) < 0) 14 | { 15 | std::cerr << "SDL could not initialize! SDL Error:" << SDL_GetError () << std::endl; 16 | SDL_Quit(); 17 | } 18 | 19 | // Create window 20 | window = SDL_CreateWindow ("Capstone Game", SDL_WINDOWPOS_CENTERED, 21 | SDL_WINDOWPOS_CENTERED, screen.x, 22 | screen.y, SDL_WINDOW_SHOWN); 23 | if (window == nullptr) 24 | { 25 | std::cerr << "Window could not be created." << std::endl; 26 | std::cerr << "SDL_Error: " << SDL_GetError () << std::endl; 27 | SDL_Quit(); 28 | } 29 | 30 | renderer = SDL_CreateRenderer (window, -1, SDL_RENDERER_ACCELERATED); 31 | if (renderer == nullptr) 32 | { 33 | std::cerr << "Renderer could not be created." << std::endl; 34 | std::cerr << "SDL_Error: " << SDL_GetError () << std::endl; 35 | SDL_Quit(); 36 | } 37 | 38 | 39 | if(TTF_Init() == -1) 40 | { 41 | printf( "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError() ); 42 | SDL_Quit(); 43 | } 44 | } 45 | 46 | Renderer::~Renderer () 47 | { 48 | // This destroys the SDL pointer used by the class. 49 | SDL_DestroyWindow (window); 50 | SDL_DestroyRenderer (renderer); 51 | SDL_Quit(); 52 | } 53 | 54 | void Renderer::clear (const Capstone::Color& color) const 55 | { 56 | // This clears the screen and fills with the assigned color 57 | fill_color (color); 58 | SDL_RenderClear(renderer); 59 | } 60 | 61 | void Renderer::fill_color (const Capstone::Color& color) const 62 | { 63 | SDL_SetRenderDrawColor (renderer, color.r, color.g, color.b, color.a); 64 | } 65 | 66 | void Renderer::fill (const Capstone::iAllocation& allocation) const 67 | { 68 | SDL_Rect rect { 69 | allocation.offset.x * allocation.size.x, 70 | allocation.offset.y * allocation.size.y, 71 | allocation.size.x, 72 | allocation.size.y, 73 | }; 74 | 75 | // This prepares the GameObject to be rendered 76 | SDL_RenderFillRect(renderer, &rect); 77 | } 78 | 79 | void Renderer::display () const 80 | { 81 | // This draws the GameObject on the screen 82 | SDL_RenderPresent (renderer); 83 | } 84 | 85 | const Capstone::iVector2 Renderer::get_grid () const 86 | { 87 | return m_grid; 88 | } 89 | 90 | const Capstone::iVector2 Renderer::get_screen () const 91 | { 92 | return m_screen; 93 | } 94 | 95 | } // namespace Capstone 96 | -------------------------------------------------------------------------------- /src/states/main_state.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_STATE_PLAY_STATE_HPP 3 | #define CPP_SNAKE_GAME_STATE_PLAY_STATE_HPP 4 | 5 | #include 6 | 7 | #include "../core/game_state.hpp" 8 | #include "../actor/snake.hpp" 9 | #include "../core/food.hpp" 10 | #include "../core/font.hpp" 11 | 12 | namespace Capstone 13 | { 14 | 15 | /** 16 | * This defines the main state on game 17 | * 18 | * It's the game properly 19 | */ 20 | class MainState: public GameState 21 | { 22 | public: 23 | /** 24 | * This updates the GameObject's logic. 25 | * 26 | * It's invoked at every game loop cycle, respecting the interval time 27 | * assigned as arguments by the Game class. 28 | * 29 | * This method holds the responsibility to update the movement, color, 30 | * or any other variable necessary to create the GameObject behavior. 31 | * 32 | * @param delta_time The elapsed time since the last game loop. 33 | */ 34 | void update (std::size_t delta_time) override; 35 | 36 | /** 37 | * This Renders the GameObjects 38 | * 39 | * It's invoked at every game loop cycle to show the game object on 40 | * the screen. The actor, font and food are renderer inside this method. 41 | * 42 | * @param renderer The Renderer instance used to clear the last 43 | * frame and show the object on the screen. 44 | */ 45 | void render (Renderer& renderer) override; 46 | 47 | /** 48 | * This prepares the GameObject to be rendered. 49 | * 50 | * As the GameState shouldn't hold the Renderer's reference, the following 51 | * method helps the current class to get access to that. The actors, food 52 | * and fonts can be prepared to be render properly. 53 | * 54 | * This method is invoked just once, when the state get focus for the first 55 | * time. 56 | * 57 | * @param renderer The Renderer instance to configure the GameObject. 58 | */ 59 | void prepare (Renderer& renderer) override; 60 | 61 | /** 62 | * It allows the current class to define its own input handler 63 | * 64 | * It ensures the Snake can use the user input to choose the correct direction 65 | * 66 | * @param key The command dispatched that can be used to handle the GameObjects 67 | */ 68 | void handle_input (const KeyPressed &key) override; 69 | 70 | protected: 71 | /** 72 | * This defines the food GameObject 73 | */ 74 | Food m_food; 75 | 76 | /** 77 | * This defines the main actor of the game 78 | */ 79 | std::shared_ptr m_snake; 80 | 81 | /** 82 | * This defines the font used to display the score on screen 83 | */ 84 | std::unique_ptr m_score_font; 85 | }; 86 | 87 | } // namespace Capstone 88 | 89 | #endif //CPP_SNAKE_GAME_STATE_PLAY_STATE_HPP 90 | -------------------------------------------------------------------------------- /src/states/pause_state.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "pause_state.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | void PauseState::update (std::size_t delta_time) 8 | { 9 | // This update the font color in focus. 10 | m_button_chooser.update (delta_time); 11 | } 12 | 13 | void PauseState::render (Renderer& renderer) 14 | { 15 | renderer.clear (Color(0x1E1E1EFF)); 16 | 17 | // This renders the fonts 18 | m_title->render (renderer); 19 | m_button_chooser.render (renderer); 20 | 21 | renderer.display (); 22 | } 23 | 24 | void PauseState::prepare (Renderer& renderer) 25 | { 26 | // This defines the screen center 27 | iVector2 centered = { 28 | renderer.get_screen ().x / 2, 29 | renderer.get_screen ().y / 2 30 | }; 31 | 32 | // This defines the title's font instance 33 | m_title = m_game->get_font ()->create (FontTheme::kDark, FontSize::kMedium); 34 | m_title->set_text("PAUSED GAME"); 35 | m_title->prepare (renderer); 36 | 37 | // This defines the resume's font instance 38 | auto resume_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 39 | resume_font->set_text ("RESUME"); 40 | resume_font->prepare (renderer); 41 | 42 | // This defines the quit's font instance 43 | auto quit_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 44 | quit_font->set_text ("QUIT"); 45 | quit_font->prepare (renderer); 46 | 47 | 48 | // This align the x coordinate of the title's font on center of screen 49 | m_title->offset = centered; 50 | m_title->offset.x -= m_title->size.x / 2; 51 | 52 | // This align the x coordinate of the quit's font on center of screen 53 | quit_font->offset = centered; 54 | quit_font->offset.x -= quit_font->size.x / 2; 55 | 56 | // This align the x coordinate of the resume's font on center of screen 57 | resume_font->offset = centered; 58 | resume_font->offset.x -= resume_font->size.x / 2; 59 | 60 | // This defines the `y` coordinate of each font 61 | resume_font->offset.y -= resume_font->size.y / 2; 62 | m_title->offset.y = resume_font->offset.y - m_title->size.y - 15; 63 | quit_font->offset.y = resume_font->offset.y + quit_font->size.y + 3; 64 | 65 | 66 | // This defines the resume's command button 67 | m_button_chooser.add_button (std::move(resume_font), [this]() -> void { 68 | m_game->pop_state (); 69 | }); 70 | 71 | // This defines the quit's command button 72 | m_button_chooser.add_button (std::move(quit_font), [this]() -> void { 73 | m_game->is_running = false; 74 | }); 75 | 76 | // It's important to prepare the buttons after add them all. 77 | m_button_chooser.prepare (renderer); 78 | } 79 | 80 | void PauseState::handle_input (const KeyPressed& key) 81 | { 82 | m_button_chooser.handle_input (key); 83 | } 84 | } // namespace Capstone 85 | -------------------------------------------------------------------------------- /src/sdl/renderer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_RENDERER_HPP 3 | #define CPP_SNAKE_GAME_SDL_RENDERER_HPP 4 | 5 | #include 6 | 7 | #include "../core/renderer.hpp" 8 | 9 | namespace SDL 10 | { 11 | 12 | /** 13 | * This creates the Renderer specific to SDL library 14 | */ 15 | class Renderer: public Capstone::Renderer { 16 | public: 17 | 18 | /** 19 | * Parametrized constructor 20 | * 21 | * @param screen The height and width of the game screen 22 | * @param grid The total number of vertical and horizontal grids 23 | */ 24 | Renderer(const Capstone::iVector2& screen, const Capstone::iVector2& grid); 25 | 26 | /** 27 | * Destructor 28 | * 29 | * This destroys the SDL_Renderer and SDL_Window pointers 30 | */ 31 | ~Renderer () override; 32 | 33 | /** 34 | * This clear the screen using the color assigned as argument 35 | * 36 | * @param color The Color used to paint the screen 37 | */ 38 | void clear(const Capstone::Color &color) const override; 39 | 40 | /** 41 | * This fill the color to be used by the object on screen 42 | * 43 | * @param color The color used by the GameObject 44 | */ 45 | void fill_color(const Capstone::Color &color) const override; 46 | 47 | /** 48 | * This defines fill the object with the color and prepares 49 | * it to be renderer by display method. 50 | * 51 | * @param allocation The place where the GameObject should be rendered 52 | */ 53 | void fill(const Capstone::iAllocation &allocation) const override; 54 | 55 | /** 56 | * This render the object properly 57 | * 58 | * It's the final step to ensures the GameObject will be displayed 59 | * on the screen. 60 | */ 61 | void display() const override; 62 | 63 | /** 64 | * This returns the grid height and the width values 65 | * 66 | * @return iVector2 The height and width value in x and y coordinates 67 | */ 68 | const Capstone::iVector2 get_grid () const override; 69 | 70 | /** 71 | * This returns the screen height and the width values 72 | * 73 | * @return iVector2 The height and width value in x and y coordinates 74 | */ 75 | const Capstone::iVector2 get_screen () const override; 76 | public: 77 | /** 78 | * This holds the Renderer and Window from SDL. 79 | * They both are the main class used to display the GameObject on 80 | * screen. 81 | */ 82 | SDL_Renderer *renderer; 83 | SDL_Window *window; 84 | 85 | protected: 86 | /** 87 | * This defines the grid measures 88 | */ 89 | const Capstone::iVector2 m_screen; 90 | 91 | /** 92 | * This defines the number of grid used along the screen 93 | */ 94 | const Capstone::iVector2 m_grid; 95 | }; 96 | } // namespace SDL 97 | 98 | #endif //CPP_SNAKE_GAME_SDL_RENDERER_HPP 99 | -------------------------------------------------------------------------------- /src/core/allocation.inl: -------------------------------------------------------------------------------- 1 | 2 | #include "allocation.hpp" 3 | 4 | // This defines the default constructor with size and offset as zero 5 | template 6 | Capstone::Allocation::Allocation (): 7 | size({ 0, 0 }), 8 | offset({ 0, 0 }) 9 | { 10 | 11 | } 12 | 13 | // It allows the client to define the offset and size of an object 14 | template 15 | Capstone::Allocation::Allocation (Capstone::Vector2 size, Capstone::Vector2 offset): 16 | size(size), 17 | offset(offset) 18 | { 19 | 20 | } 21 | 22 | // This copy and convert a type of Allocation to another one 23 | template 24 | template 25 | Capstone::Allocation::Allocation (const Capstone::Allocation& vector): 26 | size(static_cast(vector.size.x), static_cast(vector.size.y)), 27 | offset(static_cast(vector.offset.x), static_cast(vector.offset.y)) 28 | { 29 | 30 | } 31 | 32 | template 33 | template 34 | const Allocation Capstone::Allocation::get_allocation_as () const 35 | { 36 | // This returns a "copy" of the current Allocation with the type assigned 37 | // to template type deduction `U` 38 | return Allocation(*this); 39 | } 40 | 41 | // This creates the offset of each box edge and returns the BoundingBox as well 42 | template 43 | const Capstone::BoundingBox Capstone::Allocation::get_bounding_box() const 44 | { 45 | auto cast = get_allocation_as (); 46 | 47 | return { 48 | cast.offset.y * cast.size.y, // top 49 | cast.offset.y * cast.size.y + cast.size.y, // bottom 50 | cast.offset.x * cast.size.x, // left 51 | cast.offset.x * cast.size.x + cast.size.x // right 52 | }; 53 | } 54 | 55 | // This checks if the current object and another one are colliding 56 | template 57 | template 58 | bool Capstone::Allocation::check_collision (const Capstone::Allocation& other) const 59 | { 60 | auto current = get_bounding_box (); 61 | auto target = other.get_bounding_box (); 62 | 63 | return (current.right > target.left) && (target.right > current.left) && // It checks X coordinate 64 | (current.bottom > target.top) && (target.bottom > current.top); // It checks Y coordinate 65 | } 66 | 67 | // This compares the size and offset of two Allocation of the same type 68 | template 69 | bool operator ==(const Capstone::Allocation& left, const Capstone::Allocation& right) 70 | { 71 | return (left.size == right.size) && (left.offset == right.offset); 72 | } 73 | 74 | // This checks if the size and offset of two Allocations are different when they both have the same type 75 | template 76 | bool operator !=(const Capstone::Allocation& left, const Capstone::Allocation& right) 77 | { 78 | return !(left == right); 79 | } 80 | -------------------------------------------------------------------------------- /src/core/button_chooser.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "button_chooser.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | // This creates a Button using a font and a function 8 | Button::Button (std::unique_ptr font, const std::function&& command): 9 | font{std::move(font)}, 10 | m_command{command} 11 | { 12 | 13 | } 14 | 15 | // This executes the defined Button command 16 | void Button::execute () 17 | { 18 | m_command(); 19 | } 20 | 21 | 22 | // This defines the first button on vector as the active one 23 | ButtonChooser::ButtonChooser (): 24 | m_cursor{0}, 25 | active{Color(0xFFCC00FF)} 26 | { 27 | 28 | } 29 | 30 | void ButtonChooser::update (std::size_t delta_time) 31 | { 32 | // This ensures the buttons will update their colors 33 | for (auto &button: m_buttons) 34 | { 35 | button.font->update (delta_time); 36 | } 37 | } 38 | 39 | // This renders the buttons 40 | void ButtonChooser::render (Renderer& renderer) 41 | { 42 | for (auto &button: m_buttons) 43 | { 44 | button.font->render (renderer); 45 | } 46 | } 47 | 48 | void ButtonChooser::prepare (Renderer& renderer) 49 | { 50 | // It stores the original font color. It's useful when the cursor 51 | // moves, selecting a new Button as active and the previous one need 52 | // to back to the original color. 53 | for (auto &button: m_buttons) 54 | { 55 | button.color = button.font->color; 56 | } 57 | 58 | // This defines the first button as the active one 59 | m_buttons[m_cursor].font->color = active; 60 | } 61 | 62 | 63 | void ButtonChooser::handle_input (const KeyPressed &key) 64 | { 65 | if (key == KeyPressed::kUp || key == KeyPressed::kDown) 66 | { 67 | // This back the original color to the previous button in focus 68 | m_buttons[m_cursor].font->color = m_buttons[m_cursor].color; 69 | } 70 | 71 | switch (key) 72 | { 73 | // This defines a vertical carousel effect to select the Button in focus 74 | case KeyPressed::kUp: 75 | if (m_cursor == 0) m_cursor = static_cast(m_buttons.size()); 76 | --m_cursor; 77 | break; 78 | 79 | // This defines a vertical carousel effect to select the Button in focus 80 | case KeyPressed::kDown: 81 | ++m_cursor; 82 | break; 83 | 84 | // This execute the Button in focus 85 | case KeyPressed::kEnter: 86 | m_buttons[m_cursor].execute (); 87 | return; 88 | } 89 | 90 | // This defines the cursor of the button in focus 91 | m_cursor = m_cursor % static_cast(m_buttons.size()); 92 | m_buttons[m_cursor].font->color = active; 93 | } 94 | 95 | 96 | void ButtonChooser::add_button (std::unique_ptr font, const std::function&& command) 97 | { 98 | // This creates a Button and push it to the vector 99 | m_buttons.emplace_back (Button(std::move(font), std::move(command))); 100 | } 101 | } // namespace Capstone 102 | -------------------------------------------------------------------------------- /src/core/vector2.inl: -------------------------------------------------------------------------------- 1 | template 2 | Capstone::Vector2::Vector2 (): 3 | x(0), 4 | y(0) 5 | { 6 | 7 | } 8 | 9 | template 10 | Capstone::Vector2::Vector2 (T equal): 11 | x(equal), 12 | y(equal) 13 | { 14 | 15 | } 16 | 17 | template 18 | Capstone::Vector2::Vector2 (T X, T Y): 19 | x(X), 20 | y(Y) 21 | { 22 | 23 | } 24 | 25 | template 26 | template 27 | Capstone::Vector2::Vector2 (const Capstone::Vector2& vector): 28 | x(static_cast(vector.x)), 29 | y(static_cast(vector.y)) 30 | { 31 | 32 | } 33 | 34 | template 35 | Capstone::Vector2 operator -(const Capstone::Vector2 &right) { 36 | return Capstone::Vector2(-right.x, -right.y); 37 | } 38 | 39 | template 40 | Capstone::Vector2& operator +=(Capstone::Vector2 &left, const Capstone::Vector2 &right) { 41 | left.x += right.x; 42 | left.y += right.y; 43 | 44 | return left; 45 | } 46 | 47 | template 48 | Capstone::Vector2& operator -=(Capstone::Vector2 &left, const Capstone::Vector2 &right) { 49 | left.x -= right.x; 50 | left.y -= right.y; 51 | 52 | return left; 53 | } 54 | 55 | template 56 | Capstone::Vector2 operator +(const Capstone::Vector2 &left, const Capstone::Vector2 &right) { 57 | return Capstone::Vector2(left.x + right.x, left.y + right.y); 58 | } 59 | 60 | template 61 | Capstone::Vector2 operator -(const Capstone::Vector2 &left, const Capstone::Vector2 &right) { 62 | return Capstone::Vector2(left.x - right.x, left.x - right.x); 63 | } 64 | 65 | template 66 | Capstone::Vector2 operator *(const Capstone::Vector2 &left, T right) { 67 | return Capstone::Vector2(left.x * right, left.y * right); 68 | } 69 | 70 | template 71 | Capstone::Vector2 operator *(T left, const Capstone::Vector2 &right) { 72 | return Capstone::Vector2(right.x * left, right.y * left); 73 | } 74 | 75 | template 76 | Capstone::Vector2& operator *=(Capstone::Vector2 &left, T right) { 77 | left.x *= right; 78 | left.y *= right; 79 | 80 | return left; 81 | } 82 | 83 | template 84 | Capstone::Vector2 operator /(const Capstone::Vector2 &left, T right) { 85 | return Vector2(left.x / right, left.y / right); 86 | } 87 | 88 | template 89 | Capstone::Vector2& operator /=(Capstone::Vector2 &left, T right) { 90 | left.x /= right; 91 | left.y /= right; 92 | 93 | return left; 94 | } 95 | 96 | template 97 | bool operator ==(const Capstone::Vector2& left, const Capstone::Vector2& right) 98 | { 99 | return (left.x == right.x) && (left.y == right.y); 100 | } 101 | 102 | template 103 | bool operator !=(const Capstone::Vector2& left, const Capstone::Vector2& right) 104 | { 105 | return !(left == right); 106 | } -------------------------------------------------------------------------------- /src/states/splash_screen_state.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_STATES_SPLASH_SCREEN_STATE_HPP 3 | #define CPP_SNAKE_GAME_STATES_SPLASH_SCREEN_STATE_HPP 4 | 5 | #include "../core/game_state.hpp" 6 | 7 | namespace Capstone 8 | { 9 | 10 | /** 11 | * This is a simple GameState to display some information about the aim 12 | * of this project. 13 | */ 14 | class SplashScreenState: public GameState 15 | { 16 | public: 17 | /** 18 | * This displays the Capstone information along the milliseconds defined 19 | * as argument and jump to the next GameState when the this time is over. * 20 | * 21 | * @param timeout This define the millisecond used until change the focus 22 | * to the next GameState 23 | */ 24 | explicit SplashScreenState(std::size_t timeout = 4000); 25 | /** 26 | * Destructor 27 | */ 28 | virtual ~SplashScreenState () = default; 29 | 30 | /** 31 | * This updates the GameObject's logic. 32 | * 33 | * It's invoked at every game loop cycle, respecting the interval time 34 | * assigned as arguments by the Game class. 35 | * 36 | * It checks if the timeout is over and change the font's color, creating 37 | * a simple fade out effect to them. 38 | * 39 | * @param delta_time The elapsed time since the last game loop. 40 | */ 41 | void update (std::size_t delta_time) override; 42 | 43 | /** 44 | * This Renders the GameObjects 45 | * 46 | * It's invoked at every game loop cycle to show the game object on 47 | * the screen. The fonts are rendered properly, on this method. 48 | * 49 | * @param renderer The Renderer instance used to clear the last 50 | * frame and show the object on the screen. 51 | */ 52 | void render (Renderer& renderer) override; 53 | 54 | 55 | /** 56 | * This prepares the GameObject to be rendered. 57 | * 58 | * As the GameState shouldn't hold the Renderer's reference, the following 59 | * method helps the current class to get access to that. The fonts are 60 | * aligned on the screen using this method. 61 | * 62 | * @param renderer The Renderer instance to configure the GameObject. 63 | */ 64 | void prepare (Renderer& renderer) override; 65 | 66 | 67 | /** 68 | * It allows the current class to define its own input handler 69 | * 70 | * Basically, if the user press any allowed key the current class will 71 | * skip the timeout defined and change the focus to the next GameState, 72 | * immediately. 73 | * 74 | * @param key The command dispatched that can be used to handle the GameObjects 75 | */ 76 | void handle_input (const KeyPressed& key) override; 77 | 78 | protected: 79 | /** 80 | * This variables defines the current GameState's lifetime. 81 | * When the m_elapsed_time is greater than m_timeout, the current GameState 82 | * stop to show the font's text and change the focus to the next GameState. 83 | */ 84 | std::size_t m_timeout; 85 | std::size_t m_elapsed_time; 86 | 87 | /** 88 | * This defines the fonts used by the current state 89 | */ 90 | std::unique_ptr m_udacity_font; 91 | std::unique_ptr m_course_name_font; 92 | std::unique_ptr m_capstone_font; 93 | }; 94 | 95 | } // namespace Capstone 96 | 97 | 98 | #endif // CPP_SNAKE_GAME_STATES_SPLASH_SCREEN_STATE_HPP 99 | -------------------------------------------------------------------------------- /src/states/splash_screen_state.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "splash_screen_state.hpp" 3 | #include "main_state.hpp" 4 | 5 | namespace Capstone 6 | { 7 | 8 | SplashScreenState::SplashScreenState (size_t timeout): 9 | m_timeout(timeout), 10 | m_elapsed_time(0) 11 | { 12 | 13 | } 14 | 15 | 16 | void SplashScreenState::update (std::size_t delta_time) 17 | { 18 | m_elapsed_time += delta_time; 19 | 20 | if (m_timeout <= m_elapsed_time) 21 | { 22 | m_game->push_state (std::make_unique ()); 23 | } 24 | 25 | // It creates the fade out effect. 26 | if (m_elapsed_time >= (m_timeout - 600)) 27 | { 28 | Color color = Color(10, 10, 10, 0); 29 | 30 | m_udacity_font->color += color; 31 | m_capstone_font->color += color; 32 | m_course_name_font->color += color; 33 | } 34 | 35 | // It's important to change the font color before its update 36 | m_udacity_font->update (delta_time); 37 | m_capstone_font->update (delta_time); 38 | m_course_name_font->update (delta_time); 39 | } 40 | 41 | void SplashScreenState::render (Renderer& renderer) 42 | { 43 | renderer.clear (Color::White); 44 | 45 | m_udacity_font->render (renderer); 46 | m_capstone_font->render (renderer); 47 | m_course_name_font->render (renderer); 48 | 49 | renderer.display (); 50 | } 51 | 52 | void SplashScreenState::prepare (Renderer& renderer) 53 | { 54 | // This defines the screen center 55 | iVector2 centered = { 56 | renderer.get_screen ().x / 2, 57 | renderer.get_screen ().y / 2, 58 | }; 59 | 60 | // It get the smart pointer of each font using the third party library. 61 | m_udacity_font = std::move(m_game->get_font ()->create (FontTheme::kLight, FontSize::kSmall)); 62 | m_capstone_font = std::move(m_game->get_font ()->create (FontTheme::kLight, FontSize::kLarge)); 63 | m_course_name_font = std::move(m_game->get_font ()->create (FontTheme::kLight, FontSize::kSmall)); 64 | 65 | // Assigning the text before prepare the font to get the correct Allocation 66 | // of the font's texture. IT'S NECESSARY TO GET THE MEASURES 67 | m_udacity_font->set_text ("Udacity's"); 68 | m_capstone_font->set_text ("Capstone"); 69 | m_course_name_font->set_text ("C++ Nanodegree"); 70 | 71 | m_udacity_font->prepare (renderer); 72 | m_capstone_font->prepare (renderer); 73 | m_course_name_font->prepare (renderer); 74 | 75 | // This defines the "capstone"'s font text alignment 76 | m_capstone_font->offset = centered; 77 | m_capstone_font->offset.x -= m_capstone_font->size.x / 2; 78 | m_capstone_font->offset.y -= m_capstone_font->size.y / 2 + 30; 79 | 80 | // This defines "udacity"'s font text alignment 81 | m_udacity_font->offset = centered; 82 | m_udacity_font->offset.x -= m_udacity_font->size.x / 2; 83 | m_udacity_font->offset.y = m_capstone_font->offset.y - 45; 84 | 85 | // This defines the "course name"'s font text alignment 86 | m_course_name_font->offset = centered; 87 | m_course_name_font->offset.x -= m_course_name_font->size.x / 2; 88 | m_course_name_font->offset.y = m_capstone_font->offset.y + m_capstone_font->size.y + 15; 89 | } 90 | 91 | void SplashScreenState::handle_input (const KeyPressed& key) 92 | { 93 | // If any known key is pressed immediately change the focus to the next state 94 | if (key != KeyPressed::kUndefined) 95 | { 96 | m_game->push_state (std::make_unique ()); 97 | } 98 | } 99 | } // namespace Capstone -------------------------------------------------------------------------------- /src/states/end_game_state.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_STATES_END_GAME_STATE_HPP 3 | #define CPP_SNAKE_GAME_STATES_END_GAME_STATE_HPP 4 | 5 | #include "../core/game_state.hpp" 6 | #include "../core/button_chooser.hpp" 7 | 8 | namespace Capstone 9 | { 10 | 11 | /** 12 | * This defines the Game Over and the Winner State. 13 | */ 14 | class EndGameState: public GameState 15 | { 16 | public: 17 | /** 18 | * This GameState depends on the second argument assigned to define 19 | * if player lose or win the game. 20 | * 21 | * @param score The score obtained until this GameState get focus 22 | * @param finished If false the player lose, if it's true, the player win 23 | */ 24 | explicit EndGameState(int score, bool finished = false); 25 | 26 | /** 27 | * Destructor 28 | */ 29 | virtual ~EndGameState () = default; 30 | 31 | /** 32 | * This updates the GameObject's logic. 33 | * 34 | * It's invoked at every game loop cycle, respecting the interval time 35 | * assigned as arguments by the Game class. 36 | * 37 | * This method holds the responsibility to update the color of the button. 38 | * 39 | * @param delta_time The elapsed time since the last game loop. 40 | */ 41 | void update (std::size_t delta_time) override; 42 | 43 | 44 | /** 45 | * This Renders the GameObjects 46 | * 47 | * It's invoked at every game loop cycle to show the game object on 48 | * the screen. The font and buttons are rendered inside this method. 49 | * 50 | * @param renderer The Renderer instance used to clear the last 51 | * frame and show the object on the screen. 52 | */ 53 | void render (Renderer& renderer) override; 54 | 55 | /** 56 | * This prepares the GameObject to be rendered. 57 | * 58 | * As the GameState shouldn't hold the Renderer's reference, the following 59 | * method helps the current class to get access to that. The font and buttons 60 | * are prepared to be rendered properly. 61 | * 62 | * This method is invoked just once, when the state get focus for the first 63 | * time. 64 | * 65 | * @param renderer The Renderer instance to configure the GameObject. 66 | */ 67 | void prepare (Renderer& renderer) override; 68 | 69 | /** 70 | * It allows the current class to define its own input handler 71 | * 72 | * It ensures the ButtonChooser can use the user input to choose the 73 | * correct button in focus. 74 | * 75 | * @param key The command dispatched that can be used to handle the GameObjects 76 | */ 77 | void handle_input (const KeyPressed& key) override; 78 | 79 | protected: 80 | /** 81 | * The font used to display the "score" text 82 | */ 83 | std::unique_ptr m_score_font; 84 | 85 | /** 86 | * The font used to display the "Game Over" text 87 | */ 88 | std::unique_ptr m_title_font; 89 | 90 | /** 91 | * The button list used to define the options: play again and quit 92 | */ 93 | ButtonChooser m_button_chooser; 94 | 95 | /** 96 | * This holds the score of the game 97 | */ 98 | int m_score; 99 | 100 | /** 101 | * This defines if the state will represents a game over state 102 | * or a winner state 103 | */ 104 | int m_finished; 105 | }; 106 | } // namespace Capstone 107 | 108 | 109 | #endif // CPP_SNAKE_GAME_STATES_END_GAME_STATE_HPP 110 | -------------------------------------------------------------------------------- /src/core/color.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "color.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | const Color Color::Black (0, 0, 0); 8 | const Color Color::White (255, 255, 255); 9 | const Color Color::Red (255, 0, 0); 10 | const Color Color::Green (0, 255, 0); 11 | const Color Color::Blue (0, 0, 255); 12 | const Color Color::Yellow (255, 255, 0); 13 | const Color Color::Magenta (255, 0, 255); 14 | const Color Color::Cyan (0, 255, 255); 15 | const Color Color::Transparent (0, 0, 0, 0); 16 | 17 | // Default constructor 18 | // This Defines blacks as current color 19 | Color::Color (): 20 | r (0), 21 | g (0), 22 | b (0), 23 | a (255) 24 | { 25 | 26 | } 27 | 28 | // Parametrized constructor 29 | // This defines each color channel 30 | Color::Color (int red, int green, int blue, int alpha): 31 | r (static_cast(std::min(std::max(red, 0),255))), 32 | g (static_cast(std::min(std::max(green, 0),255))), 33 | b (static_cast(std::min(std::max(blue, 0),255))), 34 | a (static_cast(std::min(std::max(alpha, 0),255))) 35 | { 36 | 37 | } 38 | 39 | // This defines the current color as hexadecimal 40 | Color::Color (Uint32 color): 41 | r (static_cast((color & 0xff000000) >> 24)), 42 | g (static_cast((color & 0x00ff0000) >> 16)), 43 | b (static_cast((color & 0x0000ff00) >> 8)), 44 | a (static_cast((color & 0x000000ff) >> 0)) 45 | { 46 | 47 | } 48 | 49 | // Overload of the binary == operator 50 | bool operator== (const Color& left, const Color& right) 51 | { 52 | return (left.r == right.r) && 53 | (left.g == right.g) && 54 | (left.b == right.b) && 55 | (left.a == right.a); 56 | } 57 | 58 | // Overload of the binary != operator 59 | bool operator!= (const Color& left, const Color& right) 60 | { 61 | return !(left == right); 62 | } 63 | 64 | // Overload of the binary + operator 65 | Color operator+ (const Color& left, const Color& right) 66 | { 67 | return Color(Uint8(std::min(int(left.r) + right.r, 255)), 68 | Uint8(std::min(int(left.g) + right.g, 255)), 69 | Uint8(std::min(int(left.b) + right.b, 255)), 70 | Uint8(std::min(int(left.a) + right.a, 255))); 71 | } 72 | 73 | // Overloads of the binary - operator 74 | Color operator- (const Color& left, const Color& right) 75 | { 76 | return Color(Uint8(std::max(int(left.r) - right.r, 0)), 77 | Uint8(std::max(int(left.g) - right.g, 0)), 78 | Uint8(std::max(int(left.b) - right.b, 0)), 79 | Uint8(std::max(int(left.a) - right.a, 0))); 80 | } 81 | 82 | // Overloads of the binary * operator 83 | Color operator* (const Color& left, const Color& right) 84 | { 85 | return Color(Uint8(int(left.r) * right.r / 255), 86 | Uint8(int(left.g) * right.g / 255), 87 | Uint8(int(left.b) * right.b / 255), 88 | Uint8(int(left.a) * right.a / 255)); 89 | } 90 | 91 | // Overloads of the binary += operator 92 | Color& operator+= (Color& left, const Color& right) 93 | { 94 | return left = left + right; 95 | } 96 | 97 | // Overloads of the binary -= operator 98 | Color& operator-= (Color& left, const Color& right) 99 | { 100 | return left = left - right; 101 | } 102 | 103 | // Overloads of the binary *= operator 104 | Color& operator*= (Color& left, const Color& right) 105 | { 106 | return left = left * right; 107 | } 108 | 109 | } // namespace Capstone -------------------------------------------------------------------------------- /src/core/font.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_FONT_HPP 3 | #define CPP_SNAKE_GAME_CORE_FONT_HPP 4 | 5 | #include "game_object.hpp" 6 | 7 | namespace Capstone 8 | { 9 | 10 | class Font; 11 | 12 | /** 13 | * This class allows a third part library to create the Font's logic 14 | */ 15 | class Typography { 16 | public: 17 | 18 | /** 19 | * Destructor 20 | */ 21 | virtual ~Typography () = default; 22 | 23 | /** 24 | * This allows the third party font class to prepare their 25 | * own renderer to use the font correctly. 26 | * 27 | * @param renderer The Renderer class reference 28 | */ 29 | virtual void prepare(Renderer &renderer) = 0; 30 | 31 | /** 32 | * This creates a texture from font's text to save memory 33 | * 34 | * @param font The Font's class reference 35 | */ 36 | virtual void create_texture(Font& font) = 0; 37 | 38 | /** 39 | * This renders the font properly 40 | * 41 | * @param font The Font's class reference 42 | */ 43 | virtual void get(const Font &font) const = 0; 44 | 45 | 46 | /** 47 | * This creates the third party library logic 48 | * 49 | * @param font The Font's class reference 50 | */ 51 | virtual void update (Font &font) = 0; 52 | }; 53 | 54 | /** 55 | * It ensures the Game can use the Font without "know" what's 56 | * the library in use. 57 | */ 58 | class Font: public GameObject, public iAllocation 59 | { 60 | public: 61 | 62 | /** 63 | * This allows the current class to use external Typography 64 | * 65 | * Each library can implement their own Typography rules and define 66 | * use with Font class easily. 67 | * 68 | * @param typography The third part library Font's logic 69 | */ 70 | Font(Typography *typography); 71 | 72 | /** 73 | * This deletes the Typography pointer properly. 74 | */ 75 | virtual ~Font (); 76 | 77 | /** 78 | * This updates the font behavior 79 | * 80 | * @param delta_time The elapsed time since the last frame 81 | */ 82 | void update (std::size_t delta_time) override; 83 | 84 | /** 85 | * This renders the font properly 86 | * 87 | * @param renderer The Renderer class reference 88 | */ 89 | void render (Renderer& renderer) override; 90 | 91 | /** 92 | * This prepares the font to be rendered 93 | * 94 | * @param renderer The Renderer class reference 95 | */ 96 | void prepare (Renderer& renderer) override; 97 | 98 | /** 99 | * This defines the text to be rendered 100 | * 101 | * @param text The text to be rendered properly 102 | */ 103 | void set_text(const std::string& text); 104 | 105 | /** 106 | * This returns the text assigned to the font 107 | * 108 | * @return std::string The text assigned to the font 109 | */ 110 | std::string get_text() const; 111 | 112 | public: 113 | 114 | /** 115 | * This defines the color of the font. 116 | * 117 | * It's useful to change the color of the text and to define 118 | * transparency as well. 119 | */ 120 | Color color; 121 | 122 | protected: 123 | 124 | /** 125 | * The text to be rendered by font 126 | */ 127 | std::string m_text; 128 | 129 | /** 130 | * The external Font's logic 131 | * 132 | * It allows the font to use external library logic ti be rendered. 133 | */ 134 | Typography* m_typography; 135 | }; 136 | } // namespace Capstone 137 | 138 | 139 | #endif // CPP_SNAKE_GAME_CORE_FONT_HPP 140 | -------------------------------------------------------------------------------- /src/core/food.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_FOOD_HPP 3 | #define CPP_SNAKE_GAME_CORE_FOOD_HPP 4 | 5 | #include 6 | #include "game_thread.hpp" 7 | #include "block.hpp" 8 | #include "../actor/snake.hpp" 9 | #include "random_position.hpp" 10 | 11 | namespace Capstone 12 | { 13 | 14 | /** 15 | * This defines the thread instance of Block to be used as food's GameObject 16 | * 17 | * All the food behavior is contained is this class. 18 | */ 19 | class Food: public GameThread 20 | { 21 | public: 22 | /** 23 | * This replaces the food offset using thread 24 | * 25 | * It should be invoked to start the `replace_food` protected method 26 | * using "local" thread. 27 | */ 28 | void replace(); 29 | 30 | /** 31 | * This prepares the food to be rendered 32 | * 33 | * It creates the common behavior of the food such as color, position 34 | * and start the `shared_check_collision` method using thread as well. 35 | * 36 | * @param renderer The renderer instance 37 | */ 38 | void prepare(Renderer &renderer) override; 39 | 40 | /** 41 | * This render the food 42 | * 43 | * It just execute the Block's render method properly 44 | * 45 | * @param renderer The renderer instance 46 | */ 47 | void render(Renderer &renderer) override; 48 | 49 | /** 50 | * This updates the food behavior 51 | * 52 | * It just execute the Block's update method properly 53 | * 54 | * @param delta_time The elapsed time between each GameLoop 55 | */ 56 | void update (std::size_t delta_time) override; 57 | 58 | /** 59 | * This returns the SnakeStatus used when colliding 60 | * 61 | * The food returns SnakeStatus::kEating when it collides with 62 | * the Snake. 63 | * 64 | * @return SnakeStatus This is always SnakeStatus::kEating 65 | */ 66 | const SnakeStatus get_status () const override; 67 | 68 | /** 69 | * This assign the Snake smart pointer 70 | * 71 | * It ensures the Snake used on game can be used on check_collision properly 72 | * 73 | * @param target 74 | */ 75 | void set_target (std::shared_ptr target); 76 | 77 | private: 78 | 79 | /** 80 | * This defines the collision check of the food 81 | * 82 | * It uses a thread to allows the food to dispatch the SnakeStatus::kEating 83 | * always both, the food and snake are colliding. 84 | */ 85 | void shared_check_collision (); 86 | 87 | /** 88 | * This ensures the food only will be placed in a void "grid space" 89 | * 90 | * The method will be executed using a std::async and should not be 91 | * invoked directly. 92 | * 93 | * @param snake The "m_target" shared pointer of this class 94 | * @param random The "m_random" shared pointer of this class 95 | * @return iVector2 The offset that don't collide with snake's head and body 96 | */ 97 | iVector2 replace_food (const std::shared_ptr& snake, std::shared_ptr random); 98 | 99 | private: 100 | 101 | /** 102 | * This defines the Block used as food on screen 103 | */ 104 | std::shared_ptr m_food; 105 | 106 | /** 107 | * This defines the class to get random position 108 | */ 109 | std::shared_ptr m_random; 110 | 111 | /** 112 | * This defines the main actor used on the game 113 | */ 114 | std::shared_ptr m_target; 115 | }; 116 | } // namespace Capstone 117 | 118 | #endif // CPP_SNAKE_GAME_CORE_FOOD_HPP 119 | -------------------------------------------------------------------------------- /src/core/food.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "food.hpp" 3 | #include "random_position.hpp" 4 | 5 | namespace Capstone 6 | { 7 | 8 | void Food::render (Renderer& renderer) 9 | { 10 | m_food->render (renderer); 11 | } 12 | 13 | // This replaces the food offset using a thread that returns 14 | // a non-filled "game space" 15 | void Food::replace() 16 | { 17 | // This is executed in another thread 18 | auto offset = std::async(&Food::replace_food, this, m_target, m_random); 19 | offset.wait(); 20 | 21 | // The offset is not colliding with the head and body Snake 22 | m_food->offset = offset.get(); 23 | } 24 | 25 | // This prepares the Food to be rendered 26 | void Food::prepare (Renderer& renderer) 27 | { 28 | m_random = std::make_shared (renderer.get_grid ()); 29 | 30 | // This creates the Block and prepare it 31 | m_food = std::move(std::make_shared()); 32 | m_food->prepare (renderer); 33 | 34 | // This set the color 35 | m_food->color = Color(0xFFCC00FF); 36 | 37 | iVector2 player_position = {renderer.get_grid ().x - 1, renderer.get_grid ().y - 1}; 38 | 39 | // This ensures the Food and the Snake will not placed in same offset 40 | // on the first time 41 | while(true) 42 | { 43 | auto offset = m_random->get(); 44 | 45 | if (player_position != offset) 46 | { 47 | m_food->offset = offset; 48 | break; 49 | } 50 | } 51 | 52 | // This starts the `check_collision` in another thread 53 | m_threads.emplace_back(std::thread(&Food::shared_check_collision, this)); 54 | } 55 | 56 | // This dispatch a SnakeStatus::kEating to Snake when the food and the actor 57 | // is colliding 58 | void Food::shared_check_collision () 59 | { 60 | while(m_target != nullptr) 61 | { 62 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 63 | std::unique_lock lock(m_mutex); 64 | 65 | if (m_target->get_status () != SnakeStatus::kEating) 66 | { 67 | if (m_target->check_collision (*m_food)) 68 | { 69 | m_target->set_status(get_status ()); 70 | } 71 | } 72 | } 73 | } 74 | 75 | void Food::update (std::size_t delta_time) 76 | { 77 | m_food->update (delta_time); 78 | } 79 | 80 | // This assign the game actor to be used on check_collision properly 81 | void Food::set_target (std::shared_ptr target) 82 | { 83 | m_target = std::move(target); 84 | } 85 | 86 | // This defines the only SnakeStatus dispatched by class 87 | const SnakeStatus Food::get_status () const 88 | { 89 | return SnakeStatus::kEating; 90 | } 91 | 92 | // This ensures the food will not be placed in an already filled "grid space" 93 | iVector2 Food::replace_food (const std::shared_ptr& snake, std::shared_ptr random) 94 | { 95 | while(true) 96 | { 97 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 98 | std::unique_lock lock(m_mutex); 99 | 100 | // This creates a new offset to replace the food 101 | auto new_offset = random->get(); 102 | 103 | // It ensures the Snake's head is not occupying the same "grid space" 104 | // of the new proposed offset 105 | if (new_offset == iVector2(snake->offset)) 106 | { 107 | continue; 108 | } 109 | 110 | // It ensures the Snake's body is not occupying the same "grid space" 111 | // of the new proposed offset 112 | for(const auto& item: snake->body) 113 | { 114 | if (new_offset == item.offset) 115 | { 116 | continue; 117 | } 118 | } 119 | 120 | return new_offset; 121 | } 122 | } 123 | 124 | } // namespace Capstone 125 | -------------------------------------------------------------------------------- /src/states/end_game_state.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "end_game_state.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | EndGameState::EndGameState (int score, bool finished): 8 | m_score(score), 9 | m_finished{finished} 10 | { 11 | 12 | } 13 | 14 | void EndGameState::update (std::size_t delta_time) 15 | { 16 | // This update the font color in focus. 17 | m_button_chooser.update (delta_time); 18 | } 19 | 20 | void EndGameState::render (Renderer& renderer) 21 | { 22 | renderer.clear (Color(0x1E1E1EFF)); 23 | 24 | // This renders the fonts 25 | m_score_font->render (renderer); 26 | m_title_font->render (renderer); 27 | m_button_chooser.render (renderer); 28 | 29 | renderer.display (); 30 | } 31 | 32 | void EndGameState::prepare (Renderer& renderer) 33 | { 34 | // This defines the screen center 35 | iVector2 centered = { 36 | renderer.get_screen ().x / 2, 37 | renderer.get_screen ().y / 2 38 | }; 39 | 40 | // This defines the score's font instance 41 | m_score_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 42 | m_score_font->set_text("SCORE: " + std::to_string (m_score)); 43 | m_score_font->prepare (renderer); 44 | 45 | 46 | // The message depends on the `m_finished` value 47 | std::string title_message = "GAME OVER"; 48 | 49 | if (m_finished) 50 | { 51 | title_message = "YOU WIN!"; 52 | } 53 | 54 | // This defines the title's font instance 55 | m_title_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kMedium); 56 | m_title_font->set_text(title_message); 57 | m_title_font->prepare (renderer); 58 | 59 | // This defines the "play again"'s font instance 60 | auto play_again_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 61 | play_again_font->set_text ("PLAY AGAIN"); 62 | play_again_font->prepare (renderer); 63 | 64 | // This defines the quit's font instance 65 | auto quit_font = m_game->get_font ()->create (FontTheme::kDark, FontSize::kSmall); 66 | quit_font->set_text ("QUIT"); 67 | quit_font->prepare (renderer); 68 | 69 | 70 | // This align the x coordinate of the title's font on center of screen 71 | m_score_font->offset = centered; 72 | m_score_font->offset.x -= (m_score_font->size.x / 2); 73 | 74 | // This align the x coordinate of the title's font on center of screen 75 | m_title_font->offset = centered; 76 | m_title_font->offset.x -= m_title_font->size.x / 2; 77 | 78 | // This align the x coordinate of the quit's font on center of screen 79 | quit_font->offset = centered; 80 | quit_font->offset.x -= quit_font->size.x / 2; 81 | 82 | // This align the x coordinate of the "play again"'s font on center of screen 83 | play_again_font->offset = centered; 84 | play_again_font->offset.x -= play_again_font->size.x / 2; 85 | 86 | // This defines the `y` coordinate of each font 87 | m_score_font->offset.y = play_again_font->offset.y - m_title_font->size.y - 100; 88 | m_title_font->offset.y = play_again_font->offset.y - m_title_font->size.y - 15; 89 | quit_font->offset.y = play_again_font->offset.y + quit_font->size.y + 3; 90 | 91 | // This defines the resume's command button 92 | m_button_chooser.add_button (std::move(play_again_font), [this]() -> void { 93 | m_game->pop_state (); 94 | }); 95 | 96 | // This defines the quit's command button 97 | m_button_chooser.add_button (std::move(quit_font), [this]() -> void { 98 | m_game->is_running = false; 99 | }); 100 | 101 | // It's important to prepare the buttons after add them all. 102 | m_button_chooser.prepare (renderer); 103 | } 104 | 105 | void EndGameState::handle_input (const KeyPressed& key) 106 | { 107 | m_button_chooser.handle_input (key); 108 | } 109 | 110 | } // namespace Capstone 111 | -------------------------------------------------------------------------------- /src/sdl/typography.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_SDL_TYPOGRAPHY_HPP 3 | #define CPP_SNAKE_GAME_SDL_TYPOGRAPHY_HPP 4 | 5 | #include 6 | #include 7 | 8 | #include "../core/font.hpp" 9 | #include "renderer.hpp" 10 | 11 | namespace SDL 12 | { 13 | /** 14 | * This allows the GameState inherited class to use the SDL's Font 15 | * logic. 16 | * 17 | * It creates a texture of the font's text to save memory. This way, 18 | * the Allocations variables will change the texture data, instead 19 | * the "raw" font properly. 20 | */ 21 | class Typography: public Capstone::Typography { 22 | public: 23 | 24 | /** 25 | * Parametrized constructor 26 | * 27 | * This creates a SDL's font using the size and the relative path 28 | * to the font file. 29 | * 30 | * @param path The relative path to the font file 31 | * @param size The size of the expected font 32 | */ 33 | Typography(const std::string& path, unsigned int size); 34 | 35 | /** 36 | * This defines the destructor 37 | * 38 | * It destroys the SDL's pointer created to execute the expected 39 | * behavior. 40 | */ 41 | ~Typography () override; 42 | 43 | /** 44 | * This prepared the Renderer, converting it to SDL::Renderer class. 45 | * 46 | * @param renderer The Font class reference 47 | */ 48 | void prepare (Capstone::Renderer& renderer) override; 49 | 50 | /** 51 | * This creates the texture from SDL's font text. 52 | * 53 | * It's useful to save memory. Rendering the font at each GameLoop is 54 | * pretty expensive, so we create a texture, using the font's text 55 | * to save memory. 56 | * 57 | * @param font The Font class reference 58 | */ 59 | void create_texture (Capstone::Font& font) override; 60 | 61 | /** 62 | * This render the texture created from SDL's font text. 63 | * 64 | * This should be used on Font's render method. It not use the common 65 | * Renderer's `fill` method to be rendered. 66 | * 67 | * @param font The Font class reference 68 | */ 69 | void get (const Capstone::Font& font) const override; 70 | 71 | /** 72 | * This checks if the attributes used by the font properly has changed. 73 | * 74 | * It checks if the text and the color of the Font class was altered 75 | * from the last time. 76 | * 77 | * @param font The Font class reference 78 | */ 79 | void update (Capstone::Font& font) override; 80 | 81 | protected: 82 | 83 | /** 84 | * The pointer of the SDL's font properly 85 | */ 86 | TTF_Font *m_font; 87 | 88 | /** 89 | * It holds the Renderer pointer. As the current class need to create 90 | * a texture on GameState's update method, we need to keep it accessible. 91 | * 92 | * It broke the GameState logic, because the class expect the Renderer 93 | * should be accessed on `prepare` and `render` methods, but it's necessary 94 | * once the SDL's font is not rendered as "ordinary" classes. 95 | */ 96 | Renderer *m_renderer; 97 | 98 | /** 99 | * It's created to save memory because to render the font directly is 100 | * pretty expensive. 101 | */ 102 | SDL_Texture *m_texture; 103 | 104 | /** 105 | * This holds the color assigned to the Capstone::Font on the last 106 | * GameLoop. It allows the current class to check if the color changed 107 | * between each frame and recreate the font's texture always a change 108 | * is identified 109 | */ 110 | Capstone::Color m_color; 111 | 112 | /** 113 | * This holds the text assigned to the Capstone::Font on the last 114 | * GameLoop. It allows the current class to check if the text changed 115 | * between each frame and recreate the font's texture always a change 116 | * is identified 117 | */ 118 | std::string m_text; 119 | }; 120 | } // namespace SDL 121 | 122 | 123 | #endif //CPP_SNAKE_GAME_SDL_TYPOGRAPHY_HPP 124 | -------------------------------------------------------------------------------- /src/core/button_chooser.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_BUTTON_CHOOSER_HPP 3 | #define CPP_SNAKE_GAME_CORE_BUTTON_CHOOSER_HPP 4 | 5 | #include 6 | #include "game_object.hpp" 7 | #include "font.hpp" 8 | #include "controller.hpp" 9 | 10 | namespace Capstone 11 | { 12 | 13 | /** 14 | * This tries to imitate a Command design pattern, allowing a font 15 | * execute a command defined as a function. 16 | */ 17 | class Button 18 | { 19 | public: 20 | /** 21 | * Parametrized constructor 22 | * 23 | * This defines a font to be displayed on screen and a command to be 24 | * executed, simulating a button behavior. 25 | * 26 | * @param font This defines a well configured font. 27 | * @param command This defines a function to be executed when necessary 28 | */ 29 | Button(std::unique_ptr font, const std::function&& command); 30 | 31 | /** 32 | * This executes the function defined on constructor 33 | * 34 | * It allows a font can be used to execute a command, without creating 35 | * a brand new class to encapsulate this behavior. 36 | */ 37 | void execute(); 38 | 39 | public: 40 | /** 41 | * This saves the original font color, so if it changes 42 | * it's possible to rollback when necessary. 43 | */ 44 | Color color; 45 | 46 | /** 47 | * This hold the font to be displayed as Button label. 48 | */ 49 | std::unique_ptr font; 50 | 51 | protected: 52 | /** 53 | * This defines the command to be dispatched when the `execute` method 54 | * is invoked. 55 | */ 56 | std::function m_command; 57 | }; 58 | 59 | /** 60 | * This class allows a font to behave like a button, executing 61 | * a command when necessary. 62 | */ 63 | class ButtonChooser: public GameObject 64 | { 65 | public: 66 | /** 67 | * Default constructor 68 | */ 69 | ButtonChooser(); 70 | 71 | /** 72 | * Destructor 73 | */ 74 | virtual ~ButtonChooser() = default; 75 | 76 | /** 77 | * This updates the Button behavior 78 | * 79 | * It ensures the Button in focus will assume the "active" color, 80 | * and the last selected will back their original color as well. 81 | * 82 | * @param delta_time The elapsed time between each GameLoop 83 | */ 84 | void update (std::size_t delta_time) override; 85 | 86 | 87 | /** 88 | * This render the Button 89 | * 90 | * It renders all Button on the vector. 91 | * 92 | * @param renderer The renderer instance 93 | */ 94 | void render (Renderer& renderer) override; 95 | 96 | /** 97 | * This prepares the Buttons to be rendered 98 | * 99 | * It assigns the active color, to the first button on the vector. 100 | * 101 | * @param renderer The renderer instance 102 | */ 103 | void prepare (Renderer& renderer) override; 104 | 105 | /** 106 | * It allows the current class to define its own input handler 107 | * 108 | * It ensures the Button can be selected and executed properly. 109 | * 110 | * @param key The command dispatched that can be used to handle the Button 111 | */ 112 | void handle_input(const KeyPressed &key); 113 | 114 | /** 115 | * This creates a Button. 116 | * 117 | * This adds a font and a function, mixing them to create a button and 118 | * push them to the vector. 119 | * 120 | * @param font The font smart pointer to represent the Button label 121 | * @param command The command used to create the Button behavior when dispatched 122 | */ 123 | void add_button(std::unique_ptr font, const std::function&& command); 124 | 125 | public: 126 | /** 127 | * This defines the Button's color is focus 128 | */ 129 | Color active; 130 | 131 | protected: 132 | /** 133 | * The index of the activated button on vector 134 | */ 135 | int m_cursor; 136 | 137 | /** 138 | * The button vector 139 | */ 140 | std::vector