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;
}
}
}
1 Answer 1
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.
- 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
- 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);
}
- 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();
}
-
\$\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 saysno member named 'make_unique' in namespace 'std'
on linestd::unique_ptr<Resource> resource(std::make_unique<Resource>());
Also, on the same line there is another error'Resource' does not refer to a value
andexpected expression
. Do you know how to fix this? Also could you please explain the code to me? \$\endgroup\$iProgram– iProgram2015年02月19日 10:06:15 +00:00Commented Feb 19, 2015 at 10:06 -
\$\begingroup\$ @aPyDeveloper .. i edited my answer. it should work now \$\endgroup\$MORTAL– MORTAL2015年02月19日 10:26:54 +00:00Commented Feb 19, 2015 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 doResourcePath() + <file name>
?Thanks \$\endgroup\$iProgram– iProgram2015年02月19日 11:22:36 +00:00Commented Feb 19, 2015 at 11:22 -
\$\begingroup\$ @aPyDeveloper ... feel free to use
ResourcePath()
then. please let me know if it doesn't work. \$\endgroup\$MORTAL– MORTAL2015年02月19日 11:52:55 +00:00Commented Feb 19, 2015 at 11:52 -
1\$\begingroup\$ @aPyDeveloper .. great. if you have any question don't hesitate to ask \$\endgroup\$MORTAL– MORTAL2015年02月19日 12:06:47 +00:00Commented Feb 19, 2015 at 12:06