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