├── PongGame.png ├── res ├── icons │ ├── ball.png │ ├── pause.png │ ├── paddle.bmp │ ├── separator.bmp │ └── pause-background.png ├── fonts │ ├── OpenSans-Bold.ttf │ ├── OpenSans-ExtraBold.ttf │ └── OpenSans-Regular.ttf └── sounds │ └── effects │ └── hit_paddle.wav ├── .gitignore ├── src ├── main.cpp ├── resources.hpp ├── math │ ├── random.cpp │ ├── random.hpp │ ├── vector2d.hpp │ └── vector2d.cpp ├── graphics │ ├── font_manager.hpp │ ├── font_manager.cpp │ ├── texture.cpp │ ├── texture.hpp │ ├── window.hpp │ ├── image_manager.hpp │ ├── image_manager.cpp │ ├── window.cpp │ ├── font.hpp │ └── font.cpp ├── debug.hpp ├── audio.hpp ├── audio.cpp ├── paddle.cpp ├── game.hpp ├── paddle.hpp ├── ball.hpp ├── ball.cpp └── game.cpp ├── .github └── workflows │ └── ccpp.yml ├── Makefile ├── PongGame.pro ├── README.md └── LICENSE.md /PongGame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/PongGame.png -------------------------------------------------------------------------------- /res/icons/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/icons/ball.png -------------------------------------------------------------------------------- /res/icons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/icons/pause.png -------------------------------------------------------------------------------- /res/icons/paddle.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/icons/paddle.bmp -------------------------------------------------------------------------------- /res/icons/separator.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/icons/separator.bmp -------------------------------------------------------------------------------- /res/fonts/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/fonts/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Compilation objects 3 | # 4 | *.o 5 | 6 | # 7 | # Binaries 8 | # 9 | *.out 10 | -------------------------------------------------------------------------------- /res/fonts/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/fonts/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /res/fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /res/icons/pause-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/icons/pause-background.png -------------------------------------------------------------------------------- /res/sounds/effects/hit_paddle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaelcn/pong-game/HEAD/res/sounds/effects/hit_paddle.wav -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "game.hpp" 2 | 3 | int main(void) 4 | { 5 | Game pong_game; 6 | 7 | while (pong_game.is_running()) { 8 | pong_game.run(); 9 | } 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: make 13 | run: make release 14 | -------------------------------------------------------------------------------- /src/resources.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Resources { 6 | const std::string paddle ="res/icons/paddle.bmp"; 7 | const std::string ball = "res/icons/ball.png"; 8 | const std::string pause_background = "res/icons/pause-background.png"; 9 | const std::string pause_message = "res/icons/pause.png"; 10 | } 11 | -------------------------------------------------------------------------------- /src/math/random.cpp: -------------------------------------------------------------------------------- 1 | #include "random.hpp" 2 | 3 | std::random_device Random::m_random_device; 4 | 5 | float Random::get_real(float a, float b) 6 | { 7 | std::mt19937 generator(m_random_device()); 8 | std::uniform_real_distribution<> distribution(a, b); 9 | 10 | return distribution(generator); 11 | } 12 | 13 | int Random::get_int(int a, int b) 14 | { 15 | std::mt19937 generator(m_random_device()); 16 | std::uniform_int_distribution<> distribution(a, b); 17 | 18 | return distribution(generator); 19 | } 20 | -------------------------------------------------------------------------------- /src/graphics/font_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * @brief The FontManager class is a wrapper to the sdl-ttf API. 8 | */ 9 | class FontManager 10 | { 11 | protected: 12 | // Internal struct containing font information. 13 | TTF_Font* m_font; 14 | // Internal struct containing R, G, B, A information, will be used as the 15 | //font color. 16 | SDL_Color m_color; 17 | // Internal strucut containing shade color information, it will only be used 18 | //if render_text_shaded(). 19 | SDL_Color m_color_shaded; 20 | 21 | // A boolean storing the SDL_TTF initialization state. 22 | static bool m_ttf_opened; 23 | public: 24 | FontManager(); 25 | ~FontManager(); 26 | }; 27 | -------------------------------------------------------------------------------- /src/graphics/font_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "font_manager.hpp" 2 | #include "debug.hpp" 3 | 4 | bool FontManager::m_ttf_opened = false; 5 | 6 | FontManager::FontManager() 7 | { 8 | m_font = nullptr; 9 | 10 | m_color.r = 0xff; 11 | m_color.b = 0xff; 12 | m_color.g = 0xff; 13 | m_color.a = 1; 14 | 15 | m_color_shaded.r = 0x00; 16 | m_color_shaded.b = 0x00; 17 | m_color_shaded.g = 0x00; 18 | m_color_shaded.a = 1; 19 | 20 | if (!m_ttf_opened) { 21 | // FIXME: this code is being called for every font object present on the system 22 | // and we should have only one instance of the font manager. 23 | if (TTF_Init() != 0) { 24 | Debug::log_err("failed to initialize the font system", SDL_GetError()); 25 | return; 26 | } 27 | 28 | m_ttf_opened = true; 29 | 30 | Debug::log("font subsystem initialized"); 31 | } 32 | } 33 | 34 | FontManager::~FontManager() 35 | { 36 | TTF_Quit(); 37 | } 38 | -------------------------------------------------------------------------------- /src/math/random.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * @brief The Random class is responsible to abstract methods in which the 7 | * programmer will use to obtain random numbers. 8 | */ 9 | class Random { 10 | private: 11 | Random() {} 12 | /// 13 | static std::random_device m_random_device; 14 | public: 15 | ~Random() {} 16 | 17 | /** 18 | * @brief Returns a real number between "a" and "b". 19 | * @param a the start interval value. 20 | * @param b the final interval value 21 | * @return a random float between the interval "a" and "b". 22 | */ 23 | static float get_real(float a, float b); 24 | /** 25 | * @brief Returns an integer number between the interval 26 | * "a" and "b". 27 | * @param a the start interval value. 28 | * @param b the final interval value 29 | * @return a random int between the interval "a" and "b" 30 | */ 31 | static int get_int(int a, int b); 32 | }; 33 | -------------------------------------------------------------------------------- /src/graphics/texture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "texture.hpp" 4 | #include "debug.hpp" 5 | 6 | Texture::Texture(std::shared_ptr window) 7 | { 8 | m_window = window; 9 | m_texture = nullptr; 10 | } 11 | 12 | Texture::~Texture() 13 | { 14 | if (m_texture != nullptr) { 15 | SDL_DestroyTexture(m_texture); 16 | } 17 | } 18 | 19 | void Texture::load(SDL_Surface* image_surface, const int width, const int height, const int x, const int y) 20 | { 21 | m_rect.h = height; 22 | m_rect.w = width; 23 | m_rect.x = x; 24 | m_rect.y = y; 25 | 26 | m_texture = SDL_CreateTextureFromSurface(m_window->get_renderer(), image_surface); 27 | 28 | if (m_texture == nullptr) { 29 | Debug::log_err("failed to create image texture, reason: ", SDL_GetError()); 30 | } 31 | 32 | SDL_FreeSurface(image_surface); 33 | } 34 | 35 | void Texture::render() 36 | { 37 | if (m_texture == nullptr) { 38 | Debug::log_err("image texture is null"); 39 | } else { 40 | SDL_RenderCopy(m_window->get_renderer(), m_texture, nullptr, &m_rect); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/math/vector2d.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @brief A representation of a vector of two float components. 5 | */ 6 | class Vector2D 7 | { 8 | private: 9 | float X; 10 | float Y; 11 | public: 12 | Vector2D(float x, float y) : X(x), Y(y) {} 13 | Vector2D(const Vector2D& o): X(o.x()), Y(o.y()) {} 14 | ~Vector2D() {} 15 | 16 | Vector2D& operator ()(); 17 | Vector2D operator +(const Vector2D& rhs) const; 18 | Vector2D operator -(); 19 | Vector2D operator -(const Vector2D& rhs) const; 20 | Vector2D operator *(const float scalar); 21 | Vector2D& operator +=(const Vector2D &rhs); 22 | Vector2D& operator -=(const Vector2D &rhs); 23 | 24 | bool operator <(const Vector2D &rhs); 25 | bool operator >=(const Vector2D &rhs); 26 | 27 | Vector2D cross(const Vector2D other) const; 28 | Vector2D normalized() const; 29 | 30 | float x() const; 31 | float y() const; 32 | void x(float x); 33 | void y(float y); 34 | 35 | float magnitude() const; 36 | float magnitude_squared() const; 37 | 38 | float dot(const Vector2D& other) const; 39 | 40 | void normalize(); 41 | }; 42 | -------------------------------------------------------------------------------- /src/graphics/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "window.hpp" 8 | 9 | /** 10 | * @brief This class is a wrapper arount the SDL_Texture. 11 | */ 12 | class Texture { 13 | private: 14 | // A raw pointer to a SDL_Texture struct, it represents the actual texture. 15 | SDL_Texture* m_texture; 16 | // Position and size of the texture on the renderer. 17 | SDL_Rect m_rect; 18 | 19 | std::shared_ptr m_window; 20 | public: 21 | Texture(std::shared_ptr window); 22 | ~Texture(); 23 | 24 | /** 25 | * @brief load Create a texture from a SDL_Surface struct. 26 | * @param image_surface The surface containing the image. 27 | * @param width The width of the texture. 28 | * @param height The height of the texture. 29 | * @param x The location of the texture on the x axis. 30 | * @param y The location of the texture on the y axis. 31 | */ 32 | void load(SDL_Surface* image_surface, const int width, const int height, const int x, const int y); 33 | 34 | /** 35 | * @brief renders the texture on the screen 36 | */ 37 | void render(); 38 | }; 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = clang++ 2 | 3 | # Name of the executable 4 | EXEC = pong-game.out 5 | 6 | # Build directories 7 | BUILD = build 8 | OBJ_DIR = obj 9 | SRCDIR = src 10 | SUBDIRS := $(shell find ${SRCDIR} -type d) 11 | 12 | CXXFLAGS := -Wall -Wextra -O3 -std=c++14 $(addprefix -I, ${SUBDIRS}) 13 | CXXFLAGS_D := -Wall -Wextra -O1 -fsanitize=address -g -std=c++14 $(addprefix -I, ${SUBDIRS}) 14 | 15 | LIBS := -lSDL2 -lSDL2_mixer -lSDL2_image -lSDL2_ttf 16 | 17 | SOURCES = $(shell find ${SRCDIR} -name "*.cpp") 18 | 19 | OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES)) 20 | OBJECTS := $(notdir $(OBJECTS)) 21 | OBJECTS := $(addprefix $(BUILD)/$(OBJ_DIR)/, $(OBJECTS)) 22 | 23 | all: release 24 | 25 | release: 26 | $(CXX) $(CXXFLAGS) -c $(SOURCES) 27 | @mv -f *.o $(BUILD)/$(OBJ_DIR) 28 | $(CXX) $(CXXFLAGS) -o $(EXEC) $(OBJECTS) $(LIBS) 29 | @mv -f $(EXEC) $(BUILD) 30 | 31 | debug: 32 | $(CXX) $(CXXFLAGS_D) -c $(SOURCES) 33 | @mv -f *.o $(BUILD)/$(OBJ_DIR) 34 | $(CXX) $(CXXFLAGS_D) -o $(EXEC) $(OBJECTS) $(LIBS) 35 | @mv -f $(EXEC) $(BUILD) 36 | 37 | vars: 38 | @echo "sources: ${SOURCES}" 39 | @echo "objects: ${OBJECTS}" 40 | @echo "flags: ${CXXFLAGS}" 41 | 42 | clean: 43 | rm -f $(wildcard $(RELEASE_BUILD)/$(OBJ_DIR)/*.o) 44 | rm -f $(wildcard $(DEBUG_BUILD)/$(OBJ_DIR)/*.o) 45 | -------------------------------------------------------------------------------- /src/graphics/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | /** 10 | * @brief Class which has all the resources to create a window for the game 11 | */ 12 | class Window { 13 | private: 14 | Window(); 15 | Window(Window&); 16 | 17 | SDL_Window* m_window; 18 | SDL_Renderer* m_renderer; 19 | 20 | std::array m_window_size; 21 | 22 | public: 23 | /** 24 | * @brief Window ctor of the game window. 25 | * @param title The title of the game window. 26 | * @param width The width of the game window. 27 | * @param height The height of the game window. 28 | * @param flags The flags setted to the game window. 29 | */ 30 | Window(const std::string& title, int width, int height, std::uint32_t flags); 31 | ~Window(); 32 | 33 | /** 34 | * @return A pointer to the game window. 35 | */ 36 | SDL_Window* get_window(); 37 | /** 38 | * @return A pointer to the renderer of the window. 39 | */ 40 | SDL_Renderer* get_renderer(); 41 | 42 | /** 43 | * @return The width of the game window. 44 | */ 45 | int get_width(); 46 | /** 47 | * @return The height of the game window. 48 | */ 49 | int get_height(); 50 | }; 51 | -------------------------------------------------------------------------------- /src/debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * @brief A collection of output functions to shout information on the stdout/stderr. 7 | */ 8 | class Debug { 9 | public: 10 | /** 11 | * @brief log The default log, it prints args on the stdout. 12 | * @param args The arguments to be printed on the terminal. 13 | */ 14 | template 15 | static void log(const Ts&... args) 16 | { 17 | std::cout << "INFO: "; 18 | 19 | int expanded[] = { (std::cout << args << "", 0)... }; 20 | (void)expanded; 21 | 22 | std::cout << std::endl; 23 | } 24 | 25 | /** 26 | * @brief log_warn The warning log, it prints out args on the stdout 27 | * which should be taken with care. 28 | * @param args The arguments to be printed on the terminal. 29 | */ 30 | template 31 | static void log_warn(const Ts&... args) 32 | { 33 | std::cout << "WARN: "; 34 | 35 | int expanded[] = { (std::cout << args, 0)... }; 36 | (void)expanded; 37 | 38 | std::cout << std::endl; 39 | } 40 | 41 | /** 42 | * @brief log_err The error log, use this to print really bad things that 43 | * happened with something. 44 | * @param args The arguments to be printed on the terminal. 45 | */ 46 | template 47 | static void log_err(const Ts&... args) 48 | { 49 | std::cerr << "ERR : "; 50 | 51 | int expanded[] = { (std::cerr << args, 0)... }; 52 | (void)expanded; 53 | 54 | std::cerr << std::endl; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/math/vector2d.cpp: -------------------------------------------------------------------------------- 1 | #include "vector2d.hpp" 2 | 3 | #include 4 | 5 | Vector2D& Vector2D::operator ()() 6 | { 7 | return *this; 8 | } 9 | 10 | Vector2D Vector2D::operator +(const Vector2D& rhs) const 11 | { 12 | return Vector2D(X + rhs.X, Y + rhs.Y); 13 | } 14 | 15 | Vector2D Vector2D::operator -() 16 | { 17 | return Vector2D(-X, -Y); 18 | } 19 | 20 | Vector2D Vector2D::operator -(const Vector2D& rhs) const 21 | { 22 | return Vector2D(X - rhs.X, Y - rhs.Y); 23 | } 24 | 25 | Vector2D Vector2D::operator *(const float scalar) 26 | { 27 | return Vector2D(X * scalar, Y * scalar); 28 | } 29 | 30 | Vector2D& Vector2D::operator +=(const Vector2D& rhs) 31 | { 32 | X += rhs.X; 33 | Y += rhs.Y; 34 | 35 | return *this; 36 | } 37 | 38 | bool Vector2D::operator <(const Vector2D& rhs) 39 | { 40 | return this->magnitude() > rhs.magnitude(); 41 | } 42 | 43 | bool Vector2D::operator >=(const Vector2D& rhs) 44 | { 45 | return !(*this < rhs); 46 | } 47 | 48 | float Vector2D::magnitude() const 49 | { 50 | return sqrt((pow(X, 2) + pow(Y, 2))); 51 | } 52 | 53 | float Vector2D::magnitude_squared() const 54 | { 55 | return pow(magnitude(), 2); 56 | } 57 | 58 | float Vector2D::x() const 59 | { 60 | return X; 61 | } 62 | 63 | float Vector2D::y() const 64 | { 65 | return Y; 66 | } 67 | 68 | void Vector2D::x(const float x_) 69 | { 70 | X = x_; 71 | } 72 | 73 | void Vector2D::y(const float y_) 74 | { 75 | Y = y_; 76 | } 77 | 78 | float Vector2D::dot(const Vector2D& other) const 79 | { 80 | return (X * other.X) + (Y * other.Y); 81 | } 82 | 83 | Vector2D Vector2D::cross(const Vector2D other) const 84 | { 85 | return Vector2D(X * other.X, Y * other.Y); 86 | } 87 | -------------------------------------------------------------------------------- /PongGame.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | # Flags 7 | QMAKE_CXXFLAGS += -Wall -Wextra -std=c++0x 8 | 9 | # Optimization flags depends on the build type. 10 | CONFIG(debug, debug|release) 11 | { 12 | win32 { 13 | DESTDIR = $$PWD/out/windows/debug-build 14 | } 15 | unix { 16 | DESTDIR = $$PWD/out/linux/debug-build 17 | } 18 | 19 | OBJECTS_DIR = $$DESTDIR/obj 20 | QMAKE_CXXFLAGS += -O0 21 | } 22 | 23 | CONFIG(release, release|debug) 24 | { 25 | 26 | win32 { 27 | DESTDIR = $$PWD/out/windows/release-build 28 | } 29 | unix { 30 | DESTDIR = $$PWD/out/linux/release-build 31 | } 32 | 33 | OBJECTS_DIR = $$DESTDIR/obj 34 | QMAKE_CXXFLAGS += -O3 35 | } 36 | 37 | # Windows config. 38 | win32 { 39 | INCLUDEPATH += "C:\SDL2\i686-w64-mingw32\include" 40 | LIBS += -L"C:\SDL2\i686-w64-mingw32\lib" 41 | 42 | LIBS += -lmingw32 \ 43 | -lSDL2main \ 44 | -lSDL2 \ 45 | -lSDL2_ttf \ 46 | -lSDL2_image \ 47 | -lSDL2_mixer \ 48 | -static-libgcc 49 | } 50 | 51 | # Linux config. 52 | unix { 53 | LIBS+= -lSDL2 \ 54 | -lSDL2_image \ 55 | -lSDL2_ttf \ 56 | -lSDL2_mixer 57 | } 58 | 59 | SOURCES += src/main.cpp \ 60 | src/paddle.cpp \ 61 | src/ball.cpp \ 62 | src/window.cpp \ 63 | src/game.cpp \ 64 | src/font_manager.cpp \ 65 | src/font.cpp \ 66 | src/audio.cpp \ 67 | src/texture.cpp \ 68 | src/vector2d.cpp \ 69 | src/random.cpp \ 70 | src/image.cpp 71 | 72 | HEADERS += \ 73 | src/paddle.hpp \ 74 | src/ball.hpp \ 75 | src/window.hpp \ 76 | src/game.hpp \ 77 | src/font_manager.hpp \ 78 | src/font.hpp \ 79 | src/audio.hpp \ 80 | src/texture.hpp \ 81 | src/vector2d.hpp \ 82 | src/random.hpp \ 83 | src/debug.hpp \ 84 | src/image.hpp 85 | 86 | DISTFILES += \ 87 | makefile 88 | 89 | -------------------------------------------------------------------------------- /src/audio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | /** 9 | * @brief The Audio class is a wrapper to the sdl-mixer system and also 10 | * offers an API to load an play music/effects on the game. 11 | */ 12 | class Audio { 13 | private: 14 | // A boolean to let us know if the audio is opened or not. 15 | bool m_audio_opened; 16 | // Array of game effects 17 | std::array m_effects; 18 | // Array of the game music 19 | std::array m_musics; 20 | 21 | // An array containing the path for each effect on the hard drive. 22 | std::array m_effects_paths; 23 | // An array containing the path for each music on the hard drive. 24 | std::array m_music_path; 25 | private: 26 | /** 27 | * @brief Loads all musics of the game. 28 | */ 29 | void load_musics(); 30 | 31 | /** 32 | * @brief Loads all the effects of the game. 33 | */ 34 | void load_effects(); 35 | 36 | public: 37 | /** 38 | * @brief The EffectType enum. Each enum corresponds to an effect. 39 | */ 40 | enum class EffectType { 41 | hit_paddle, 42 | score_up, 43 | ball_speed 44 | }; 45 | 46 | /** 47 | * @brief The Music enum 48 | */ 49 | enum class Music { 50 | music_1 51 | }; 52 | 53 | public: 54 | Audio(); 55 | ~Audio(); 56 | 57 | /** 58 | * @brief is_open 59 | * @return 60 | */ 61 | bool is_open(); 62 | 63 | /** 64 | * @brief play_music 65 | * @param music_type 66 | * @param loop_flag 67 | */ 68 | void play_music(Music music_type, const int& loop_flag); 69 | 70 | /** 71 | * @brief play_effect Play any effect of the EffectType enum 72 | * @param effect_type An enum which describes which effects are available. 73 | */ 74 | void play_effect(EffectType effect_type); 75 | 76 | /** 77 | * @brief show_audio_drivers Lists all the audio drivers available. 78 | */ 79 | void show_audio_drivers(); 80 | }; 81 | -------------------------------------------------------------------------------- /src/graphics/image_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | 11 | /** 12 | * @brief The ImageSubsystem class provide functions to load BMP and PNG images. 13 | */ 14 | class ImageManager 15 | { 16 | private: 17 | static bool m_image_opened; 18 | public: 19 | ImageManager(); 20 | ~ImageManager(); 21 | 22 | /** 23 | * @brief Function to load BMP images and then return a pointer to the 24 | * surface that contains the image. 25 | * @param The path to where the image is located. 26 | * @param The color to be interpreted as transparent. 27 | * @return A pointer to a surface which contains the image. 28 | */ 29 | SDL_Surface* load_bmp(const std::string& path, int color_key_state, const std::array& color_key); 30 | 31 | /** 32 | * @brief Function to load BMP images and then return a pointer to the 33 | * surface that contains the image. 34 | * @param The path to where the image is located. 35 | * @param The color to be interpreted as transparent. 36 | * @return A pointer to a surface which contains the image. 37 | */ 38 | SDL_Surface* load_bmp(const std::string &path, const std::array& color_key); 39 | 40 | /** 41 | * @brief Function to load BMP images and then return a pointer to the 42 | * surface that contains the image. 43 | * @param The path to where the image is located. 44 | * @return A pointer to a surface which contains the image. 45 | */ 46 | SDL_Surface* load_bmp(const std::string& path); 47 | 48 | /** 49 | * @brief Function to load PNG images and then return a pointer to the 50 | * surface that contains the image. 51 | * @param The path to where the image is located. 52 | * @return A pointer to a surface which contains the image. 53 | */ 54 | SDL_Surface* load_png(const std::string& path); 55 | }; 56 | -------------------------------------------------------------------------------- /src/graphics/image_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "image_manager.hpp" 2 | #include "debug.hpp" 3 | 4 | #include 5 | #include 6 | 7 | bool ImageManager::m_image_opened = false; 8 | 9 | ImageManager::ImageManager() 10 | { 11 | if (!m_image_opened) { 12 | if (IMG_Init(IMG_INIT_PNG) == 0) { 13 | Debug::log_err("failed to initialize image subsystem, reason:", SDL_GetError()); 14 | return; 15 | } 16 | 17 | Debug::log("image subsystem initialized."); 18 | 19 | m_image_opened = true; 20 | } 21 | } 22 | 23 | ImageManager::~ImageManager() { 24 | if (m_image_opened) { 25 | IMG_Quit(); 26 | } 27 | } 28 | 29 | SDL_Surface* ImageManager::load_bmp(const std::string& path, int color_key_state, const std::array& color_key) 30 | { 31 | SDL_Surface* image = SDL_LoadBMP(path.c_str()); 32 | 33 | if (image == nullptr) { 34 | Debug::log_err("failed to load image ", path,", reason ", SDL_GetError()); 35 | } 36 | else { 37 | SDL_SetColorKey(image, 38 | color_key_state, 39 | SDL_MapRGB( 40 | image->format, 41 | color_key[0], color_key[1], color_key[2] 42 | ) 43 | ); 44 | } 45 | 46 | return image; 47 | } 48 | 49 | SDL_Surface* ImageManager::load_bmp(const std::string &path, const std::array& color_key) 50 | { 51 | return load_bmp(path, SDL_TRUE, color_key); 52 | } 53 | 54 | SDL_Surface* ImageManager::load_bmp(const std::string &path) 55 | { 56 | std::array color_key_{{ 0, 0, 0 }}; 57 | 58 | return load_bmp(path, SDL_FALSE, color_key_); 59 | } 60 | 61 | SDL_Surface* ImageManager::load_png(const std::string &path) 62 | { 63 | SDL_Surface* image = nullptr; 64 | SDL_RWops* rwop = nullptr; 65 | 66 | if (m_image_opened) { 67 | rwop = SDL_RWFromFile(path.c_str(), "rb"); 68 | image = IMG_LoadPNG_RW(rwop); 69 | } 70 | 71 | if (image == nullptr) { 72 | Debug::log_err("failed to load image ", path,", reason ", SDL_GetError()); 73 | } 74 | 75 | return image; 76 | } 77 | -------------------------------------------------------------------------------- /src/graphics/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.hpp" 2 | #include "debug.hpp" 3 | 4 | Window::Window(const std::string& title, int width, int height, std::uint32_t flags) 5 | { 6 | m_window = nullptr; 7 | m_renderer = nullptr; 8 | 9 | 10 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { 11 | Debug::log_err("failed to initialize the video or audio, reason", SDL_GetError()); 12 | } else { 13 | Debug::log("SDL Initialized."); 14 | m_window = SDL_CreateWindow(title.c_str(), 15 | SDL_WINDOWPOS_CENTERED, 16 | SDL_WINDOWPOS_CENTERED, 17 | width, 18 | height, 19 | flags); 20 | 21 | m_window_size[0] = width; 22 | m_window_size[1] = height; 23 | 24 | 25 | if(m_window == nullptr) { 26 | Debug::log_err("failed to create window, reason", SDL_GetError()); 27 | } else { 28 | SDL_ShowCursor(0); //don't show the cursor. 29 | 30 | m_renderer = SDL_CreateRenderer(m_window, -1, 31 | SDL_RENDERER_ACCELERATED | 32 | SDL_RENDERER_PRESENTVSYNC); 33 | 34 | if(m_renderer == nullptr) { 35 | Debug::log_err("failed to create window renderer", SDL_GetError()); 36 | } else { 37 | SDL_SetRenderDrawColor(m_renderer, 0x20, 0x20, 0x20, 1); 38 | } 39 | } 40 | } 41 | } 42 | 43 | Window::~Window() 44 | { 45 | if(m_renderer != nullptr) { 46 | SDL_DestroyRenderer(m_renderer); 47 | } 48 | if(m_window != nullptr) { 49 | SDL_DestroyWindow(m_window); 50 | } 51 | } 52 | 53 | 54 | SDL_Window* Window::get_window() 55 | { 56 | return m_window; 57 | } 58 | 59 | SDL_Renderer* Window::get_renderer() 60 | { 61 | return m_renderer; 62 | } 63 | 64 | int Window::get_width() 65 | { 66 | return m_window_size[0]; 67 | } 68 | 69 | int Window::get_height() 70 | { 71 | return m_window_size[1]; 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | __Pong Game__ 2 | === 3 | 4 | A remake of the __Pong Game__ of Atari(1972) with SDL2! The game has better graphics, but yet, is not so GOOD. 5 | **NOTE: This software is alpha, not all features are ready.** 6 | 7 | *Q: What looks like to play Pong?* 8 | *A: See with your eyes!* 9 | 10 | ![Game Image](PongGame.png) 11 | 12 | __How to play?__ 13 | 14 | Player | Keys | Moviment 15 | ------ | ----------- | -------- 16 | One | W | Up 17 | One | S | Down 18 | Two | Up arrow | Up 19 | Two | Down arrow | Down 20 | 21 | __There's also action keys!__ 22 | 23 | Key | Action | 24 | ------| -----------------| 25 | F11 | Fullscreen mode 26 | ESC | Exit game 27 | F5 | Debug mode 28 | P | Pause game 29 | R | Reset game 30 | 31 | 32 | 33 | This game was compiled in Linux with G++ and in Windows with MingW32, using the Qt and Code::Blocks, so you may have no problems with these IDEs. If you have any issue with the compiler saying that to_string is not a member of std you problaby is not compiling with -std=c++11 or you are compiling with MingW32 that has this _[problem](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52015)! But is easy to solve, so good luck! 34 | 35 | You do __need__ of: 36 | * [SDL2](http://www.libsdl.org/download-2.0.php) 37 | * [SDL2 Image](https://www.libsdl.org/projects/SDL_image/) 38 | * [SDL2 TTF](https://www.libsdl.org/projects/SDL_ttf/) 39 | 40 | 41 | __Remind:__ The res folder has to be on the same folder as the *.exe , otherwise, it will give you a segmentation fault, or you can change the path to the images and fonts in the main.cpp and recompile the game. If you are having problems executing the game is probably because you don't have the runtime libraries of the SDL2, SDL2_image, SDL2_ttf, so just look at the links above and download them. 42 | 43 | 44 | This project is licensee on __[Apache v2](http://www.apache.org/licenses/LICENSE-2.0.html)__! 45 | 46 | __Roadmap:__ 47 | - [X] __Perfect__ collision detection. 48 | - [X] Over each match of the game, a random direction for the ball. 49 | - [x] Ball speed increases over the course of hits in the paddles. 50 | - [X] Pause game 51 | -------------------------------------------------------------------------------- /src/graphics/font.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "font_manager.hpp" 11 | #include "window.hpp" 12 | 13 | /** 14 | * @brief The Font class extends the FontManager and is responsible to offer 15 | * a clean API to open and render fonts onto the screen. 16 | */ 17 | class Font : protected FontManager 18 | { 19 | private: 20 | // The rect which will contain information about where the font will be 21 | // rendered. 22 | SDL_Rect m_rect; 23 | // The name of the font used. 24 | std::string m_name; 25 | 26 | std::shared_ptr m_window; 27 | public: 28 | /** 29 | * @brief Describes the three kind of fonts used in the game. 30 | */ 31 | enum class FontType 32 | { 33 | normal, 34 | bold, 35 | extra_bold 36 | }; 37 | 38 | /** 39 | * @brief Abstracts the styles on which a font can be rendered 40 | */ 41 | enum class FontStyle 42 | { 43 | solid, 44 | shaded 45 | }; 46 | public: 47 | Font(std::shared_ptr window, SDL_Rect& rect); 48 | ~Font(); 49 | 50 | /** 51 | * @brief open_font, function to initialize a font to be used with 52 | * render_text_solid(...). 53 | * @param font_type the type of the font, @see FontType. 54 | * @param size the size of the font. 55 | */ 56 | void open_font(FontType font_type, int size); 57 | 58 | /** 59 | * @brief render_text_solid renders a solid text onto the game window. 60 | * @param text the text which will be rendered on the game window. 61 | */ 62 | void render_text_solid(const std::string& text); 63 | 64 | /** 65 | * @brief render_text_shaded 66 | * @param text 67 | */ 68 | void render_text_shaded(const std::string& text); 69 | 70 | /** 71 | * @brief font_color is a function to set the color of the font. 72 | * @param r - red 73 | * @param g - green 74 | * @param b - blue 75 | * @param a - alpha 76 | */ 77 | void font_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a); 78 | 79 | /** 80 | * @brief font_color is a function to set the color of the font. 81 | * @param colors is an array that contain information about the 3 colors, 82 | * plus an alpha. 83 | */ 84 | void font_color(std::array colors); 85 | private: 86 | void render_text(const FontStyle style, const std::string& text); 87 | }; 88 | -------------------------------------------------------------------------------- /src/graphics/font.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "font.hpp" 6 | #include "debug.hpp" 7 | 8 | #include "graphics/window.hpp" 9 | 10 | Font::Font(std::shared_ptr window, SDL_Rect &rect) : FontManager() 11 | { 12 | m_window = window; 13 | m_rect = rect; 14 | } 15 | 16 | Font::~Font() { 17 | TTF_CloseFont(m_font); 18 | } 19 | 20 | void Font::open_font(FontType type, int size) 21 | { 22 | if (!m_ttf_opened) { 23 | Debug::log_err("the font subsystem was not initialized"); 24 | return; 25 | } 26 | 27 | switch (type) { 28 | case FontType::normal: 29 | m_name = "res/fonts/OpenSans-Regular.ttf"; 30 | break; 31 | case FontType::bold: 32 | m_name = "res/fonts/OpenSans-Bold.ttf"; 33 | break; 34 | case FontType::extra_bold: 35 | m_name = "res/fonts/OpenSans-ExtraBold.ttf"; 36 | break; 37 | default: 38 | break; 39 | } 40 | 41 | m_font = TTF_OpenFont(m_name.c_str(), size); 42 | 43 | if (m_font == nullptr) { 44 | Debug::log_err("failed to load the font ", m_name, ", reason: ", TTF_GetError()); 45 | } 46 | } 47 | 48 | void Font::render_text_solid(const std::string &text) 49 | { 50 | render_text(FontStyle::solid, text); 51 | } 52 | 53 | void Font::render_text_shaded(const std::string& text) 54 | { 55 | render_text(FontStyle::shaded, text); 56 | } 57 | 58 | void Font::render_text(const FontStyle style, const std::string& text) 59 | { 60 | 61 | if (m_font == nullptr) { 62 | Debug::log_err("the font subsystem was not initialized"); 63 | return; 64 | } 65 | 66 | SDL_Surface* surface = nullptr; 67 | 68 | switch (style) { 69 | case FontStyle::shaded: 70 | surface = TTF_RenderText_Shaded(m_font, text.c_str(), m_color, m_color_shaded); 71 | break; 72 | case FontStyle::solid: 73 | surface = TTF_RenderText_Solid(m_font, text.c_str(), m_color); 74 | break; 75 | } 76 | 77 | if (surface == nullptr) { 78 | Debug::log_err("failed to render the font ", m_name); 79 | return; 80 | } 81 | 82 | auto texture = SDL_CreateTextureFromSurface(m_window->get_renderer(), surface); 83 | 84 | SDL_FreeSurface(surface); 85 | 86 | if (texture == nullptr) { 87 | Debug::log_err("failed to create texture from the surface, reason: ", TTF_GetError()); 88 | return; 89 | } 90 | 91 | SDL_RenderCopy(m_window->get_renderer(), texture, nullptr, &m_rect); 92 | SDL_DestroyTexture(texture); 93 | } 94 | 95 | void Font::font_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) 96 | { 97 | m_color.r = r; 98 | m_color.b = g; 99 | m_color.g = b; 100 | m_color.a = a; 101 | } 102 | 103 | void Font::font_color(std::array colors) 104 | { 105 | m_color.r = colors[0]; 106 | m_color.b = colors[1]; 107 | m_color.g = colors[2]; 108 | m_color.a = colors[3]; 109 | } 110 | -------------------------------------------------------------------------------- /src/audio.cpp: -------------------------------------------------------------------------------- 1 | #include "debug.hpp" 2 | #include "audio.hpp" 3 | 4 | Audio::Audio() 5 | { 6 | m_music_path[0] = "res/sounds/musics/music_1.wav"; 7 | 8 | // hit_paddle_effect 9 | m_effects_paths[0] = "res/sounds/effects/hit_paddle.wav"; 10 | // score_up_effect 11 | m_effects_paths[1] = "res/sounds/effects/score_up.wav"; 12 | // ball_speed_up 13 | m_effects_paths[2] = "res/sounds/effects/speed_up.wav"; 14 | 15 | 16 | if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) == -1) { 17 | Debug::log_err("failed to initialize the audio subsystem, reason ", Mix_GetError()); 18 | 19 | show_audio_drivers(); 20 | return; 21 | } 22 | 23 | Debug::log("audio subsystem initialized"); 24 | m_audio_opened = true; 25 | 26 | load_musics(); 27 | load_effects(); 28 | } 29 | 30 | Audio::~Audio() 31 | { 32 | if (m_audio_opened) { 33 | Mix_CloseAudio(); 34 | } 35 | } 36 | 37 | void Audio::load_musics() 38 | { 39 | m_musics[0] = Mix_LoadMUS(m_music_path[0].c_str()); 40 | 41 | if (m_musics[0] == nullptr) { 42 | Debug::log_warn("failed to load music " + m_music_path[0], " reason ", Mix_GetError()); 43 | } 44 | } 45 | 46 | void Audio::load_effects() 47 | { 48 | for (unsigned int i = 0; i < m_effects.size(); i++) { 49 | m_effects[i] = Mix_LoadWAV(m_effects_paths[i].c_str()); 50 | if (m_effects[i] == nullptr) { 51 | Debug::log_warn("failed to load effect " + m_effects_paths[i], " reason ", Mix_GetError()); 52 | } 53 | } 54 | } 55 | 56 | void Audio::play_effect(EffectType effect_type) 57 | { 58 | Mix_Chunk* effect = nullptr; 59 | 60 | switch (effect_type) { 61 | case EffectType::hit_paddle: 62 | effect = m_effects[0]; 63 | break; 64 | case EffectType::score_up: 65 | effect = m_effects[1]; 66 | break; 67 | case EffectType::ball_speed: 68 | effect = m_effects[2]; 69 | break; 70 | default: 71 | Debug::log_warn("invalid effect"); 72 | break; 73 | } 74 | 75 | if (Mix_PlayChannel(-1, effect, 0) != -1) { 76 | //success 77 | } else { 78 | Debug::log_err("failed to play effect, reason: ", Mix_GetError()); 79 | } 80 | } 81 | 82 | // Going to make an enum to be accepted as a flag. 83 | void Audio::play_music(Music music_type, const int& loop_flag) 84 | { 85 | Mix_Music* music = nullptr; 86 | 87 | switch (music_type) { 88 | case Music::music_1: 89 | music = m_musics[0]; 90 | break; 91 | default: 92 | Debug::log_warn(""); 93 | break; 94 | } 95 | 96 | if (Mix_PlayMusic(music, loop_flag) == -1) { 97 | Debug::log_err("failed to play music, reason ", Mix_GetError()); 98 | } 99 | } 100 | 101 | 102 | bool Audio::is_open() 103 | { 104 | return m_audio_opened; 105 | } 106 | 107 | void Audio::show_audio_drivers() 108 | { 109 | for(int i = 0; i < 10; i++) 110 | std::cout << "available on index[" << i << "]: " << SDL_GetAudioDriver(i) << "\n"; 111 | } 112 | -------------------------------------------------------------------------------- /src/paddle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "paddle.hpp" 4 | #include "debug.hpp" 5 | #include "game.hpp" 6 | 7 | int Paddle::m_hits; 8 | 9 | Paddle::Paddle(std::shared_ptr window, SDL_Surface* paddle_image, const float x, const float y) 10 | { 11 | if(paddle_image == nullptr) { 12 | Debug::log_err("the paddle image is null"); 13 | } else { 14 | m_score = 0; 15 | 16 | m_surface = paddle_image; 17 | m_rect.x = x; 18 | m_rect.y = y; 19 | m_rect.w = paddle_characteristics.width; 20 | m_rect.h = paddle_characteristics.height; 21 | 22 | m_window = window; 23 | 24 | m_texture = SDL_CreateTextureFromSurface(m_window->get_renderer(), paddle_image); 25 | 26 | if (m_texture == nullptr) { 27 | Debug::log_err("failed to create paddle texture!", SDL_GetError()); 28 | } 29 | 30 | SDL_FreeSurface(paddle_image); 31 | } 32 | } 33 | 34 | Paddle::~Paddle() 35 | { 36 | if (m_texture != nullptr) { 37 | SDL_DestroyTexture(m_texture); 38 | } 39 | } 40 | 41 | void Paddle::render() 42 | { 43 | SDL_RenderCopy(m_window->get_renderer(), m_texture, nullptr, &m_rect); 44 | } 45 | 46 | void Paddle::move_up() 47 | { 48 | /* The minus sign it's because the top left corner of the screen is like 49 | * (0,0), so we have to subtract the value of the paddleRect->y by the 50 | * y velocity. 51 | * The if statement there is because due the bug of the paddle going out of 52 | * the screen, I'm limiting the up movement of the paddle by doing the 53 | * following statement. 54 | */ 55 | if (m_rect.y > 0) { 56 | m_rect.y -= paddle_characteristics.velocity_y; 57 | } 58 | } 59 | 60 | void Paddle::move_down() 61 | { 62 | /* Same thing here, but the bottom left corner of the screen is like (0, 1) 63 | * for (x, y) values, so we have to add (...). 64 | * The if statement is the same Debug::logic used on moveUp(); but here has a POG. 65 | */ 66 | if (m_rect.y + m_rect.h < m_window->get_height()) { 67 | m_rect.y += paddle_characteristics.velocity_y; 68 | } 69 | } 70 | 71 | SDL_Rect* Paddle::get_rect() 72 | { 73 | return &m_rect; 74 | } 75 | 76 | double Paddle::velocity() 77 | { 78 | return paddle_characteristics.velocity; 79 | } 80 | 81 | void Paddle::velocity(const double v) 82 | { 83 | paddle_characteristics.velocity = v; 84 | } 85 | 86 | int Paddle::score() 87 | { 88 | return m_score; 89 | } 90 | 91 | void Paddle::reset_score() 92 | { 93 | m_score = 0; 94 | } 95 | 96 | void Paddle::add_score() 97 | { 98 | if (Game::audio->is_open()) { 99 | Game::audio->play_effect(Audio::EffectType::score_up); 100 | } 101 | m_score++; 102 | } 103 | 104 | int Paddle::get_hits() 105 | { 106 | return m_hits; 107 | } 108 | 109 | void Paddle::add_hit() 110 | { 111 | m_hits += 1; 112 | } 113 | 114 | void Paddle::reset_hit_count() 115 | { 116 | m_hits = 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/game.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "graphics/window.hpp" 11 | #include "graphics/font.hpp" 12 | #include "graphics/texture.hpp" 13 | #include "graphics/image_manager.hpp" 14 | 15 | #include "ball.hpp" 16 | #include "paddle.hpp" 17 | #include "audio.hpp" 18 | 19 | /** 20 | * @brief The game class represent the game itself, it wrappers all the other 21 | * entities and intializes them in order to make the Pong work. 22 | */ 23 | class Game 24 | { 25 | private: 26 | struct players_score 27 | { 28 | // Fonts used to render the score. 29 | std::shared_ptr font_player1; 30 | std::shared_ptr font_player2; 31 | // The rect of each font. 32 | SDL_Rect font1_rect; 33 | SDL_Rect font2_rect; 34 | } players_score; 35 | 36 | /** 37 | * @brief Storoes information that will be displayed on debug mode 38 | */ 39 | struct debug_info { 40 | std::shared_ptr font_debug; 41 | SDL_Rect font_debug_rect; 42 | } debug_info; 43 | 44 | SDL_Event event; 45 | 46 | std::uint32_t current_time; 47 | 48 | std::shared_ptr m_pause_texture; 49 | std::shared_ptr m_pause_backgroud_texture; 50 | 51 | std::uint32_t m_target_fps; 52 | std::uint32_t m_current_fps; 53 | 54 | bool m_is_running; 55 | bool m_pause; 56 | 57 | std::shared_ptr player1; 58 | std::shared_ptr player2; 59 | 60 | std::shared_ptr ball; 61 | 62 | std::shared_ptr window; 63 | std::shared_ptr image; 64 | 65 | public: 66 | // The variable which describes if we are in the debug mode. 67 | bool debug_mode; 68 | 69 | static std::shared_ptr