| 1 | |
| 2 | //////////////////////////////////////////////////////////// |
| 3 | // Headers |
| 4 | //////////////////////////////////////////////////////////// |
| 5 | #include <SFML/Graphics.hpp> |
| 6 | #include <SFML/Audio.hpp> |
| 7 | #include <cmath> |
| 8 | #include <ctime> |
| 9 | #include <cstdlib> |
| 10 | |
| 11 | #ifdef SFML_SYSTEM_IOS |
| 12 | #include <SFML/Main.hpp> |
| 13 | #endif |
| 14 | |
| 15 | std::string resourcesDir() |
| 16 | { |
| 17 | #ifdef SFML_SYSTEM_IOS |
| 18 | return "" ; |
| 19 | #else |
| 20 | return "resources/" ; |
| 21 | #endif |
| 22 | } |
| 23 | |
| 24 | //////////////////////////////////////////////////////////// |
| 25 | /// Entry point of application |
| 26 | /// |
| 27 | /// \return Application exit code |
| 28 | /// |
| 29 | //////////////////////////////////////////////////////////// |
| 30 | int main() |
| 31 | { |
| 32 | std::srand(static_cast<unsigned int>(std::time(NULL))); |
| 33 | |
| 34 | // Define some constants |
| 35 | const float pi = 3.14159f; |
| 36 | const int gameWidth = 800; |
| 37 | const int gameHeight = 600; |
| 38 | sf::Vector2f paddleSize(25, 100); |
| 39 | float ballRadius = 10.f; |
| 40 | |
| 41 | // Create the window of the application |
| 42 | sf::RenderWindow window(sf::VideoMode(gameWidth, gameHeight, 32), "SFML Pong" , |
| 43 | sf::Style::Titlebar | sf::Style::Close); |
| 44 | window.setVerticalSyncEnabled(true); |
| 45 | |
| 46 | // Load the sounds used in the game |
| 47 | sf::SoundBuffer ballSoundBuffer; |
| 48 | if (!ballSoundBuffer.loadFromFile(resourcesDir() + "ball.wav" )) |
| 49 | return EXIT_FAILURE; |
| 50 | sf::Sound ballSound(ballSoundBuffer); |
| 51 | |
| 52 | // Create the left paddle |
| 53 | sf::RectangleShape leftPaddle; |
| 54 | leftPaddle.setSize(paddleSize - sf::Vector2f(3, 3)); |
| 55 | leftPaddle.setOutlineThickness(3); |
| 56 | leftPaddle.setOutlineColor(sf::Color::Black); |
| 57 | leftPaddle.setFillColor(sf::Color(100, 100, 200)); |
| 58 | leftPaddle.setOrigin(paddleSize / 2.f); |
| 59 | |
| 60 | // Create the right paddle |
| 61 | sf::RectangleShape rightPaddle; |
| 62 | rightPaddle.setSize(paddleSize - sf::Vector2f(3, 3)); |
| 63 | rightPaddle.setOutlineThickness(3); |
| 64 | rightPaddle.setOutlineColor(sf::Color::Black); |
| 65 | rightPaddle.setFillColor(sf::Color(200, 100, 100)); |
| 66 | rightPaddle.setOrigin(paddleSize / 2.f); |
| 67 | |
| 68 | // Create the ball |
| 69 | sf::CircleShape ball; |
| 70 | ball.setRadius(ballRadius - 3); |
| 71 | ball.setOutlineThickness(3); |
| 72 | ball.setOutlineColor(sf::Color::Black); |
| 73 | ball.setFillColor(sf::Color::White); |
| 74 | ball.setOrigin(ballRadius / 2, ballRadius / 2); |
| 75 | |
| 76 | // Load the text font |
| 77 | sf::Font font; |
| 78 | if (!font.loadFromFile(resourcesDir() + "sansation.ttf" )) |
| 79 | return EXIT_FAILURE; |
| 80 | |
| 81 | // Initialize the pause message |
| 82 | sf::Text pauseMessage; |
| 83 | pauseMessage.setFont(font); |
| 84 | pauseMessage.setCharacterSize(40); |
| 85 | pauseMessage.setPosition(170.f, 150.f); |
| 86 | pauseMessage.setFillColor(sf::Color::White); |
| 87 | |
| 88 | #ifdef SFML_SYSTEM_IOS |
| 89 | pauseMessage.setString("Welcome to SFML pong!\nTouch the screen to start the game" ); |
| 90 | #else |
| 91 | pauseMessage.setString("Welcome to SFML pong!\nPress space to start the game" ); |
| 92 | #endif |
| 93 | |
| 94 | // Define the paddles properties |
| 95 | sf::Clock AITimer; |
| 96 | const sf::Time AITime = sf::seconds(0.1f); |
| 97 | const float paddleSpeed = 400.f; |
| 98 | float rightPaddleSpeed = 0.f; |
| 99 | const float ballSpeed = 400.f; |
| 100 | float ballAngle = 0.f; // to be changed later |
| 101 | |
| 102 | sf::Clock clock; |
| 103 | bool isPlaying = false; |
| 104 | while (window.isOpen()) |
| 105 | { |
| 106 | // Handle events |
| 107 | sf::Event event; |
| 108 | while (window.pollEvent(event)) |
| 109 | { |
| 110 | // Window closed or escape key pressed: exit |
| 111 | if ((event.type == sf::Event::Closed) || |
| 112 | ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))) |
| 113 | { |
| 114 | window.close(); |
| 115 | break; |
| 116 | } |
| 117 | |
| 118 | // Space key pressed: play |
| 119 | if (((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Space)) || |
| 120 | (event.type == sf::Event::TouchBegan)) |
| 121 | { |
| 122 | if (!isPlaying) |
| 123 | { |
| 124 | // (re)start the game |
| 125 | isPlaying = true; |
| 126 | clock.restart(); |
| 127 | |
| 128 | // Reset the position of the paddles and ball |
| 129 | leftPaddle.setPosition(10 + paddleSize.x / 2, gameHeight / 2); |
| 130 | rightPaddle.setPosition(gameWidth - 10 - paddleSize.x / 2, gameHeight / 2); |
| 131 | ball.setPosition(gameWidth / 2, gameHeight / 2); |
| 132 | |
| 133 | // Reset the ball angle |
| 134 | do |
| 135 | { |
| 136 | // Make sure the ball initial angle is not too much vertical |
| 137 | ballAngle = (std::rand() % 360) * 2 * pi / 360; |
| 138 | } |
| 139 | while (std::abs(std::cos(ballAngle)) < 0.7f); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | // Window size changed, adjust view appropriately |
| 144 | if (event.type == sf::Event::Resized) |
| 145 | { |
| 146 | sf::View view; |
| 147 | view.setSize(gameWidth, gameHeight); |
| 148 | view.setCenter(gameWidth/2.f, gameHeight/2.f); |
| 149 | window.setView(view); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | if (isPlaying) |
| 154 | { |
| 155 | float deltaTime = clock.restart().asSeconds(); |
| 156 | |
| 157 | // Move the player's paddle |
| 158 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) && |
| 159 | (leftPaddle.getPosition().y - paddleSize.y / 2 > 5.f)) |
| 160 | { |
| 161 | leftPaddle.move(0.f, -paddleSpeed * deltaTime); |
| 162 | } |
| 163 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) && |
| 164 | (leftPaddle.getPosition().y + paddleSize.y / 2 < gameHeight - 5.f)) |
| 165 | { |
| 166 | leftPaddle.move(0.f, paddleSpeed * deltaTime); |
| 167 | } |
| 168 | |
| 169 | if (sf::Touch::isDown(0)) |
| 170 | { |
| 171 | sf::Vector2i pos = sf::Touch::getPosition(0); |
| 172 | sf::Vector2f mappedPos = window.mapPixelToCoords(pos); |
| 173 | leftPaddle.setPosition(leftPaddle.getPosition().x, mappedPos.y); |
| 174 | } |
| 175 | |
| 176 | // Move the computer's paddle |
| 177 | if (((rightPaddleSpeed < 0.f) && (rightPaddle.getPosition().y - paddleSize.y / 2 > 5.f)) || |
| 178 | ((rightPaddleSpeed > 0.f) && (rightPaddle.getPosition().y + paddleSize.y / 2 < gameHeight - 5.f))) |
| 179 | { |
| 180 | rightPaddle.move(0.f, rightPaddleSpeed * deltaTime); |
| 181 | } |
| 182 | |
| 183 | // Update the computer's paddle direction according to the ball position |
| 184 | if (AITimer.getElapsedTime() > AITime) |
| 185 | { |
| 186 | AITimer.restart(); |
| 187 | if (ball.getPosition().y + ballRadius > rightPaddle.getPosition().y + paddleSize.y / 2) |
| 188 | rightPaddleSpeed = paddleSpeed; |
| 189 | else if (ball.getPosition().y - ballRadius < rightPaddle.getPosition().y - paddleSize.y / 2) |
| 190 | rightPaddleSpeed = -paddleSpeed; |
| 191 | else |
| 192 | rightPaddleSpeed = 0.f; |
| 193 | } |
| 194 | |
| 195 | // Move the ball |
| 196 | float factor = ballSpeed * deltaTime; |
| 197 | ball.move(std::cos(ballAngle) * factor, std::sin(ballAngle) * factor); |
| 198 | |
| 199 | #ifdef SFML_SYSTEM_IOS |
| 200 | const std::string inputString = "Touch the screen to restart" ; |
| 201 | #else |
| 202 | const std::string inputString = "Press space to restart or\nescape to exit" ; |
| 203 | #endif |
| 204 | |
| 205 | // Check collisions between the ball and the screen |
| 206 | if (ball.getPosition().x - ballRadius < 0.f) |
| 207 | { |
| 208 | isPlaying = false; |
| 209 | pauseMessage.setString("You Lost!\n" + inputString); |
| 210 | } |
| 211 | if (ball.getPosition().x + ballRadius > gameWidth) |
| 212 | { |
| 213 | isPlaying = false; |
| 214 | pauseMessage.setString("You Won!\n" + inputString); |
| 215 | } |
| 216 | if (ball.getPosition().y - ballRadius < 0.f) |
| 217 | { |
| 218 | ballSound.play(); |
| 219 | ballAngle = -ballAngle; |
| 220 | ball.setPosition(ball.getPosition().x, ballRadius + 0.1f); |
| 221 | } |
| 222 | if (ball.getPosition().y + ballRadius > gameHeight) |
| 223 | { |
| 224 | ballSound.play(); |
| 225 | ballAngle = -ballAngle; |
| 226 | ball.setPosition(ball.getPosition().x, gameHeight - ballRadius - 0.1f); |
| 227 | } |
| 228 | |
| 229 | // Check the collisions between the ball and the paddles |
| 230 | // Left Paddle |
| 231 | if (ball.getPosition().x - ballRadius < leftPaddle.getPosition().x + paddleSize.x / 2 && |
| 232 | ball.getPosition().x - ballRadius > leftPaddle.getPosition().x && |
| 233 | ball.getPosition().y + ballRadius >= leftPaddle.getPosition().y - paddleSize.y / 2 && |
| 234 | ball.getPosition().y - ballRadius <= leftPaddle.getPosition().y + paddleSize.y / 2) |
| 235 | { |
| 236 | if (ball.getPosition().y > leftPaddle.getPosition().y) |
| 237 | ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180; |
| 238 | else |
| 239 | ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180; |
| 240 | |
| 241 | ballSound.play(); |
| 242 | ball.setPosition(leftPaddle.getPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.getPosition().y); |
| 243 | } |
| 244 | |
| 245 | // Right Paddle |
| 246 | if (ball.getPosition().x + ballRadius > rightPaddle.getPosition().x - paddleSize.x / 2 && |
| 247 | ball.getPosition().x + ballRadius < rightPaddle.getPosition().x && |
| 248 | ball.getPosition().y + ballRadius >= rightPaddle.getPosition().y - paddleSize.y / 2 && |
| 249 | ball.getPosition().y - ballRadius <= rightPaddle.getPosition().y + paddleSize.y / 2) |
| 250 | { |
| 251 | if (ball.getPosition().y > rightPaddle.getPosition().y) |
| 252 | ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180; |
| 253 | else |
| 254 | ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180; |
| 255 | |
| 256 | ballSound.play(); |
| 257 | ball.setPosition(rightPaddle.getPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.getPosition().y); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | // Clear the window |
| 262 | window.clear(sf::Color(50, 200, 50)); |
| 263 | |
| 264 | if (isPlaying) |
| 265 | { |
| 266 | // Draw the paddles and the ball |
| 267 | window.draw(leftPaddle); |
| 268 | window.draw(rightPaddle); |
| 269 | window.draw(ball); |
| 270 | } |
| 271 | else |
| 272 | { |
| 273 | // Draw the pause message |
| 274 | window.draw(pauseMessage); |
| 275 | } |
| 276 | |
| 277 | // Display things on screen |
| 278 | window.display(); |
| 279 | } |
| 280 | |
| 281 | return EXIT_SUCCESS; |
| 282 | } |
| 283 | |