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 | |