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
15std::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////////////////////////////////////////////////////////////
30int 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