Code Review: Pong

Started by
7 comments, last by Musikai 10 years, 11 months ago

I've just finished making a pong clone written in C++ using SFML and would really appreciate some feedback and criticism on the game logic and/or coding style. One thing i really don't like is that my game states are just big switch staments. I've read about state machines and even tried implementing one but i just couldn't figure out how everything fits together, if anyone could help me with a state machine, that would be awesome.

The executable with DLL's and audio files.

[attachment=15505:Pong.rar]

Main.cpp


#include <Windows.h>
#include <SFML\Graphics.hpp>
#include "Game.h"
#include "Keyboard.h"

int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	sf::RenderWindow window( sf::VideoMode(900, 700), "Pong", sf::Style::Close );
	window.setVerticalSyncEnabled( true );

	MyGame game( window );
	Keyboard keyboard;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
			else if (event.type == sf::Event::KeyPressed)
				keyboard.KeyDown( event.key.code );
			else if (event.type == sf::Event::KeyReleased)
				keyboard.KeyUp( event.key.code );
        }

		game.Update( keyboard );
		keyboard.Update();

        window.clear();
		game.Draw();
        window.display();
    }

	return 0;
}

Game.h


#ifndef GAME_H
#define GAME_H
#include <SFML\Graphics.hpp>
#include <SFML\Audio.hpp>
#include "Paddle.h"
#include "Ball.h"
#include "Scores.h"

enum ScreenState
{
	SS_TITLE,
	SS_INGAME
};

enum InGameState
{
	GS_INIT,
	GS_ONE,
	GS_TWO,
	GS_THREE,
	GS_RUN,
	GS_PAUSED,
	GS_GAMEOVER
};

enum MenuState
{
	MS_YES,
	MS_NO
};

class MyGame
{
public:
	MyGame( sf::RenderWindow& window );
	void Update( Keyboard& keyboard );
	void Draw();

private:
	void InitText();
	void InitSounds();

	bool TestCollision( Paddle paddle, Ball ball );
	void RestartGame();
	void Reset();

private:
	sf::RenderWindow& m_window;
	sf::Clock m_clock;
	sf::Clock m_eventClock;
	float m_eventTime;

	Paddle m_player1;
	Paddle m_player2;
	Ball m_ball;
	Scores m_scores;

	// States
	ScreenState m_screenState;
	InGameState m_inGameState;
	MenuState m_menuState;

	// Font and Text
	sf::Font m_sqPixFont;

	sf::Text txtTitle;
	sf::Text txtPressEnt;
	sf::Text txtThree;
	sf::Text txtTwo;
	sf::Text txtOne;
	sf::Text txtWinner;
	sf::Text txtPaused;
	sf::Text txtQuitGame;
	sf::Text txtPlayAgain;
	sf::Text txtYes;
	sf::Text txtNo;

	sf::Color m_blue;
	sf::Color m_yellow;
	sf::Color m_grey;

	// Sounds
	sf::Music m_fearOfDark;

	sf::SoundBuffer m_blip1Buffer;
	sf::Sound m_blip1;
	sf::SoundBuffer m_blip2Buffer;
	sf::Sound m_blip2;
	sf::SoundBuffer m_blopBuffer;
	sf::Sound m_blop;
};

#endif

Game.cpp


#include "Game.h"
using std::to_string;

MyGame::MyGame( sf::RenderWindow& window )
:       m_window( window ),
	m_player1( 75.0f, window.getSize().x, window.getSize().y ),
	m_player2( 815.0f, window.getSize().x, window.getSize().y ),
	m_ball( window.getSize().x, window.getSize().y )
{
	m_screenState = SS_TITLE;
	m_inGameState = GS_INIT;
	m_menuState	  = MS_YES;

	m_blue = sf::Color( 0, 100, 200, 255 );
	m_yellow = sf::Color( 255, 200, 0, 255 );
	m_grey = sf::Color( 20, 20, 20, 255 );

	InitText();
	InitSounds();
	m_scores.Init( m_sqPixFont, m_window );

	m_fearOfDark.play();
}

void MyGame::Update( Keyboard& keyboard )
{
	float frameTime = m_clock.getElapsedTime().asSeconds();
	m_clock.restart();

	m_eventTime = m_eventClock.getElapsedTime().asSeconds();

	switch( m_screenState )
	{
	case SS_TITLE:
		if( keyboard.KeyIsPressed( sf::Keyboard::Escape ) )
			m_window.close();
		if( keyboard.KeyIsPressed( sf::Keyboard::Return ) )
		{
			m_screenState = SS_INGAME;
			m_fearOfDark.stop();
			m_eventClock.restart();
		}
		break;

	case SS_INGAME:
		switch( m_inGameState )
		{
		case GS_INIT:
			{
				if( m_eventTime > 1.0f )
				{
					m_blip1.play();
					m_inGameState = GS_THREE;
					m_eventTime = 0;
					m_eventClock.restart();
				}

				break;
			}

		case GS_THREE:
			{
				if( m_eventTime > 1.0f )
				{
					m_blip1.play();
					m_inGameState = GS_TWO;
					m_eventTime = 0;
					m_eventClock.restart();
				}

				break;
			}

		case GS_TWO:
			{
				if( m_eventTime > 1.0f )
				{
					m_blip1.play();
					m_inGameState = GS_ONE;
					m_eventTime = 0;
					m_eventClock.restart();
				}

				break;
			}

		case GS_ONE:
			{
				if( m_eventTime > 1.0f )
				{
					m_blip2.play();
					m_inGameState = GS_RUN;
					m_eventTime = 0;
					m_eventClock.restart();
				}

				break;
			}

		case GS_RUN:
			{
				if( m_scores.Winner() )
				{
					m_inGameState = GS_GAMEOVER;
					break;
				}

				if( keyboard.KeyIsPressed( sf::Keyboard::Escape ) ||
					keyboard.KeyIsPressed( sf::Keyboard::P ) )
					m_inGameState = GS_PAUSED;

				// Player one controls
				if( keyboard.KeyIsHeld( sf::Keyboard::W ) )
					m_player1.MoveUp( frameTime );
				if( keyboard.KeyIsHeld( sf::Keyboard::S ) )
					m_player1.MoveDown( frameTime );

				// Player two controls
				if( keyboard.KeyIsHeld( sf::Keyboard::Up ) )
					m_player2.MoveUp( frameTime );
				if( keyboard.KeyIsHeld( sf::Keyboard::Down ) )
					m_player2.MoveDown( frameTime );

				// World collision
				if( m_ball.GetPosition().x < 0 )
				{
					m_ball.SetXPosition( 0 );
					if( m_ball.GetSpeed().x < 0 )
					{
						m_ball.BounceX();
						m_ball.IncreaseSpeed();
						m_scores.Player2Scored();
					}
				}
				if( m_ball.GetPosition().x + m_ball.GetSize().x >= m_window.getSize().x )
				{
					m_ball.SetXPosition( m_window.getSize().x - m_ball.GetSize().x );
					if( m_ball.GetSpeed().x > 0 )
					{
						m_ball.BounceX();
						m_ball.IncreaseSpeed();
						m_scores.Player1Scored();
					}
				}
				if( m_ball.GetPosition().y < 0 )
				{
					m_ball.SetYPosition( 0 );
					if( m_ball.GetSpeed().y < 0 )
					{
						m_ball.BounceY();
					}
				}
				if( m_ball.GetPosition().y + m_ball.GetSize().y >= m_window.getSize().y )
				{
					m_ball.SetYPosition( m_window.getSize().y - m_ball.GetSize().y );
					if( m_ball.GetSpeed().y > 0 )
					{
						m_ball.BounceY();
					}
				}

				// Paddle collision
				if( TestCollision( m_player1, m_ball ) )
				{
					if( m_ball.GetSpeed().x < 0 )
					{
						m_blop.play();
						m_ball.BouncePaddle( m_player1 );
						m_ball.IncreaseSpeed();
					}
				}
				if( TestCollision( m_player2, m_ball ) )
				{
					if( m_ball.GetSpeed().x > 0 )
					{
						m_blop.play();
						m_ball.BouncePaddle( m_player2 );
						m_ball.IncreaseSpeed();
					}
				}

				m_player1.Update();
				m_player2.Update();
				m_ball.Update( frameTime );
				m_scores.Update();

				break;
			}
		case GS_PAUSED:
			{
				if( keyboard.KeyIsPressed( sf::Keyboard::Escape ) ||
					keyboard.KeyIsPressed( sf::Keyboard::P ) )
					m_inGameState = GS_RUN;

				if( keyboard.KeyIsPressed( sf::Keyboard::Left ) ||
					keyboard.KeyIsPressed( sf::Keyboard::A    ) )
					m_menuState = MS_YES;

				if( keyboard.KeyIsPressed( sf::Keyboard::Right ) ||
					keyboard.KeyIsPressed( sf::Keyboard::D   ) )
					m_menuState = MS_NO;

				if( keyboard.KeyIsPressed( sf::Keyboard::Return ) )
				{
					if( m_menuState == MS_YES )
						Reset();
					else
						m_inGameState = GS_RUN;
				}

				if( m_menuState == MS_YES )
				{
					txtYes.setColor( m_blue );
					txtNo.setColor( m_grey );
				}
				else if( m_menuState == MS_NO )
				{
					txtYes.setColor( m_grey );
					txtNo.setColor( m_blue );
				}

				break;
			}
		case GS_GAMEOVER:
			{
				if( keyboard.KeyIsPressed( sf::Keyboard::Left ) ||
					keyboard.KeyIsPressed( sf::Keyboard::A    ) )
					m_menuState = MS_YES;

				if( keyboard.KeyIsPressed( sf::Keyboard::Right ) ||
					keyboard.KeyIsPressed( sf::Keyboard::D   ) )
					m_menuState = MS_NO;

				if( keyboard.KeyIsPressed( sf::Keyboard::Return ) )
				{
					if( m_menuState == MS_YES )
						RestartGame();
					if( m_menuState == MS_NO )
						Reset();
				}

				if( m_menuState == MS_YES )
				{
					txtYes.setColor( m_blue );
					txtNo.setColor( m_grey );
				}
				else if( m_menuState == MS_NO )
				{
					txtYes.setColor( m_grey );
					txtNo.setColor( m_blue );
				}


				break;
			}
		}
		break;
	}
}

void MyGame::Draw()
{
	m_eventTime = m_eventClock.getElapsedTime().asSeconds();

	switch( m_screenState )
	{
	case SS_TITLE:
		m_window.draw( txtTitle );

		if( m_eventTime < 0.5f )
			m_window.draw( txtPressEnt );
		else if( m_eventTime > 1.0f )
			m_eventClock.restart();

		break;

	case SS_INGAME:
		{
			switch( m_inGameState )
			{
			case GS_INIT:
				break;

			case GS_THREE:
				m_window.draw( txtThree );
				break;

			case GS_TWO:
				m_window.draw( txtTwo );
				break;

			case GS_ONE:
				m_window.draw( txtOne );
				break;

			case GS_RUN:
				break;

			case GS_PAUSED:
				m_window.draw( txtPaused );
				m_window.draw( txtQuitGame );
				m_window.draw( txtYes );
				m_window.draw( txtNo );
				break;

			case GS_GAMEOVER:
				m_window.draw( txtWinner );
				m_window.draw( txtPlayAgain );
				m_window.draw( txtYes );
				m_window.draw( txtNo );
				break;
			}

			m_scores.Draw( m_window );
			m_player1.Draw( m_window );
			m_player2.Draw( m_window );
			m_ball.Draw( m_window );

			break;
		}
	}
}

void MyGame::InitText()
{
	int centerX = m_window.getSize().x / 2;
	int centerY = m_window.getSize().y / 2;

	m_sqPixFont.loadFromFile( "squarePixel7.ttf" );

	txtTitle = sf::Text( "PONG", m_sqPixFont, 100);
	txtTitle.setColor( m_blue );
	txtTitle.setPosition( centerX - (txtTitle.getLocalBounds().width / 2), 100.0f );

	txtPressEnt = sf::Text( "Press Enter", m_sqPixFont, 30 );
	txtPressEnt.setColor( m_blue );
	txtPressEnt.setPosition( centerX - (txtPressEnt.getLocalBounds().width / 2), 500 );

	txtOne = sf::Text( "1", m_sqPixFont, 100 );
	txtOne.setColor( m_blue );
	txtOne.setPosition( centerX - (txtOne.getLocalBounds().width / 2) - 5, 400 );

	txtTwo = sf::Text( "2", m_sqPixFont, 100 );
	txtTwo.setColor( m_blue );
	txtTwo.setPosition( centerX - (txtTwo.getLocalBounds().width / 2), 400 );

	txtThree = sf::Text( "3", m_sqPixFont, 100 );
	txtThree.setColor( m_blue );
	txtThree.setPosition( centerX - (txtThree.getLocalBounds().width / 2), 400 );

	txtWinner = sf::Text( "Winner", m_sqPixFont, 100 );
	txtWinner.setColor( m_yellow );
	txtWinner.setPosition( centerX - (txtWinner.getLocalBounds().width / 2), 100 );

	txtPaused = sf::Text( "Paused", m_sqPixFont, 100 );
	txtPaused.setColor( m_blue );
	txtPaused.setPosition( centerX - (txtPaused.getLocalBounds().width / 2), 100 );

	txtQuitGame = sf::Text( "Quit game?", m_sqPixFont, 30 );
	txtQuitGame.setColor( m_blue );
	txtQuitGame.setPosition( centerX - (txtQuitGame.getLocalBounds().width / 2), 450 );

	txtPlayAgain = sf::Text( "Play Again?", m_sqPixFont, 30 );
	txtPlayAgain.setColor( m_blue );
	txtPlayAgain.setPosition( centerX - (txtPlayAgain.getLocalBounds().width / 2), 450 );

	txtYes = sf::Text( "Yes", m_sqPixFont, 30 );
	txtYes.setColor( m_grey );
	txtYes.setPosition( centerX - txtYes.getLocalBounds().width - 50, 510);

	txtNo = sf::Text( "No", m_sqPixFont, 30 );
	txtNo.setColor( m_grey );
	txtNo.setPosition( centerX + 50, 510);
}

void MyGame::InitSounds()
{
	m_fearOfDark.openFromFile( "fearofdark.wav" );

	m_blip1Buffer.loadFromFile( "Blip1.wav" );
	m_blip1.setBuffer( m_blip1Buffer );

	m_blip2Buffer.loadFromFile( "Blip2.wav" );
	m_blip2.setBuffer( m_blip2Buffer );

	m_blopBuffer.loadFromFile( "Blop.wav" );
	m_blop.setBuffer( m_blopBuffer );
}

bool MyGame::TestCollision( Paddle paddle, Ball ball )
{
	float padX = paddle.GetPosition().x;
	float padY = paddle.GetPosition().y;
	float padW = paddle.GetSize().x;
	float padH = paddle.GetSize().y;

	float ballX = ball.GetPosition().x;
	float ballY = ball.GetPosition().y;
	float ballW = ball.GetSize().x;
	float ballH = ball.GetSize().y;

	if( padX + padW < ballX || ballX + ballW < padX ) return false;
	if( padY + padH < ballY || ballY + ballH < padY ) return false;

	return true;
}

void MyGame::RestartGame()
{
	m_inGameState = GS_INIT;
	m_menuState = MS_YES;

	m_clock.restart();
	m_eventClock.restart();

	m_player1.Reset();
	m_player2.Reset();
	m_ball.Reset();
	m_scores.Reset();
}

void MyGame::Reset()
{
	m_screenState = SS_TITLE;
	RestartGame();
}

Paddle.h


#ifndef PADDLE_H
#define PADDLE_H

#include <SFML\Graphics.hpp>

class Paddle
{
public:
	Paddle();
	Paddle( float posX, int screenHeight, int screenWidth );
	void Update();
	void Draw( sf::RenderWindow& window );

	void MoveUp( float frameTime );
	void MoveDown( float frameTime );
	void Reset();

	sf::Vector2f GetPosition();
	sf::Vector2f GetSize();

private:
	int m_screenWidth, m_screenHeight;

	sf::RectangleShape m_rectangle;
	sf::Vector2f m_position;
	int m_width, m_height;
	float m_moveSpeed;

	// Stored variables for reset.
	float m_initX, m_initY;
};

#endif 

Paddle.cpp


#include "Paddle.h"
#include "Keyboard.h"

Paddle::Paddle()
{}

Paddle::Paddle( float posX, int screenWidth, int screenHeight )
:	m_screenWidth( screenWidth ),
	m_screenHeight( screenHeight ),
	m_width( 15 ),
	m_height( 100 ),
	m_moveSpeed( 500 )
{
	m_initX = m_position.x = posX;
	m_initY = m_position.y = ((float)screenHeight / 2.0f) - ((float)m_height / 2.0f);

	m_rectangle = sf::RectangleShape( sf::Vector2f( (float)m_width, (float)m_height ) );
	m_rectangle.setPosition( m_position.x, m_position.y );
	m_rectangle.setFillColor( sf::Color( 0, 100, 200, 255 ) );
}

void Paddle::Update()
{
	// Clamp to screen
	if( m_position.y < 0 )
		m_position.y = 0;
	if( m_position.y + m_height >= m_screenHeight )
		m_position.y = (float)m_screenHeight - (float)m_height;

	m_rectangle.setPosition( m_position.x, m_position.y );
}

void Paddle::Draw( sf::RenderWindow& window )
{
	window.draw( m_rectangle );
}

void Paddle::MoveUp( float frameTime )
{
	m_position.y -= m_moveSpeed * frameTime;
}

void Paddle::MoveDown( float frameTime )
{
	m_position.y += m_moveSpeed * frameTime;
}

sf::Vector2f Paddle::GetPosition()
{
	return m_position;
}

sf::Vector2f  Paddle::GetSize()
{
	sf::Vector2f size;
	size.x = (float)m_width;
	size.y = (float)m_height;
	return size;
}

void Paddle::Reset()
{
	m_position.x = m_initX;
	m_position.y = m_initY;
	m_rectangle.setPosition( m_position.x, m_position.y );
}

Ball.h


#ifndef BALL_H
#define BALL_H

#include <SFML\Graphics.hpp>
#include <SFML\Audio.hpp>
#include "Paddle.h"

class Ball
{
public:
	Ball( int screenWidth, int screenHeight );
	void Update( float frameTime );
	void Draw( sf::RenderWindow& window );

	void BounceX();
	void BounceY();
	void BouncePaddle( Paddle paddle );
	void SetXPosition( float x );
	void SetYPosition( float y );
	void IncreaseSpeed();
	void Reset();

	sf::Vector2f GetPosition();
	sf::Vector2f GetSize();
	sf::Vector2f GetSpeed();

private:
	sf::RectangleShape m_rectangle;
	sf::Vector2f m_position;
	int m_width, m_height;
	float m_speedX, m_speedY, m_maxYSpeed;

	// Store variables for reset
	float m_initX, m_initY;
	float m_initYspeed;
};

#endif

Ball.cpp


#include "Ball.h"
#include <random>
#include <time.h>

Ball::Ball( int screenWidth, int screenHeight )
:	m_maxYSpeed( 650 ),
	m_initYspeed( 650 )
{
	// Randomize the initial direction.
	srand( (unsigned int)time(NULL) );
	int xDirection = rand() % 2;
	if( xDirection == 0 )
		xDirection = -1;
	m_speedX = xDirection * 600;
	m_speedY = 0;

	m_width = m_height = 10;
	m_initX = m_position.x = (screenWidth / 2) - (m_width / 2);
	m_initY = m_position.y = (screenHeight / 2) - (m_height / 2);

	m_rectangle = sf::RectangleShape( sf::Vector2f( (float)m_width, (float)m_height ) );
	m_rectangle.setPosition( m_position.x, m_position.y );
	m_rectangle.setFillColor( sf::Color( 0, 100, 200, 255 ) );
}

void Ball::Update( float frameTime )
{
	m_position.x += m_speedX * frameTime;
	m_position.y += m_speedY * frameTime;

	m_rectangle.setPosition( m_position.x, m_position.y );
}

void Ball::Draw( sf::RenderWindow& window )
{
	window.draw( m_rectangle );
}

sf::Vector2f Ball::GetPosition()
{
	return m_position;
}

sf::Vector2f Ball::GetSize()
{
	sf::Vector2f size;
	size.x = (float)m_width;
	size.y = (float)m_height;
	return size;
}

sf::Vector2f Ball::GetSpeed()
{
	sf::Vector2f speed;
	speed.x = m_speedX;
	speed.y = m_speedY;
	return speed;
}

void Ball::BounceX()
{
	m_speedX *= -1;
}

void Ball::BounceY()
{
	m_speedY *= -1;
}

void Ball::BouncePaddle( Paddle paddle )
{
	BounceX();

	float paddleY = paddle.GetPosition().y + (paddle.GetSize().y / 2.0f);
	float ballY = m_position.y + (m_height / 2.0f);

	m_speedY = (ballY - paddleY) / (paddle.GetSize().y / 2.0f) * m_maxYSpeed;
}

void Ball::SetXPosition( float x )
{
	m_position.x = x;
}

void Ball::SetYPosition( float y )
{
	m_position.y = y;
}

void Ball::IncreaseSpeed()
{
	if( m_speedX < 0 )
		m_speedX -= 1;
	if( m_speedX > 0 )
		m_speedX += 1;

	m_maxYSpeed += 1;
}

void Ball::Reset()
{
	m_position.x = m_initX;
	m_position.y = m_initY;
	m_maxYSpeed = m_initYspeed;
	m_rectangle.setPosition( m_position.x, m_position.y );

	int xDirection = rand() % 2;
	if( xDirection == 0 )
		xDirection = -1;
	m_speedX = xDirection * 500;
	m_speedY = 0;
}

Scores.h


#ifndef SCORES_H
#define SCORES_H

#include <SFML\Graphics.hpp>

class Scores
{
public:
	Scores();
	void Init( sf::Font& font, sf::RenderWindow& window );
	void Update();
	void Draw( sf::RenderWindow& window );

	void Player1Scored();
	void Player2Scored();
	void SetMaxScore( int maxScore );

	bool Winner();

	void Reset();

private:
	int m_player1Score, m_player2Score, m_maxScore;
	sf::Text m_txtScore1, m_txtScore2;

	sf::Color m_grey;
	sf::Color m_yellow;

	bool m_winner;
};

#endif

Scores.cpp


#include "Scores.h"

Scores::Scores()
{}

void Scores::Init( sf::Font& font, sf::RenderWindow& window )
{
	m_yellow = sf::Color( 255, 200, 0, 255 );
	m_grey = sf::Color( 20, 20, 20, 255 );

	m_player1Score = 0;
	m_player2Score = 0;
	m_maxScore = 10;
	m_winner = false;
	
	int centerY = window.getSize().y / 2;

	m_txtScore1 = sf::Text( "00", font, 100 );
	m_txtScore1.setColor( m_grey );
	m_txtScore1.setPosition( 350 - m_txtScore1.getLocalBounds().width, centerY - (m_txtScore1.getLocalBounds().height / 2) - m_txtScore1.getLocalBounds().top );

	m_txtScore2 = sf::Text( "00", font, 100 );
	m_txtScore2.setColor( m_grey );
	m_txtScore2.setPosition( 550, centerY - (m_txtScore2.getLocalBounds().height / 2) - m_txtScore2.getLocalBounds().top );
}

void Scores::Update()
{
	if( m_player1Score >= m_maxScore )
	{
		m_player1Score = m_maxScore;
		m_txtScore1.setColor( m_yellow );
		m_winner = true;
	}

	if( m_player2Score >= m_maxScore )
	{
		m_player2Score = m_maxScore;
		m_txtScore2.setColor( m_yellow );
		m_winner = true;
	}

	std::string score1 = std::to_string( (_Longlong)m_player1Score );
	if( m_player1Score < 10 )
		score1 = std::string( "0" ).append( score1 );
	m_txtScore1.setString( score1 );

	std::string score2 = std::to_string( (_Longlong)m_player2Score );
	if( m_player2Score < 10 )
		score2 = std::string( "0" ).append( score2 );
	m_txtScore2.setString( score2 );
}

void Scores::Draw( sf::RenderWindow& window )
{
	window.draw( m_txtScore1 );
	window.draw( m_txtScore2 );
}

void Scores::Player1Scored()
{
	m_player1Score += 1;
}

void Scores::Player2Scored()
{
	m_player2Score += 1;
}

void Scores::SetMaxScore( int maxScore )
{
	m_maxScore = maxScore;
}

bool Scores::Winner()
{
	return m_winner;
}

void Scores::Reset()
{
	m_player1Score = 0;
	m_player2Score = 0;
	m_winner = false;
	m_txtScore1.setColor( m_grey );
	m_txtScore1.setString( "00" );
	m_txtScore2.setColor( m_grey );
	m_txtScore2.setString( "00" );

}

Advertisement

DLL-s were missing. ....I downloaded some, but there was an other error. ....use static links.

I didnt want to go through the logic but about the style:

1. Instead of this:


    m_yellow = sf::Color( 255, 200, 0, 255 );
    m_grey = sf::Color( 20, 20, 20, 255 );

    m_player1Score = 0;
    m_player2Score = 0;
    m_maxScore = 10;
    m_winner = false;
 

...do this:


    m_yellow =        sf::Color( 255, 200, 0, 255 );
    m_grey =          sf::Color( 20, 20, 20, 255 );

    m_player1Score =  0;
    m_player2Score =  0;
    m_maxScore =      10;
    m_winner =        false;
 

2. Instead of this:


    sf::Music m_fearOfDark;

    sf::SoundBuffer m_blip1Buffer;
    sf::Sound m_blip1;
    sf::SoundBuffer m_blip2Buffer;
    sf::Sound m_blip2;
    sf::SoundBuffer m_blopBuffer;
    sf::Sound m_blop;
 

...do this:


    sf::Music        m_fearOfDark;

    sf::SoundBuffer  m_blip1Buffer;
    sf::Sound        m_blip1;
    sf::SoundBuffer  m_blip2Buffer;
    sf::Sound        m_blip2;
    sf::SoundBuffer  m_blopBuffer;
    sf::Sound        m_blop;
 

....make exceptions with very long types and names of course.

3. No magic numbers! (Except if its == 0, or /2, or something trivial like that)


m_speedX = xDirection * 500;
m_yellow = sf::Color( 255, 200, 0, 255 );
m_grey = sf::Color( 20, 20, 20, 255 );
 

4. Its a very good idea to put a C/E/S for the first letters of class/enum/struct names.

...so class MyGame becomes CMyGame, enum InGameState becomes EInGameState, etc.

5. Its quite a personal choice but IMO if you dont use some convention that clearly distinguishes the functions and variables your code will be anything but easy to read.

something like:

getScreenWidth - function

screen_width - variable

...I guess some IDE-s use different colors for them, ....but still:)

6. If you dont intend to use copy/default constructors, default assignment operators, then make them at least private or delete them(c++11).

7. Using consts is also a good thing.

8. Use different functions for handling input, and running physics. (I didnt really look into the code but your game has just 2 public functions). Later you will want to use different frequency for rendering and doing physics.

9. Short functions could be inline simply because it makes the cpp file easiert to read.


void Scores::Player1Scored()
{
    m_player1Score += 1;
}

void Scores::Player2Scored()
{
    m_player2Score += 1;
}
 

10.


                if( m_ball.GetPosition().y < 0 )
                {
                    m_ball.SetYPosition( 0 );
                    if( m_ball.GetSpeed().y < 0 )
                    {
                        m_ball.BounceY();
                    }
                }
 

...im not sure this is the best way to use the curly brackets but this is an other personal choice

11. At most companies they dont use comments but in your own code its a good thing. ...at least if the code does something tricky and after a few months you will have no idea WTF is that line doing:)

+what do you need a "state machine" for?

Thanks for the feedback Aliii. Strange, can i ask did it say what DLL's were missing?

I'l definately work on the things you mentioned. I spent quite a while reading about hungarian notation, i didn't want to over do it but i can see how prefixing enums, structs and classes would help redability.

As for the state machine, i'm gonna need to know how to make one eventually. This is only a simple game of pong with two screens and the switch statemant for game states is already over 230 lines long.

11. At most companies they dont use comments but in your own code its a good thing. ...at least if the code does something tricky and after a few months you will have no idea WTF is that line doing:)

I don't know in what company you worked, but using comments is generally something you really want. I'm sure there are companies that lack comments for their own reasons, but explaining what your code does with a comment is faster than trying to understand what a person did by watching his/her code.

As for your coding style:

Different people have different styles, while skimming through the code, I generally use the same kind of style. Always be prepared though to adapt someone else's style. Try finding some (simple) samples and go through them and see what you like/don't like while trying to understand what it does (comments are coming in very handy now).

As for statemachines:

You're using to many now for a simple game like this. The countdown could be done in 1 startup state for example and just using the timer to call your sounds, but the more your practice, the more you get to know how you want to get things done and the easier it gets. Rome wasn't build in a day :)

General coding:

I didn't work with SFML, but you can (or rather should be able) to simply increment/decrement the vector like:

vector_1 += vector_2;

instead of separately altering the x, y, z values.

Use references in your functions where you pass your class objects, make them const if you're not going to alter hem. If you pass them by value, you make a new object which is something you don't really want all the time, while passing it as a const reference (the & sign like you did with the window and keyboard) you're simply using that object you're referencing to.

MSVC...110.DLL, ...I downloaded a few dll-s, then it came up with the message: "the procedure entry point ....bla-bla.... couldnt be located in the bla-bla dll". The redistributable dll-s for VisualStudio I guess.

Im not a big fan of those prefixes:) For pointers I use p_ when I want to make it clear that it points to some "distant" object that is not a member of the class or its outside of the current scope. And i_ and f_ when there are lots of temporary ints and floats in the same scope.

Yes ...you mean that big switch:) Paused,GameOver,Run make sense but that Init,3,2,1 not really. And it would be better to separate the input handling and physics.

I dont think you should focus on that though. The one you have now works fine, ...later when you add lots of new things to the game, you will have to rethink the game logic and main loop. ....usually the physics and rendering functions are the most important anyway.

I don't know in what company you worked, but using comments is generally something you really want. I'm sure there are companies that lack comments for their own reasons, but explaining what your code does with a comment is faster than trying to understand what a person did by watching his/her code.

I know that. But at the places Ive been comments were almost nonexistent. Dont ask me why. It forced me to write the cleanest and most easy-to-read code I could. and to use those few comments - that I was allowed to write - very wisely:D Other than that it made no sense.

In a lot of style guides, comments are tightly constrained. Code should be self-documenting (descriptive identifiers, clean code rather than obfuscations, short methods etc...) so that it requires as few explicit comments as possible. Heavily commented code is susceptible to "comment rot", where code is modified but the comments are not always edited to match, so you end up with comments that have nothing at all to do with the code as written. For example, see the Google style guide:

In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.

In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.

Outside of the descriptive comments useful for generating Doxygen documentation, comments should be viewed as a necessary evil, watched with a suspicious stare, and ruthlessly crushed any time they seem to be getting out of hand.

One book had a very nice way to define comments in code. "Comments are the documented failure of a programmer to write clean and readable code".

That doesn't mean that some comments aren't justified. Explaining _why_ something is done in a way that seems weird is often useful ("working around a bug in compiler x", "doing it like this for performance reasons"). Mentioning what the code does on a _higher level_ ("using optimized algorithm from <some link>").

Avoid comments where possible, because most of the time they are outdated by the time you finish typing them, will be confusing more than helping after two weeks and turn into flat out nonsense another few weeks later. Unless your team exists of super disciplined programmers that will always also maintain the comments, no matter how closely that deadline is looming above them.

I'm also not a fan of aligning your assignments. It's not about looking pretty or creating an excel sheet. If stuff is indented so far that you need to bring a ruler to find out which value belongs where, then things just got counter productive. I find it much more useful to insert a blank line to separate blocks that logically belong together.

Besides, having big blocks of assignments is often indicating a different problem. Not getting over the C habit of declaring everything at the start of a function.

For reset or init of simple classes (mostly data, nothing fancy in constructor/destructor), it can often be more convenient to just assign a default constructed object ("object = Object()"). It's also a lot more robust, since you can't forget variables, don't have to touch multiple sections of code when adding new variables and it's immediately obvious to someone reading the code.

f@dzhttp://festini.device-zero.de

Thanks for the replies guys.

I tried getting the msvcr110 to static link but apparently the only way is to change the code generation options in VS, but this makes it assume all libraries will be staticly linked and conflicts with the other DLL's. It's a shame, i see over 40 people have downloaded it but only the people who happen to have Visual Studio 11.0 runtimes will be able to play it. I wonder what commercial games do, do they just include the DLL with the install? Or do they make people install the VS redistributable.

I tried to make code as readable as possible with smart variable names and such, i would try only comment where i though the code wasn't self explanatory, that and laziness.

I've seen people declaring all the variables at the start of a function before, I didn't know that was a C programing style. I didn't really like that as it means a variable can be a mile away from where it's used.

This topic is closed to new replies.

Advertisement