4
\$\begingroup\$

Some of you may have seen my previous post asking if my code was efficient and easy to read.

I have made some more improvements to it by adding sounds, visual count down and checking if the window has focus. Before I carry on with my program I would like to know if this is efficient and easy to read.

Here is the zip file containing all the resources.

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "ResourcePath.hpp"
#include <cstdint>
#include <iostream>

using std::cout;
using std::endl;

//Version
std::string ver = "V0.3";
//Create Text and font variables
sf::Text text;
sf::Font font;

// for eliminating magic numbers
enum class Players
{
    PlayerOne,
    PlayerTwo
};



//Make this class Drawable
class Paddel : public sf::Drawable
{
    // for member data perfered to be started with m prefix
    float mSpeed;
    sf::Vector2f mBorder;
    sf::Vector2f mPosition;
    sf::Vector2u mScreenSize;
    sf::RectangleShape mShape;
public:

    Paddel(sf::Vector2u screenSize, Players player)
    // member data perfered be initilaized as contructor's initilaized list
    : mScreenSize(screenSize)
    , mBorder(8, 6)
    , mSpeed(5.f)
    {
        sf::Vector2f size = sf::Vector2f(20, 100);

        //Which player
        switch (player)
        {
            case Players::PlayerOne:
                //If player one set position to border
                mPosition = mBorder;
                break;
            default:
                //Else set positionX to (screenWidth - paddelWidth) - borderWidth
                mPosition.x = (mScreenSize.x - size.x) - mBorder.x;
                //positionY = borderY
                mPosition.y = mBorder.y;
        }
        //Set size and position of drawable shape
        mShape.setSize(size);
        mShape.setPosition(mPosition);
    }

    void moveUp()
    {
        //Get closer to 0 (Top of screen)
        mPosition.y -= mSpeed;
        //If gone too far add speed
        if (mPosition.y < mBorder.y)
            mPosition.y += mSpeed;
        mShape.setPosition(mPosition);
    }

    //Do the same opposite way round
    void moveDown()
    {
        mPosition.y += mSpeed;
        if (mPosition.y >(mScreenSize.y - mBorder.y) - mShape.getSize().y)
            mPosition.y -= mSpeed;
        mShape.setPosition(mPosition);
    }

    //Override sf::Drawable function
    void draw(sf::RenderTarget& target, sf::RenderStates states) const override
    {
        // sf::RectangleShape has its own defualt RenderStates
        target.draw(mShape, states);
    }
};

int main()
{
    //Initilise screen size and Window
    sf::RenderWindow Window(sf::VideoMode(800,600), "Ping Pong " + ver, sf::Style::Titlebar | sf::Style::Close);

    sf::Vector2u screenSize(Window.getSize().x,Window.sf::Window::getSize().y);

    //Create Text
    bool enableText = true;
    bool countdown = false;
    if(!font.loadFromFile(resourcePath() + "Pacifico.ttf"))
    {
        cout << "No Font File" << endl;
        enableText = false;
    } else {
        text.setFont(font);
        text.setCharacterSize(50);
        text.setColor(sf::Color::White);
    }


    //Limit frame rate and enable vertical sync
    Window.setFramerateLimit(60);
    Window.setVerticalSyncEnabled(true);


    //Load resume sound
    sf::SoundBuffer countdownBuffer;
    sf::SoundBuffer startBuffer;
    if(!countdownBuffer.loadFromFile(resourcePath() + "3_2_1.ogg") || !startBuffer.loadFromFile(resourcePath() + "Go.ogg"))
        return -1;
    sf::Sound countdownSound;
    sf::Sound startSound;
    countdownSound.setBuffer(countdownBuffer);
    startSound.setBuffer(startBuffer);


    //Seporate left from right
    sf::RectangleShape middleBar(sf::Vector2f(10,screenSize.y));
    cout << "Screen X / 2: " << screenSize.x/2 << endl;
    middleBar.setPosition(screenSize.x/2,0);
    middleBar.setFillColor(sf::Color::Red);


    //Create player paddels
    Paddel playerOne(screenSize, Players::PlayerOne);
    Paddel playerTwo(screenSize, Players::PlayerTwo);

    //Use this for refresh time
    const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;
    sf::Time elapsedTime;

    //Should ping pong be running
    bool play = true;
    bool hasFocus = true;
    //int lastFocus = 1;
    bool firstLoad = true;

    //Game loop
    while (Window.isOpen())
    {
        //Clear screen and emplt Events
        Window.clear();
        sf::Event Event;

        //If window receves events
        while (Window.pollEvent(Event))
        {
            //Which events
            switch(Event.type)
            {
                    //Did the user close the window
                case sf::Event::Closed:
                    //Close it
                    Window.close();
                    break;
                    //If this is the second time or more this code has looped
                    if(!firstLoad)
                    {
                        //Did the window gane focus aka did the user click on it
                    case sf::Event::GainedFocus:
                    {
                        //Set variables
                        hasFocus = true;
                        play = true;
                        //Prevent this from haponing again
                        sf::Clock focusGap;
                    }
                    break;
                    //Did the window lose focus aka did the user select another application
                    case sf::Event::LostFocus:
                    {
                        hasFocus = false;
                        play = false;
                        sf::Clock focusGap;
                    }
                    break;
                default:
                    break;
                }
            }
        }
        if(hasFocus)
        {
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::P))
            {
                sf::Clock resume;
                if(play == true)
                {
                    play = false;
                    //Pause a bit to prevent toggling too fast
                    while(resume.getElapsedTime().asSeconds() < 0.15)
                        continue;
                } else {
                    //We are counting down
                    countdown = true;
                    play = true;
                    text.setPosition(screenSize.x/2, screenSize.y/2);
                    text.setString("3");
                    //Clear window and draw stuff
                    Window.clear();
                    Window.draw(playerOne);
                    Window.draw(playerTwo);
                    Window.draw(middleBar);
                    //Draw text
                    Window.draw(text);
                    Window.display();
                    //Play first beep (3)
                    countdownSound.play();
                    //Start clocks
                    sf::Clock soundRepeat;
                    sf::Clock soundGap;
                    int stages = 1;
                    //Run for 3 seconds (beeps 2,1 and GO!)
                    while(soundRepeat.getElapsedTime().asSeconds() < 4)
                    {
                        if(stages == 1)
                            text.setString("2");
                        else
                            text.setString("1");
                        //If 1 second has been since last beep and beeps are 2 or 1
                        if(soundGap.getElapsedTime().asSeconds() >= 1 && stages <= 2)
                        {
                            cout << stages << endl;
                            //Clear window and draw stuff
                            Window.clear();
                            Window.draw(playerOne);
                            Window.draw(playerTwo);
                            Window.draw(middleBar);
                            //Draw text
                            Window.draw(text);
                            Window.display();
                            //Play beep
                            countdownSound.play();
                            stages ++;
                            soundGap.restart();
                            //If 1 second has been since last beep and we want the GO! beep
                        } else if(soundGap.getElapsedTime().asSeconds() >= 1 && stages > 2){
                            text.setString("GO!");
                            text.setPosition((screenSize.x/2)-(text.getCharacterSize()-2),screenSize.y/2);
                            soundGap.restart(); //This prevents sound glitch
                            Window.clear();
                            Window.draw(playerOne);
                            Window.draw(playerTwo);
                            Window.draw(middleBar);
                            //Draw text
                            Window.draw(text);
                            Window.display();
                            startSound.play();
                        }
                    }

                }
            }
        }
        //Do refresh time calculation stuff
        elapsedTime = clock.restart();
        timeSinceLastUpdate += elapsedTime;

        //If user wants to play
        if (play)
        {
            //If screen should refresh
            if (timeSinceLastUpdate > TimePerFrame)
            {
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                    playerOne.moveUp();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                    playerOne.moveDown();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                    playerTwo.moveUp();
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
                    playerTwo.moveDown();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
                    play = false;
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                    Window.close();
            }
            //Draw stuff
            Window.draw(middleBar);
            Window.draw(playerOne);
            Window.draw(playerTwo);

            Window.display();

            //Everything has run at least once
            if(firstLoad)
                firstLoad = false;
        }
    }
}
\$\endgroup\$
5
\$\begingroup\$

few thing will come in handy for your game at this point

Resource Management

resources are heavyweight multimedia items, such as images, music themes, or fonts. "Heavyweight" refers to the fact that those objects occupy a lot of memory, and that operations on them, especially copying, perform slowly. This affects the way we use them in your application, as we try to restrict slow operations on them to a minimum. Therefore, we have decided which resources are required by the application, the next step is to investigate how long and by whom they are used. This allows us to decide how the resources are stored in the application, as well as who is responsible of loading and releasing them.

  1. We want to load the resource in advance, for example, at the time the game starts or the player begins a new level. In contrast to loading on demand (as soon as a resource is needed), this approach has the advantage that possible loading times occur in the beginning and not during a game. Therefore, the game itself remains fluent and is not interrupted because of resources
  2. When resources are likely to not be needed anymore, we can release them and free the memory. This is usually the case at the end of a level or when the application is quit. We do not want to release resources too early if we risk reloading them shortly after. For example, we do not release the explosion sound buffer as soon as the sound effect is over, because the next explosion may follow a few seconds later.

Our goal is to encapsulate the just mentioned functionality into a class that relieves us from managing resources again and again. For resource management, the C++ idiom Resource Acquisition Is Initialization (RAII) comes in handy. like following approach

ResourceHolder.h

#ifndef RESOURCEHOLDER_H
#define RESOURCEHOLDER_H

#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>


template <typename Resource, typename Identifier>
class ResourceHolder
{
public:
    void load(Identifier id, const std::string& filename);

    Resource& get(Identifier id);
    const Resource& get(Identifier id) const;

private:
    void insertResource(Identifier id, std::unique_ptr<Resource> resource);

    std::map<Identifier, std::unique_ptr<Resource>> mResourceMap;
};

#include "ResourceHolder.inl"
#endif // RESOURCEHOLDER_H

ResourceHolder.inl

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename)
{
    // Create and load resource
    // it seems codeX doesn't support std::make_unique
    std::unique_ptr<Resource> resource(new Resource());
    if (!resource->loadFromFile(filename))
        throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);

    // If loading successful, insert resource to map
    insertResource(id, std::move(resource));
}

template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
    auto found = mResourceMap.find(id);
    assert(found != mResourceMap.end());

    return *found->second;
}

template <typename Resource, typename Identifier>
const Resource& ResourceHolder<Resource, Identifier>::get(Identifier id) const
{
    auto found = mResourceMap.find(id);
    assert(found != mResourceMap.end());

    return *found->second;
}

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::insertResource(Identifier id, std::unique_ptr<Resource> resource)
{
    // Insert and check success
    auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);
}
  1. There must be a possibility to get a reference to a certain resource after it has been loaded—using a resource identifier. This identifier (ID) could be the file path as a std::string. This has some disadvantages: all classes that use a resource must hardcode the path, so if it changes, a lot of code needs to be refactored. Strings are also quite error-prone regarding typographic or case errors. An alternative to strings are enums, where each enumerator denotes an ID. Since an enum has a predefined set of possible states, we get some compile-time safety, and we can handle the paths in a central place. The best approach is going to be like following script:

ResourceIdentifiers.h

#ifndef RESOURCEIDENTIFIERS_H
#define RESOURCEIDENTIFIERS_H


// Forward declaration of SFML classes
namespace sf
{
    class SoundBuffer;
}

enum class Sounds
{
    Countdown,
    Start
};

// Forward declaration and a few type definitions
template <typename Resource, typename Identifier>
class ResourceHolder;

using SoundHolder = ResourceHolder<sf::SoundBuffer, Sounds>;

#endif // RESOURCEIDENTIFIERS_H

Remove compiler warning

few warnings alert due to convert from float to unsigned int, this could be solved easily by creating nums for screen size, it seems you don't like enum class, so here is an alternative approach

namespace Screen
{
    enum Size
    {
        Width = 800,
        Height = 600
    };
}

final code

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
//#include "ResourcePath.hpp"
#include "ResourceHolder.h"
#include "ResourceIdentifiers.h"
#include <cstdint>
#include <iostream>

using std::cout;
using std::endl;

//Version
std::string ver = "V0.3";
//Create Text and font variables
sf::Text text;
sf::Font font;

// for eliminating magic numbers
enum class Players
{
    PlayerOne,
    PlayerTwo
};


namespace Screen
{
    enum Size
    {
        Width = 800,
        Height = 600
    };
}


//Make this class Drawable
class Paddel : public sf::Drawable
{
    // for member data perfered to be started with m prefix
    float mSpeed;
    sf::Vector2f mBorder;
    sf::Vector2f mPosition;
    sf::Vector2u mScreenSize;
    sf::RectangleShape mShape;
public:

    Paddel(Screen::Size screenSize, Players player)
        // member data perfered be initilaized as contructor's initilaized list
        : mScreenSize(Screen::Width, Screen::Height)
        , mBorder(8, 6)
        , mSpeed(5.f)
    {
        sf::Vector2f size = sf::Vector2f(20, 100);

        //Which player
        switch (player)
        {
        case Players::PlayerOne:
            //If player one set position to border
            mPosition = mBorder;
            break;
        default:
            //Else set positionX to (screenWidth - paddelWidth) - borderWidth
            mPosition.x = (mScreenSize.x - size.x) - mBorder.x;
            //positionY = borderY
            mPosition.y = mBorder.y;
        }
        //Set size and position of drawable shape
        mShape.setSize(size);
        mShape.setPosition(mPosition);
    }

    void moveUp()
    {
        //Get closer to 0 (Top of screen)
        mPosition.y -= mSpeed;
        //If gone too far add speed
        if (mPosition.y < mBorder.y)
            mPosition.y += mSpeed;
        mShape.setPosition(mPosition);
    }

    //Do the same opposite way round
    void moveDown()
    {
        mPosition.y += mSpeed;
        if (mPosition.y >(mScreenSize.y - mBorder.y) - mShape.getSize().y)
            mPosition.y -= mSpeed;
        mShape.setPosition(mPosition);
    }

    //Override sf::Drawable function
    void draw(sf::RenderTarget& target, sf::RenderStates) const override
    {
        // sf::RectangleShape has its own defualt RenderStates
        target.draw(mShape); // <-- NOTE : shorter version 
    }
};

int main()
{
    //Initilise screen size and Window
    sf::RenderWindow Window(sf::VideoMode(Screen::Width, Screen::Height), "Ping Pong " + ver, sf::Style::Titlebar | sf::Style::Close);

    //Create Text
    bool enableText = true;
    bool countdown = false;
    // Try to load font resources
    try
    {
        font.loadFromFile(/*resourcePath() +*/ "Pacifico.ttf");
    }
    catch (std::runtime_error& e)
    {
        cout << "Exception: " << e.what() << std::endl;
        enableText = false;
    }

    text.setFont(font);
    text.setCharacterSize(50);
    text.setColor(sf::Color::White);

    //Limit frame rate and enable vertical sync
    Window.setFramerateLimit(60);
    Window.setVerticalSyncEnabled(true);


    //Load resume sound
    SoundHolder sounds;
    // Try to load resources
    try
    {
        sounds.load(Sounds::Countdown, /*resourcePath() +*/ "3_2_1.ogg");
        sounds.load(Sounds::Start, /*resourcePath() +*/ "Go.ogg");
    }
    catch (std::runtime_error& e)
    {
        cout << "Exception: " << e.what() << std::endl;
        return 1;
    }

    sf::Sound countdownSound;
    sf::Sound startSound;
    countdownSound.setBuffer(sounds.get(Sounds::Countdown));
    startSound.setBuffer(sounds.get(Sounds::Start));


    //Seporate left from right
    sf::RectangleShape middleBar(sf::Vector2f(10, Screen::Height));
    cout << "Screen X / 2: " << Screen::Width / 2 << endl;
    middleBar.setPosition(Screen::Width / 2, 0);
    middleBar.setFillColor(sf::Color::Red);


    //Create player paddels
    Paddel playerOne(Screen::Size(), Players::PlayerOne);
    Paddel playerTwo(Screen::Size(), Players::PlayerTwo);

    //Use this for refresh time
    const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;
    sf::Time elapsedTime;

    //Should ping pong be running
    bool play = true;
    bool hasFocus = true;
    //int lastFocus = 1;
    bool firstLoad = true;

    //Game loop
    while (Window.isOpen())
    {
        //Clear screen and emplt Events
        Window.clear();
        sf::Event Event;

        //If window receves events
        while (Window.pollEvent(Event))
        {
            //Which events
            switch (Event.type)
            {
                //Did the user close the window
            case sf::Event::Closed:
                //Close it
                Window.close();
                break;
                //If this is the second time or more this code has looped
                if (!firstLoad)
                {
                    //Did the window gane focus aka did the user click on it
                    case sf::Event::GainedFocus:
                    {
                        //Set variables
                        hasFocus = true;
                        play = true;
                        //Prevent this from haponing again
                        sf::Clock focusGap;
                    }
                        break;
                        //Did the window lose focus aka did the user select another application
                    case sf::Event::LostFocus:
                    {
                        hasFocus = false;
                        play = false;
                        sf::Clock focusGap;
                    }
                        break;
                    default:
                        break;
                }
            }
        }
        if (hasFocus)
        {
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
            {
                sf::Clock resume;
                if (play == true)
                {
                    play = false;
                    //Pause a bit to prevent toggling too fast
                    while (resume.getElapsedTime().asSeconds() < 0.15)
                        continue;
                }
                else {
                    //We are counting down
                    countdown = true;
                    play = true;
                    text.setPosition(Screen::Width / 2, Screen::Height / 2);
                    text.setString("3");
                    //Clear window and draw stuff
                    Window.clear();
                    Window.draw(playerOne);
                    Window.draw(playerTwo);
                    Window.draw(middleBar);
                    //Draw text
                    Window.draw(text);
                    Window.display();
                    //Play first beep (3)
                    countdownSound.play();
                    //Start clocks
                    sf::Clock soundRepeat;
                    sf::Clock soundGap;
                    int stages = 1;
                    //Run for 3 seconds (beeps 2,1 and GO!)
                    while (soundRepeat.getElapsedTime().asSeconds() < 4)
                    {
                        if (stages == 1)
                            text.setString("2");
                        else
                            text.setString("1");
                        //If 1 second has been since last beep and beeps are 2 or 1
                        if (soundGap.getElapsedTime().asSeconds() >= 1 && stages <= 2)
                        {
                            cout << stages << endl;
                            //Clear window and draw stuff
                            Window.clear();
                            Window.draw(playerOne);
                            Window.draw(playerTwo);
                            Window.draw(middleBar);
                            //Draw text
                            Window.draw(text);
                            Window.display();
                            //Play beep
                            countdownSound.play();
                            stages++;
                            soundGap.restart();
                            //If 1 second has been since last beep and we want the GO! beep
                        }
                        else if (soundGap.getElapsedTime().asSeconds() >= 1 && stages > 2){
                            text.setString("GO!");
                            text.setPosition((Screen::Width / 2.f) - (text.getCharacterSize() - 2), Screen::Height / 2.f); // <-- we divided by 2.f to remove the warning
                            soundGap.restart(); //This prevents sound glitch
                            Window.clear();
                            Window.draw(playerOne);
                            Window.draw(playerTwo);
                            Window.draw(middleBar);
                            //Draw text
                            Window.draw(text);
                            Window.display();
                            startSound.play();
                        }
                    }

                }
            }
        }
        //Do refresh time calculation stuff
        elapsedTime = clock.restart();
        timeSinceLastUpdate += elapsedTime;

        //If user wants to play
        if (play)
        {
            //If screen should refresh
            if (timeSinceLastUpdate > TimePerFrame)
            {
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                    playerOne.moveUp();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                    playerOne.moveDown();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                    playerTwo.moveUp();
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
                    playerTwo.moveDown();

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
                    play = false;
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                    Window.close();
            }
            //Draw stuff
            Window.draw(middleBar);
            Window.draw(playerOne);
            Window.draw(playerTwo);

            Window.display();

            //Everything has run at least once
            if (firstLoad)
                firstLoad = false;
        }
    }
}

UPDATE

avoid global variables

we need to remove global variables sf::Text text and sf::Font font that can be done by create new class called Game. Also, it is helpful for making game logic easier to read instead of doing our logic in the main().

Game.h

#ifndef GAME_H
#define GAME_H

#include "ResourceHolder.h"
#include "ResourceIdentifiers.h"
#include "Paddle.h"

#include <SFML/System/Time.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Audio.hpp>

class Game : private sf::NonCopyable
{
public:
    Game();
    void run();


private:
    void processEvents();
    void update(sf::Time elapsedTime);
    void render();

    void handlePlayerOneInput(sf::Keyboard::Key key, bool isPressed);
    void handlePlayerTwoInput(sf::Keyboard::Key key, bool isPressed);
    void loadResource();
    static const float      PlayerSpeed;
    static const sf::Time   TimePerFrame;
    std::string             version;

    sf::RenderWindow        mWindow;
    SoundHolder             mSounds;
    sf::Sound               mCountdownSound;
    sf::Sound               mStartSound;
    sf::RectangleShape      mMiddleBar;

    Paddle                  mPlayerOne;
    Paddle                  mPlayerTwo;

    sf::Font                mFont;
    sf::Text                mText;

    bool                    mIsMovingUp;
    bool                    mIsMovingDown;

    bool                    mIsMovingUpTwo;
    bool                    mIsMovingDownTwo;

};

#endif // GAME_H

Game.cpp

#include "Game.h"
#include "Screen.h"
#include <iostream>
#include "ResourcePath.hpp"
#include <SFML/Window/Event.hpp>

const float Game::PlayerSpeed = 100.f;
const sf::Time Game::TimePerFrame = sf::seconds(1.f / 60.f);

Game::Game()
    : version("V0.3")
    , mWindow(sf::VideoMode(Screen::Width, Screen::Height), "Ping Pong " + version , sf::Style::Close)
    , mPlayerOne(Screen::Size(), Players::PlayerOne)
    , mPlayerTwo(Screen::Size(), Players::PlayerTwo)
    , mMiddleBar(sf::Vector2f(10, Screen::Height))
    , mFont()
    , mText()
    , mCountdownSound()
    , mStartSound()
    , mIsMovingUp(false)
    , mIsMovingDown(false)
    , mIsMovingUpTwo(false)
    , mIsMovingDownTwo(false)
{
    mFont.loadFromFile(resourcePath() + "Pacifico.ttf");

    mText.setFont(mFont);
    mText.setCharacterSize(50);
    mText.setColor(sf::Color::White);

    loadResource();

    mCountdownSound.setBuffer(mSounds.get(Sounds::Countdown));
    mStartSound.setBuffer(mSounds.get(Sounds::Start));

    //Separate left from right
    mMiddleBar.setPosition(Screen::Width / 2, 0);
    mMiddleBar.setFillColor(sf::Color::Red);

}

void Game::loadResource()
{
    try
    {
        mSounds.load(Sounds::Countdown, resourcePath() + "3_2_1.ogg");
        mSounds.load(Sounds::Start, resourcePath() + "Go.ogg");
    }
    catch (std::runtime_error& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
}

void Game::run()
{
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;

    while (mWindow.isOpen())
    {
        sf::Time elapsedTime = clock.restart();
        timeSinceLastUpdate += elapsedTime;
        while (timeSinceLastUpdate > TimePerFrame)
        {
            timeSinceLastUpdate -= TimePerFrame;

            processEvents();
            update(TimePerFrame);

        }

        render();
    }
}

void Game::processEvents()
{
    sf::Event event;
    while (mWindow.pollEvent(event))
    {
        switch (event.type)
        {
        case sf::Event::KeyPressed:
            handlePlayerOneInput(event.key.code, true);
            handlePlayerTwoInput(event.key.code, true);
            break;

        case sf::Event::KeyReleased:
            handlePlayerOneInput(event.key.code, false);
            handlePlayerTwoInput(event.key.code, false);
            break;
        // no need for Focus
        case sf::Event::Closed:
            mWindow.close();
            break;
        }
    }
}

void Game::update(sf::Time elapsedTime)
{
    sf::Vector2f movement(0.f, 0.f);

    if (mIsMovingUp)
    {
        movement.y -= PlayerSpeed;
    }

    if (mIsMovingDown)
    {
        movement.y += PlayerSpeed;
    }

    mPlayerOne.move(movement * elapsedTime.asSeconds());

    // Player two
    sf::Vector2f movement2(0.f, 0.f);

    if (mIsMovingUpTwo)
    {
        movement2.y -= PlayerSpeed;
    }

    if (mIsMovingDownTwo)
    {
        movement2.y += PlayerSpeed;
    }

    mPlayerTwo.move(movement2 * elapsedTime.asSeconds());
}

void Game::render()
{
    mWindow.clear();
    mWindow.draw(mMiddleBar);

    mWindow.draw(mPlayerOne);
    mWindow.draw(mPlayerTwo);

    mWindow.setView(mWindow.getDefaultView()); // for resizing 
    mWindow.display();
}

void Game::handlePlayerOneInput(sf::Keyboard::Key key, bool isPressed)
{

    if (key == sf::Keyboard::Up)
    {
        mIsMovingUp = isPressed;
    }
    else if (key == sf::Keyboard::Down)
    {
        mIsMovingDown = isPressed;
    }
}

void Game::handlePlayerTwoInput(sf::Keyboard::Key key, bool isPressed)
{
    if (key == sf::Keyboard::W)
    {
        mIsMovingUpTwo = isPressed;
    }
    else if (key == sf::Keyboard::S)
    {
        mIsMovingDownTwo = isPressed;
    }
}

Paddle.h

#ifndef PADDLE_H
#define PADDLE_H

#include "Screen.h"
#include "PlayerIdenitifiers.h"

#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/RectangleShape.hpp>

// add sf::Transformable to get advantage of move() function
class Paddle : public sf::Drawable, public sf::Transformable
{
public:

    Paddle(Screen::Size screenSize, Players player);

    void draw(sf::RenderTarget& target, sf::RenderStates) const override;

private:
    sf::Vector2f mBorder;
    sf::Vector2f mPosition;
    sf::Vector2u mScreenSize;
    sf::RectangleShape mShape;
};

#endif // PADDLE_H

Paddle.cpp

#include "Paddle.h"

Paddle::Paddle(Screen::Size screenSize, Players player)
: mScreenSize(Screen::Width, Screen::Height)
, mBorder(8, 6)
{
    sf::Vector2f size = sf::Vector2f(20, 100);

    switch (player)
    {
    case Players::PlayerOne:

        mPosition = mBorder;
        break;
    default:
        mPosition.x = (mScreenSize.x - size.x) - mBorder.x;
        mPosition.y = mBorder.y;
    }

    mShape.setSize(size);
    mShape.setPosition(mPosition);
}

void Paddle::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    // Apply transform of current Player
    states.transform *= getTransform();
    target.draw(mShape, states);
}

PlayerIdentifiers.h

#ifndef PLAYERIDENTIFIERS_H
#define PLAYERIDENTIFIERS_H

enum class Players
{
    PlayerOne,
    PlayerTwo
};

#endif // PLAYERIDENTIFIERS_H

Screen.h

#ifndef SCREEN_H
#define SCREEN_H

namespace Screen
{
    enum Size
    {
        Width = 800,
        Height = 600
    };
}

#endif

main.cpp

#include "Game.h"

int main()
{
    Game game;
    game.run();
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for helping me to make my code more efficient. When compiling it with Xcode I get a error in the file ResourceHolder.ini which says no member named 'make_unique' in namespace 'std' on line std::unique_ptr<Resource> resource(std::make_unique<Resource>()); Also, on the same line there is another error 'Resource' does not refer to a value and expected expression. Do you know how to fix this? Also could you please explain the code to me? \$\endgroup\$ – iProgram Feb 19 '15 at 10:06
  • \$\begingroup\$ @aPyDeveloper .. i edited my answer. it should work now \$\endgroup\$ – MORTAL Feb 19 '15 at 10:26
  • \$\begingroup\$ Thank you for your edit. The only issue I get now is Failed to load font "Pacifico.ttf" (failed to create the font face)``Failed to open sound file "3_2_1.ogg" (System error : No such file or directory.)``Exception: ResourceHolder::load - Failed to load 3_2_1.ogg. How would I fix this? Will I need to do ResourcePath() + <file name>?Thanks \$\endgroup\$ – iProgram Feb 19 '15 at 11:22
  • \$\begingroup\$ @aPyDeveloper ... feel free to use ResourcePath() then. please let me know if it doesn't work. \$\endgroup\$ – MORTAL Feb 19 '15 at 11:52
  • 1
    \$\begingroup\$ @aPyDeveloper .. great. if you have any question don't hesitate to ask \$\endgroup\$ – MORTAL Feb 19 '15 at 12:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.