├── .gitignore ├── Boilerplate ├── CMakeLists.txt ├── config.h.in ├── license.md └── main.cpp ├── Pong ├── CMakeLists.txt ├── content │ ├── background.png │ ├── bip.wav │ └── sansation.ttf ├── include │ ├── Ball.h │ ├── Bat.h │ └── config.h.in ├── license.md └── src │ ├── Ball.cpp │ ├── Bat.cpp │ └── main.cpp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | build 3 | compile_commands.json 4 | information.txt 5 | 6 | # OS 7 | .DS_Store 8 | Thumbs.db 9 | 10 | # Misc 11 | .cache 12 | -------------------------------------------------------------------------------- /Boilerplate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12 FATAL_ERROR) 2 | 3 | # Set project name and version 4 | project(SFMLBoilerplate VERSION 0.1) 5 | 6 | # Set C++ standard 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 10 | 11 | # Enable debug symbols by default 12 | if(NOT CMAKE_BUILD_TYPE) 13 | set(CMAKE_BUILD_TYPE Debug 14 | CACHE STRING 15 | "Choose the type of build (Debug or Release)" 16 | FORCE 17 | ) 18 | endif() 19 | 20 | # Generate config.h 21 | configure_file(config.h.in config.h) 22 | 23 | # Find SFML shared libraries 24 | find_package(SFML 2.5 25 | COMPONENTS 26 | system window graphics network audio REQUIRED 27 | ) 28 | 29 | # Compile executable 30 | add_executable(SFMLBoilerplate main.cpp) 31 | 32 | # Set include directory search paths 33 | target_include_directories(SFMLBoilerplate 34 | PRIVATE 35 | "${PROJECT_BINARY_DIR}" 36 | ) 37 | 38 | # Link executable to required SFML libraries 39 | target_link_libraries(SFMLBoilerplate sfml-graphics) 40 | 41 | # Install target 42 | install(TARGETS SFMLBoilerplate DESTINATION bin) 43 | 44 | -------------------------------------------------------------------------------- /Boilerplate/config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PROJECT_VERSION_MAJOR @SFMLBoilerplate_VERSION_MAJOR@ 3 | #define PROJECT_VERSION_MINOR @SFMLBoilerplate_VERSION_MINOR@ 4 | 5 | -------------------------------------------------------------------------------- /Boilerplate/license.md: -------------------------------------------------------------------------------- 1 | License goes here... 2 | -------------------------------------------------------------------------------- /Boilerplate/main.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | 8 | // Output project version 9 | std::cout << "Version: " << PROJECT_VERSION_MAJOR << "." 10 | << PROJECT_VERSION_MINOR << std::endl; 11 | 12 | // Create a window 13 | sf::Window window(sf::VideoMode(800, 600), "SFML Boilerplate"); 14 | 15 | // Handle closing the window 16 | while(window.isOpen()) { 17 | sf::Event Event; 18 | while(window.pollEvent(Event)) { 19 | if (Event.type == sf::Event::Closed) 20 | window.close(); 21 | if (Event.type == sf::Event::KeyPressed) { 22 | if (Event.key.code == sf::Keyboard::Escape) 23 | window.close(); 24 | } 25 | } 26 | window.display(); 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /Pong/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12 FATAL_ERROR) 2 | 3 | # Set project name and version 4 | project(Pong VERSION 0.1) 5 | 6 | # Set C++ standard 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 10 | 11 | ## If you want to link SFML statically 12 | # set(SFML_STATIC_LIBRARIES TRUE) 13 | 14 | # Enable debug symbols by default 15 | if(NOT CMAKE_BUILD_TYPE) 16 | set(CMAKE_BUILD_TYPE Debug 17 | CACHE STRING 18 | "Choose the type of build (Debug or Release)" FORCE) 19 | endif() 20 | 21 | # Export compile commands for completion engines (optional) 22 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 23 | 24 | # Set option to control setting the resource path variable 25 | option(USE_INSTALL_RESOURCE_PATH "Set resource path to install location" OFF) 26 | 27 | if(NOT USE_INSTALL_RESOURCE_PATH) 28 | set(RESOURCE_PATH "${PROJECT_SOURCE_DIR}/content/") 29 | else() 30 | set(RESOURCE_PATH "${CMAKE_INSTALL_PREFIX}/share/${CMAKE_PROJECT_NAME}/content/") 31 | endif() 32 | 33 | # Generate config.h 34 | configure_file(include/config.h.in config.h) 35 | 36 | # Find SFML shared libraries 37 | find_package(SFML 2.5 COMPONENTS system window graphics audio REQUIRED) 38 | 39 | # Compile executable 40 | add_executable(Pong src/main.cpp src/Ball.cpp src/Bat.cpp) 41 | 42 | # Set include directory search paths 43 | target_include_directories(Pong 44 | PRIVATE 45 | "${PROJECT_BINARY_DIR}" 46 | "${CMAKE_CURRENT_SOURCE_DIR}/include" 47 | ) 48 | 49 | # Set executable RPATH 50 | set_target_properties(Pong 51 | PROPERTIES 52 | INSTALL_RPATH 53 | "${CMAKE_INSTALL_PREFIX}/lib" 54 | ) 55 | 56 | # Link executable to required SFML modules 57 | target_link_libraries(Pong sfml-graphics sfml-audio) 58 | 59 | # Set executable install location 60 | install(TARGETS Pong DESTINATION bin) 61 | 62 | # Set install location for resources 63 | install(DIRECTORY content 64 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/${CMAKE_PROJECT_NAME}" 65 | ) 66 | 67 | -------------------------------------------------------------------------------- /Pong/content/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danebulat/cmake-sfml-demo/2721d7eff957cb61531f5b3a09ffd4dd2a413062/Pong/content/background.png -------------------------------------------------------------------------------- /Pong/content/bip.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danebulat/cmake-sfml-demo/2721d7eff957cb61531f5b3a09ffd4dd2a413062/Pong/content/bip.wav -------------------------------------------------------------------------------- /Pong/content/sansation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danebulat/cmake-sfml-demo/2721d7eff957cb61531f5b3a09ffd4dd2a413062/Pong/content/sansation.ttf -------------------------------------------------------------------------------- /Pong/include/Ball.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Ball { 5 | private: 6 | sf::Vector2f m_Position; // X and Y position 7 | sf::RectangleShape m_Shape; // Sprite 8 | 9 | float m_Speed; // Pixels per second 10 | float m_DirectionX; // Horizontal velocity 11 | float m_DirectionY; // Vertical velocity 12 | 13 | static constexpr float WIDTH{ 10.f }; 14 | 15 | public: 16 | Ball(const float startX, const float startY); 17 | ~Ball(); 18 | 19 | /* Accessors */ 20 | sf::FloatRect getPosition() const; 21 | sf::RectangleShape getShape() const; 22 | float getVelocityY() const; 23 | 24 | void setPositionX(const float xPos); 25 | void setPositionY(const float yPos); 26 | 27 | /* Collision */ 28 | void reboundSides(); // Side of screen collision 29 | void reboundBatOrTop(); // Bat or top of screen collision 30 | void reboundBottom(); // Bottom of screen collision 31 | 32 | /* Update */ 33 | void update(sf::Time& dt); 34 | }; 35 | -------------------------------------------------------------------------------- /Pong/include/Bat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class Bat { 6 | private: 7 | sf::Vector2f m_Position; // Bat x and y position 8 | sf::RectangleShape m_Shape; // The bounding box sprite 9 | 10 | float m_Speed; // Movement in pixels per second 11 | bool m_MovingRight; 12 | bool m_MovingLeft; 13 | 14 | static const unsigned int WIDTH{ 250 }; 15 | static const unsigned int HEIGHT{ 5 }; 16 | 17 | public: 18 | Bat(const float startX, const float startY); 19 | ~Bat(); 20 | 21 | sf::FloatRect getPosition() const; 22 | sf::RectangleShape getShape() const; 23 | 24 | void moveLeft(); 25 | void moveRight(); 26 | void stopLeft(); 27 | void stopRight(); 28 | void update(sf::Time& dt); 29 | }; 30 | -------------------------------------------------------------------------------- /Pong/include/config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PROJECT_VERSION_MAJOR @SFMLProject_VERSION_MAJOR@ 3 | #define PROJECT_VERSION_MINOR @SFMLProject_VERSION_MINOR@ 4 | #define RESOURCE_PATH "@RESOURCE_PATH@" 5 | 6 | -------------------------------------------------------------------------------- /Pong/license.md: -------------------------------------------------------------------------------- 1 | License goes here... 2 | -------------------------------------------------------------------------------- /Pong/src/Ball.cpp: -------------------------------------------------------------------------------- 1 | #include "Ball.h" 2 | 3 | using sf::Vector2f; 4 | using sf::RectangleShape; 5 | using sf::FloatRect; 6 | using sf::Time; 7 | using sf::Color; 8 | 9 | /* Constructor */ 10 | Ball::Ball(const float startX, const float startY) 11 | : m_Position{startX, startY} 12 | , m_Speed{ 300.f } 13 | , m_DirectionX{ 1.5f } 14 | , m_DirectionY{ 1.5f } 15 | { 16 | m_Shape.setSize(Vector2f(WIDTH, WIDTH)); 17 | m_Shape.setPosition(m_Position); 18 | m_Shape.setFillColor(Color::White); 19 | } 20 | 21 | /* Destructor */ 22 | Ball::~Ball() 23 | {} 24 | 25 | /* Accessors */ 26 | FloatRect Ball::getPosition() const { 27 | return m_Shape.getGlobalBounds(); 28 | } 29 | 30 | RectangleShape Ball::getShape() const { 31 | return m_Shape; 32 | } 33 | 34 | float Ball::getVelocityY() const { 35 | return m_DirectionX; 36 | } 37 | 38 | void Ball::setPositionX(const float xPos) { 39 | m_Position.x = xPos; 40 | } 41 | 42 | void Ball::setPositionY(const float yPos) { 43 | m_Position.y = yPos; 44 | } 45 | 46 | /* Collision */ 47 | void Ball::reboundSides() { 48 | m_DirectionX = -m_DirectionX; 49 | } 50 | 51 | void Ball::reboundBatOrTop() { 52 | m_DirectionY = -m_DirectionY; 53 | } 54 | 55 | void Ball::reboundBottom() { 56 | // Position on top of screen 57 | m_Position.y = 0; 58 | m_Position.x = 500; 59 | } 60 | 61 | /* Update */ 62 | void Ball::update(Time& dt) { 63 | // Update the ball's position 64 | m_Position.x += m_DirectionX * m_Speed * dt.asSeconds(); 65 | m_Position.y += m_DirectionY * m_Speed * dt.asSeconds(); 66 | 67 | // Move the ball 68 | m_Shape.setPosition(m_Position); 69 | } 70 | -------------------------------------------------------------------------------- /Pong/src/Bat.cpp: -------------------------------------------------------------------------------- 1 | #include "Bat.h" 2 | #include 3 | 4 | using sf::Vector2f; 5 | using sf::RectangleShape; 6 | using sf::Time; 7 | using sf::Color; 8 | using sf::FloatRect; 9 | 10 | /* Constructor */ 11 | Bat::Bat(const float startX, const float startY) 12 | : m_Position{ startX, startY } 13 | , m_Speed{ 1000.f } 14 | , m_MovingLeft{ false } 15 | , m_MovingRight{ false } 16 | { 17 | m_Shape.setSize(Vector2f(WIDTH, HEIGHT)); 18 | m_Shape.setPosition(m_Position); 19 | m_Shape.setFillColor(Color::White); 20 | } 21 | 22 | /* Destructor */ 23 | Bat::~Bat() 24 | {} 25 | 26 | /* Accessors */ 27 | FloatRect Bat::getPosition() const { 28 | return m_Shape.getGlobalBounds(); 29 | } 30 | 31 | RectangleShape Bat::getShape() const { 32 | return m_Shape; 33 | } 34 | 35 | /* Behavior */ 36 | 37 | void Bat::moveLeft() { 38 | m_MovingLeft = true; 39 | } 40 | 41 | void Bat::moveRight() { 42 | m_MovingRight = true; 43 | } 44 | 45 | void Bat::stopLeft() { 46 | m_MovingLeft = false; 47 | } 48 | 49 | void Bat::stopRight() { 50 | m_MovingRight = false; 51 | } 52 | 53 | /* Update */ 54 | void Bat::update(Time& dt) { 55 | if (m_MovingLeft) { 56 | m_Position.x -= m_Speed * dt.asSeconds(); 57 | } 58 | if (m_MovingRight) { 59 | m_Position.x += m_Speed * dt.asSeconds(); 60 | } 61 | m_Shape.setPosition(m_Position); 62 | } 63 | -------------------------------------------------------------------------------- /Pong/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "Bat.h" 3 | #include "Ball.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | // Create a video mode object 14 | sf::VideoMode vm(800.f, 600.f); 15 | 16 | // Create and open a window 17 | sf::RenderWindow window(vm, "Pong", sf::Style::Default); 18 | sf::View view(sf::FloatRect(0, 0, 800.f, 600.f)); 19 | window.setView(view); 20 | 21 | int score{ 0 }; 22 | int lives{ 3 }; 23 | 24 | // Create a bat at the bottom center of the screen 25 | Bat bat(400.f, 600.f-20.f); 26 | 27 | // Create a ball 28 | Ball ball(400.f, 0.f); 29 | 30 | // Load font 31 | sf::Font font; 32 | std::string source_dir = RESOURCE_PATH; 33 | 34 | #ifndef NDEBUG 35 | // Output RESOURCE_PATH 36 | std::cout << "RESOURCE_PATH: " << source_dir << std::endl; 37 | #endif 38 | 39 | if (!font.loadFromFile(source_dir + "sansation.ttf")) 40 | return EXIT_FAILURE; 41 | 42 | // Load texture 43 | sf::Texture texture; 44 | if (!texture.loadFromFile(source_dir + "background.png")) 45 | return EXIT_FAILURE; 46 | 47 | sf::Sprite sprite; 48 | sprite.setTexture(texture); 49 | sprite.setPosition(0.f, 0.f); 50 | unsigned int c = 212; 51 | sprite.setColor(sf::Color(c,c,c,255)); 52 | 53 | // Load sound 54 | sf::SoundBuffer buffer; 55 | if (!buffer.loadFromFile(source_dir + "bip.wav")) 56 | return EXIT_FAILURE; 57 | 58 | sf::Sound sound; 59 | sound.setBuffer(buffer); 60 | 61 | // Create a text object 62 | sf::Text hud; 63 | hud.setFont(font); 64 | hud.setCharacterSize(24); 65 | hud.setFillColor(sf::Color::White); 66 | hud.setPosition(10.f, 10.f); 67 | 68 | // Clock for timing everything 69 | sf::Clock clock; 70 | 71 | // Flag for updating the score 72 | bool updateScore{ false }; 73 | 74 | while (window.isOpen()) 75 | { 76 | /* Handle the player input 77 | *****************************************/ 78 | sf::Event event; 79 | while (window.pollEvent(event)) 80 | { 81 | if (event.type == sf::Event::Closed) { 82 | window.close(); 83 | } 84 | 85 | if (event.type == sf::Event::KeyPressed) 86 | { 87 | // Escape 88 | if (event.key.code == sf::Keyboard::Escape) 89 | window.close(); 90 | 91 | // Left arrow 92 | if (event.key.code == sf::Keyboard::Left) 93 | bat.moveLeft(); 94 | 95 | // Right arrow 96 | if (event.key.code == sf::Keyboard::Right) 97 | bat.moveRight(); 98 | } 99 | 100 | if (event.type == sf::Event::KeyReleased) 101 | { 102 | // Left arrow 103 | if (event.key.code == sf::Keyboard::Left) 104 | bat.stopLeft(); 105 | 106 | // Right arrow 107 | if (event.key.code == sf::Keyboard::Right) 108 | bat.stopRight(); 109 | } 110 | } 111 | 112 | /* 113 | * Update 114 | *****************************************/ 115 | // Update delta time 116 | sf::Time dt = clock.restart(); 117 | 118 | bat.update(dt); 119 | ball.update(dt); 120 | 121 | // Handle ball hiting the bottom 122 | if (ball.getPosition().top > view.getSize().y) 123 | { 124 | ball.reboundBottom(); // Reverse ball Y velocity 125 | --lives; // Remove a life 126 | 127 | if (lives < 1) { // Check for zero lives 128 | score = 0; // Reset the score 129 | lives = 3; // Reest the lives 130 | } 131 | } 132 | 133 | // Handle ball hitting top 134 | if (ball.getPosition().top < 0.f) { 135 | sound.play(); 136 | ball.setPositionY(0.f); 137 | ball.reboundBatOrTop(); // Reverse ball Y velocity 138 | updateScore = true; // Set flag to increment score 139 | } 140 | 141 | // Handle ball hitting sides 142 | if (ball.getPosition().left < 0) { 143 | sound.play(); 144 | ball.setPositionX(0); 145 | ball.reboundSides(); // Reverse ball X velocity 146 | } 147 | 148 | if (ball.getPosition().left + ball.getPosition().width > view.getSize().x) { 149 | sound.play(); 150 | ball.setPositionX(view.getSize().x - ball.getPosition().width); 151 | ball.reboundSides(); 152 | } 153 | 154 | // Has the ball hit the bat? 155 | if (ball.getPosition().intersects(bat.getPosition())) { 156 | sound.play(); 157 | ball.setPositionY(bat.getPosition().top - ball.getPosition().height); 158 | ball.reboundBatOrTop(); // Reverse ball Y velocity 159 | updateScore = true; 160 | } 161 | 162 | // Increment score 163 | if (updateScore) { 164 | ++score; 165 | updateScore = false; 166 | } 167 | 168 | // Update HUD 169 | std::stringstream ss; 170 | ss << "Score: " << score << " Lives: " << lives; 171 | hud.setString(ss.str()); 172 | 173 | /* 174 | * Draw 175 | ****************************************/ 176 | window.clear(); 177 | window.draw(sprite); 178 | window.draw(hud); 179 | window.draw(bat.getShape()); 180 | window.draw(ball.getShape()); 181 | window.display(); 182 | } 183 | 184 | return EXIT_SUCCESS; 185 | } 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmake-sfml-demo 2 | 3 | The companion repository for the [CMake: Building SFML and Game Projects on Linux](https://dane-bulat.medium.com/cmake-building-sfml-and-game-projects-on-linux-3947b3ba6e8) article. 4 | 5 | --------------------------------------------------------------------------------