SFML/examples/tennis/Tennis.cpp
2022-02-17 00:40:29 +00:00

291 lines
11 KiB
C++

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <cmath>
#include <ctime>
#include <cstdlib>
#ifdef SFML_SYSTEM_IOS
#include <SFML/Main.hpp>
#endif
std::filesystem::path resourcesDir()
{
#ifdef SFML_SYSTEM_IOS
return "";
#else
return "resources/";
#endif
}
////////////////////////////////////////////////////////////
/// Entry point of application
///
/// \return Application exit code
///
////////////////////////////////////////////////////////////
int main()
{
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Define some constants
const float gameWidth = 800;
const float gameHeight = 600;
sf::Vector2f paddleSize(25, 100);
float ballRadius = 10.f;
// Create the window of the application
sf::RenderWindow window(sf::VideoMode(static_cast<unsigned int>(gameWidth), static_cast<unsigned int>(gameHeight), 32), "SFML Tennis",
sf::Style::Titlebar | sf::Style::Close);
window.setVerticalSyncEnabled(true);
// Load the sounds used in the game
sf::SoundBuffer ballSoundBuffer;
if (!ballSoundBuffer.loadFromFile(resourcesDir() / "ball.wav"))
return EXIT_FAILURE;
sf::Sound ballSound(ballSoundBuffer);
// Create the SFML logo texture:
sf::Texture sfmlLogoTexture;
if(!sfmlLogoTexture.loadFromFile(resourcesDir() / "sfml_logo.png"))
return EXIT_FAILURE;
sf::Sprite sfmlLogo;
sfmlLogo.setTexture(sfmlLogoTexture);
sfmlLogo.setPosition({170.f, 50.f});
// Create the left paddle
sf::RectangleShape leftPaddle;
leftPaddle.setSize(paddleSize - sf::Vector2f(3, 3));
leftPaddle.setOutlineThickness(3);
leftPaddle.setOutlineColor(sf::Color::Black);
leftPaddle.setFillColor(sf::Color(100, 100, 200));
leftPaddle.setOrigin(paddleSize / 2.f);
// Create the right paddle
sf::RectangleShape rightPaddle;
rightPaddle.setSize(paddleSize - sf::Vector2f(3, 3));
rightPaddle.setOutlineThickness(3);
rightPaddle.setOutlineColor(sf::Color::Black);
rightPaddle.setFillColor(sf::Color(200, 100, 100));
rightPaddle.setOrigin(paddleSize / 2.f);
// Create the ball
sf::CircleShape ball;
ball.setRadius(ballRadius - 3);
ball.setOutlineThickness(2);
ball.setOutlineColor(sf::Color::Black);
ball.setFillColor(sf::Color::White);
ball.setOrigin({ballRadius / 2.f, ballRadius / 2.f});
// Load the text font
sf::Font font;
if (!font.loadFromFile(resourcesDir() / "tuffy.ttf"))
return EXIT_FAILURE;
// Initialize the pause message
sf::Text pauseMessage;
pauseMessage.setFont(font);
pauseMessage.setCharacterSize(40);
pauseMessage.setPosition({170.f, 200.f});
pauseMessage.setFillColor(sf::Color::White);
#ifdef SFML_SYSTEM_IOS
pauseMessage.setString("Welcome to SFML Tennis!\nTouch the screen to start the game.");
#else
pauseMessage.setString("Welcome to SFML Tennis!\n\nPress space to start the game.");
#endif
// Define the paddles properties
sf::Clock AITimer;
const sf::Time AITime = sf::seconds(0.1f);
const float paddleSpeed = 400.f;
float rightPaddleSpeed = 0.f;
const float ballSpeed = 400.f;
sf::Angle ballAngle = sf::degrees(0); // to be changed later
sf::Clock clock;
bool isPlaying = false;
while (window.isOpen())
{
// Handle events
sf::Event event;
while (window.pollEvent(event))
{
// Window closed or escape key pressed: exit
if ((event.type == sf::Event::Closed) ||
((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
{
window.close();
break;
}
// Space key pressed: play
if (((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Space)) ||
(event.type == sf::Event::TouchBegan))
{
if (!isPlaying)
{
// (re)start the game
isPlaying = true;
clock.restart();
// Reset the position of the paddles and ball
leftPaddle.setPosition({10.f + paddleSize.x / 2.f, gameHeight / 2.f});
rightPaddle.setPosition({gameWidth - 10.f - paddleSize.x / 2.f, gameHeight / 2.f});
ball.setPosition({gameWidth / 2.f, gameHeight / 2.f});
// Reset the ball angle
do
{
// Make sure the ball initial angle is not too much vertical
ballAngle = sf::degrees(static_cast<float>(std::rand() % 360));
}
while (std::abs(std::cos(ballAngle.asRadians())) < 0.7f);
}
}
// Window size changed, adjust view appropriately
if (event.type == sf::Event::Resized)
{
sf::View view;
view.setSize(gameWidth, gameHeight);
view.setCenter({gameWidth / 2.f, gameHeight / 2.f});
window.setView(view);
}
}
if (isPlaying)
{
float deltaTime = clock.restart().asSeconds();
// Move the player's paddle
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) &&
(leftPaddle.getPosition().y - paddleSize.y / 2 > 5.f))
{
leftPaddle.move({0.f, -paddleSpeed * deltaTime});
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) &&
(leftPaddle.getPosition().y + paddleSize.y / 2 < gameHeight - 5.f))
{
leftPaddle.move({0.f, paddleSpeed * deltaTime});
}
if (sf::Touch::isDown(0))
{
sf::Vector2i pos = sf::Touch::getPosition(0);
sf::Vector2f mappedPos = window.mapPixelToCoords(pos);
leftPaddle.setPosition({leftPaddle.getPosition().x, mappedPos.y});
}
// Move the computer's paddle
if (((rightPaddleSpeed < 0.f) && (rightPaddle.getPosition().y - paddleSize.y / 2 > 5.f)) ||
((rightPaddleSpeed > 0.f) && (rightPaddle.getPosition().y + paddleSize.y / 2 < gameHeight - 5.f)))
{
rightPaddle.move({0.f, rightPaddleSpeed * deltaTime});
}
// Update the computer's paddle direction according to the ball position
if (AITimer.getElapsedTime() > AITime)
{
AITimer.restart();
if (ball.getPosition().y + ballRadius > rightPaddle.getPosition().y + paddleSize.y / 2)
rightPaddleSpeed = paddleSpeed;
else if (ball.getPosition().y - ballRadius < rightPaddle.getPosition().y - paddleSize.y / 2)
rightPaddleSpeed = -paddleSpeed;
else
rightPaddleSpeed = 0.f;
}
// Move the ball
float factor = ballSpeed * deltaTime;
ball.move({std::cos(ballAngle.asRadians()) * factor, std::sin(ballAngle.asRadians()) * factor});
#ifdef SFML_SYSTEM_IOS
const std::string inputString = "Touch the screen to restart.";
#else
const std::string inputString = "Press space to restart or\nescape to exit.";
#endif
// Check collisions between the ball and the screen
if (ball.getPosition().x - ballRadius < 0.f)
{
isPlaying = false;
pauseMessage.setString("You Lost!\n\n" + inputString);
}
if (ball.getPosition().x + ballRadius > gameWidth)
{
isPlaying = false;
pauseMessage.setString("You Won!\n\n" + inputString);
}
if (ball.getPosition().y - ballRadius < 0.f)
{
ballSound.play();
ballAngle = -ballAngle;
ball.setPosition({ball.getPosition().x, ballRadius + 0.1f});
}
if (ball.getPosition().y + ballRadius > gameHeight)
{
ballSound.play();
ballAngle = -ballAngle;
ball.setPosition({ball.getPosition().x, gameHeight - ballRadius - 0.1f});
}
// Check the collisions between the ball and the paddles
// Left Paddle
if (ball.getPosition().x - ballRadius < leftPaddle.getPosition().x + paddleSize.x / 2 &&
ball.getPosition().x - ballRadius > leftPaddle.getPosition().x &&
ball.getPosition().y + ballRadius >= leftPaddle.getPosition().y - paddleSize.y / 2 &&
ball.getPosition().y - ballRadius <= leftPaddle.getPosition().y + paddleSize.y / 2)
{
if (ball.getPosition().y > leftPaddle.getPosition().y)
ballAngle = sf::degrees(180) - ballAngle + sf::degrees(static_cast<float>(std::rand() % 20));
else
ballAngle = sf::degrees(180) - ballAngle - sf::degrees(static_cast<float>(std::rand() % 20));
ballSound.play();
ball.setPosition({leftPaddle.getPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.getPosition().y});
}
// Right Paddle
if (ball.getPosition().x + ballRadius > rightPaddle.getPosition().x - paddleSize.x / 2 &&
ball.getPosition().x + ballRadius < rightPaddle.getPosition().x &&
ball.getPosition().y + ballRadius >= rightPaddle.getPosition().y - paddleSize.y / 2 &&
ball.getPosition().y - ballRadius <= rightPaddle.getPosition().y + paddleSize.y / 2)
{
if (ball.getPosition().y > rightPaddle.getPosition().y)
ballAngle = sf::degrees(180) - ballAngle + sf::degrees(static_cast<float>(std::rand() % 20));
else
ballAngle = sf::degrees(180) - ballAngle - sf::degrees(static_cast<float>(std::rand() % 20));
ballSound.play();
ball.setPosition({rightPaddle.getPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.getPosition().y});
}
}
// Clear the window
window.clear(sf::Color(50, 50, 50));
if (isPlaying)
{
// Draw the paddles and the ball
window.draw(leftPaddle);
window.draw(rightPaddle);
window.draw(ball);
}
else
{
// Draw the pause message
window.draw(pauseMessage);
window.draw(sfmlLogo);
}
// Display things on screen
window.display();
}
return EXIT_SUCCESS;
}