So, I wrote a simple Pong game in C++ with SFML and Visual Studio 2013 and was wondering if somebody could tell me if there is anything I could improve upon in the code.
Noteworthy:
- The collision between the ball and the top and bottom of the player panel seems to be slightly bugged
- The full solution for visual studio can be found here:
https://www.dropbox.com/s/558as04io6jr2b5/Pong.zip - To quit the game, press escape
Game:
Files:
Code: (main.cpp)
#include <iostream>
#include <string>
#include <vector>
#include <SFML\Graphics.hpp>
#include <SFML\Audio.hpp>
#include <Windows.h>
#pragma warning(disable: 4244)
#define SCRW 1000 // Screen width
#define SCRH 600 // Screen height
#define BALLW 20 // Ball width
#define BALLH 20 // Ball height
#define PADDLEW 20 // Paddle width
#define PADDLEH 100 // Paddle height
#define BSPEED -0.1f // Ball origin speed
#define ESPEED 0.08f // Enemy origin speed (player is mouse controlled)
sf::RenderWindow window(sf::VideoMode(SCRW, SCRH), "Pong");
sf::RectangleShape ball(sf::Vector2f(BALLW, BALLH));
sf::RectangleShape player(sf::Vector2f(PADDLEW, PADDLEH));
sf::RectangleShape enemy(sf::Vector2f(PADDLEW, PADDLEH));
float ballSpeedX = BSPEED;
float ballSpeedY = BSPEED;
float enemySpeed = ESPEED;
bool gameOver = false;
int playerPoints = 0;
int enemyPoints = 0;
sf::Font font;
sf::Text plScoreTxt;
sf::Text enScoreTxt;
void draw();
void moveBall();
void enemyAI();
bool intersect(sf::Vector2f, sf::Vector2f, sf::Vector2f);
float intersectPos(float, sf::RectangleShape);
void reset();
int main(){
// Font and text stuff
std::string fontname = "segoeuil.ttf";
if (!font.loadFromFile(fontname))
{
std::cerr << "Font " << "\"" << fontname << "\" couldn't be/wasn't loaded. Please try again.\n";
}
plScoreTxt.setFont(font);
plScoreTxt.setCharacterSize(200);
plScoreTxt.setPosition(sf::Vector2f(window.getSize().x / 2 - 250, window.getSize().y / 2 - 150));
enScoreTxt.setFont(font);
enScoreTxt.setColor(sf::Color(255, 255, 255, 32));
enScoreTxt.setCharacterSize(200);
enScoreTxt.setPosition(sf::Vector2f(window.getSize().x / 2 + 150, window.getSize().y / 2 - 150));
// Setting up the window ant entities
window.setMouseCursorVisible(false);
ball.setPosition(sf::Vector2f(window.getSize().x / 2 - BALLW / 2, window.getSize().y / 2 - BALLH / 2));
enemy.setPosition(sf::Vector2f(window.getSize().x - PADDLEW, window.getSize().y / 2 - PADDLEH / 2));
// Mouse confinement stuff
RECT rect;
rect.bottom = window.getPosition().y + window.getSize().y + 32 - PADDLEH / 2;
rect.top = window.getPosition().y + 31 + PADDLEH / 2;
rect.left = window.getPosition().x + 50;
rect.right = window.getPosition().x + window.getSize().x - 42;
ClipCursor(&rect);
// Game loop
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if (event.type == sf::Event::Closed){
window.close();
}
if (event.type == sf::Event::KeyPressed){
// Press escape to close the window
if (event.key.code == sf::Keyboard::Escape)
window.close();
}
}
moveBall(); // Move the ball and test for collision
player.setPosition(sf::Vector2f(player.getPosition().x, sf::Mouse::getPosition(window).y - PADDLEH / 2)); // Move the player paddle to mouse position
if (ballSpeedX > 0) // Perform enemy AI stuff when the ball is heading in it's direction
enemyAI();
if (gameOver){ // if someone scored, reset the game
reset();
}
// Text stuff
plScoreTxt.setString(std::to_string(playerPoints));
enScoreTxt.setString(std::to_string(enemyPoints));
plScoreTxt.setColor(sf::Color(ball.getFillColor().r, ball.getFillColor().g, ball.getFillColor().b, 32));
enScoreTxt.setColor(sf::Color(ball.getFillColor().r, ball.getFillColor().g, ball.getFillColor().b, 32));
player.setFillColor(ball.getFillColor());
enemy.setFillColor(ball.getFillColor());
draw(); // draw everything to the screen
// If somebody wins, the window closes (the window can also be closed by pressing escape)
if (playerPoints > 9 || enemyPoints > 9)
window.close();
}
return 0;
}
void reset(){
ball.setPosition(sf::Vector2f(window.getSize().x / 2 - BALLW / 2, window.getSize().y / 2 - BALLH / 2));
enemy.setPosition(sf::Vector2f(window.getSize().x - PADDLEW, window.getSize().y / 2 - PADDLEH / 2));
if (ballSpeedX < 0)
ballSpeedX = BSPEED;
else
ballSpeedX = BSPEED * -1;
ballSpeedY = BSPEED;
enemySpeed = ESPEED;
gameOver = false;
}
void draw(){
window.clear();
window.draw(plScoreTxt);
window.draw(enScoreTxt);
window.draw(player);
window.draw(enemy);
window.draw(ball);
window.display();
}
void increaseSpeed(){
if (ballSpeedY > 0)
ballSpeedY += (rand() % 15 + 1) / 1000.0;
else
ballSpeedY -= (rand() % 15 + 1) / 1000.0;
if (ballSpeedX > 0)
ballSpeedX += (rand() % 20 + 1) / 1000.0;
else
ballSpeedX -= (rand() % 20 + 1) / 1000.0;
}
void moveBall(){
ball.setPosition(sf::Vector2f(ball.getPosition().x + ballSpeedX, ball.getPosition().y + ballSpeedY));
// Ball -> Wall
if (ball.getPosition().x < 0){
enemyPoints++;
gameOver = true;
return;
}
if (ball.getPosition().x + BALLW > window.getSize().x){
playerPoints++;
gameOver = true;
return;
}
if (ball.getPosition().y < 0 || ball.getPosition().y + BALLH > window.getSize().y){
ballSpeedY *= -1;
return;
}
// Ball -> Both paddles (Side)
if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY), sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y),
sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y + PADDLEH)) ||
intersect(sf::Vector2f(ballSpeedX, ballSpeedY), enemy.getPosition(),
sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y + PADDLEH)))
{
ballSpeedX *= -1;
ball.setFillColor(sf::Color(rand() % 156 + 100, rand() % 156 + 100, rand() % 156 + 100));
increaseSpeed();
return;
}
// Player - Top
if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY), player.getPosition(),
sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y)))
{
ballSpeedY += 0.02f;
if (ballSpeedX < 0)
ballSpeedX *= -1;
if (ballSpeedY > 0)
ballSpeedY *= -1;
return;
}
// Player - Bottom
if (intersect(sf::Vector2f(ballSpeedX, ballSpeedY),
sf::Vector2f(player.getPosition().x, player.getPosition().y + PADDLEH),
sf::Vector2f(player.getPosition().x + PADDLEW, player.getPosition().y + PADDLEH)))
{
if (ballSpeedX < 0)
ballSpeedX *= -1;
if (ballSpeedY < 0)
ballSpeedY *= -1;
ballSpeedY += 0.02f;
}
}
void enemyAI(){
float Ypos = intersectPos(ballSpeedY, ball);
// Move enemy to optimal location
if (enemy.getPosition().y + PADDLEH / 2 > Ypos){
enemy.setPosition(sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y - enemySpeed));
}
else if (enemy.getPosition().y + PADDLEH / 2 < Ypos){
enemy.setPosition(sf::Vector2f(enemy.getPosition().x, enemy.getPosition().y + enemySpeed));
}
// Check if enemy collided with the walls
if (enemy.getPosition().y < 0)
enemy.setPosition(enemy.getPosition().x, 0);
else if (enemy.getPosition().y + PADDLEH > window.getSize().y)
enemy.setPosition(enemy.getPosition().x, window.getSize().y - PADDLEH);
}
bool lineIntersect(sf::Vector2f LinePointA, sf::Vector2f LinePointB, sf::Vector2f pointMoving, float diam){
if (LinePointA.x == LinePointB.x && pointMoving.x >= LinePointA.x - diam / 2 && pointMoving.x <= LinePointA.x + diam / 2 &&
pointMoving.y >= LinePointA.y && pointMoving.y <= LinePointB.y)
return true;
if (LinePointA.y == LinePointB.y && pointMoving.y >= LinePointA.y - diam / 2 && pointMoving.y <= LinePointA.y + diam / 2 &&
pointMoving.x >= LinePointA.x && pointMoving.x <= LinePointB.x)
return true;
return false;
}
bool intersect(sf::Vector2f vel, sf::Vector2f LinePointA, sf::Vector2f LinePointB){
double m, b;
m = ((ball.getPosition().y + ball.getSize().y / 2.0 + vel.y) - (ball.getPosition().y + ball.getSize().y / 2.0)) /
((ball.getPosition().x + ball.getSize().x / 2.0 + vel.x) - (ball.getPosition().x + ball.getSize().x / 2.0));
b = (ball.getPosition().y + ball.getSize().y / 2.0 + vel.y) - m * (ball.getPosition().x + ball.getSize().x / 2.0 + vel.x);
// If the line is on the y axis, solve for y in order to have highest accuracy rate
if (vel.x > 0 && LinePointA.x == LinePointB.x){
for (double x = 0; x <= vel.x; x += 0.1){
if (lineIntersect(LinePointA, LinePointB, sf::Vector2f(x + ball.getPosition().x + ball.getSize().x / 2, (int)(m * (x + ball.getPosition().x + ball.getSize().x / 2.0) + b)), ball.getSize().x))
return true;
}
}
else if (vel.x < 0 && LinePointA.x == LinePointB.x){
for (double x = 0; x >= vel.x; x -= 0.1){
if (lineIntersect(LinePointA, LinePointB, sf::Vector2f(x + ball.getPosition().x + ball.getSize().x / 2, (int)(m * (x + ball.getPosition().x + ball.getSize().x / 2.0) + b)), ball.getSize().x))
return true;
}
}
// if the line is on the x axis, solve for x in order to have highest accuracy rate
else if (vel.y > 0 && LinePointA.y == LinePointB.y){
for (double y = 0; y <= vel.y; y += 0.1){
if (lineIntersect(LinePointA, LinePointB, sf::Vector2f((int)((y + ball.getPosition().y + ball.getSize().y / 2.0 - b) / m), y + ball.getPosition().y + ball.getSize().y / 2.0), ball.getSize().y))
return true;
}
}
else if (vel.y < 0 && LinePointA.y == LinePointB.y){
for (double y = 0; y >= vel.y; y -= 0.1){
if (lineIntersect(LinePointA, LinePointB, sf::Vector2f((int)((y + ball.getPosition().y + ball.getSize().y / 2.0 - b) / m), y + ball.getPosition().y + ball.getSize().y / 2.0), ball.getSize().y))
return true;
}
}
return false;
}
// This function is for the enemy to determine where it has to go
// It basically returns a 'y' coordinate of where the ball is going to end up for the enemy's 'x' coordinate
// If the ball hits either the ceiling or the floor before it reaches it's goal, the function is called recursively until the ball stays within bounds
float intersectPos(float tmpBallSpeedY, sf::RectangleShape tmpBall){
float x = window.getSize().x - PADDLEW;
float m = (((tmpBall.getPosition().y + BALLH / 2) + tmpBallSpeedY) - (tmpBall.getPosition().y + BALLH / 2)) /
(((tmpBall.getPosition().x + BALLW / 2) + ballSpeedX) - (tmpBall.getPosition().x + BALLW / 2));
float b = (tmpBall.getPosition().y) - m * (tmpBall.getPosition().x);
float y = m * x + b;
if (y <= 0){
tmpBall.setPosition(sf::Vector2f((0 - b) / m, 0));
y = intersectPos(tmpBallSpeedY*-1, tmpBall);
}
else if (y >= window.getSize().y - BALLH){
tmpBall.setPosition(sf::Vector2f(((window.getSize().y - BALLH) - b) / m, window.getSize().y - BALLH));
y = intersectPos(tmpBallSpeedY*-1, tmpBall);
}
return y;
}