I have worked on a SFML Game/Game-Engine Project for 2 months now and am looking for some feedback in terms of...
- Is this a valid approach for a Game class?
- Are there any glaring C++ coding practice mistakes?
The following code is from my Game class which is the main organizational unit of the Game/Engine. I use a Scene(State) approach, meaning that every Scene/Screen in the Game is a closed of unit with it's Entities being updated from the Game class.
Game.h
#ifndef GAME_H
#define GAME_H
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <string>
#include <vector>
#include <map>
#include "../game/Entity.h"
#include "../game/FpsCounter.h"
#include "../game/EScene.h"
#include "../game/Scene.h"
#include "../game/InputHandler.h"
#include "../game/WindowStyle.h"
#include "../menu/VBox.h"
#include "../menu/HBox.h"
#include "../menu/ButtonMenu.h"
#include "../animation/TranslateAnimation.h"
namespace ProjectSpace
{
/**
* @brief Core class of the Game. Contains main Game-Loop and Event-Loop.
*/
class Game
{
public:
/**
* @brief Constructs a Game detecting the monitor's resolution automatically.
*
* @param[in] windowTitle Title of the Game's window.
*/
Game(std::string windowTitle, WindowStyle style = DEFAULT);
/**
* @brief Constructs a Game.
*
* @param[in] screenWidth Width of the Game's window.
* @param[in] screenHeight Height of the Game's window.
* @param[in] windowTitle Title of the Game's window.
*/
Game(unsigned int screenWidth, unsigned int screenHeight, std::string windowTitle, WindowStyle style = DEFAULT);
/**
* @brief Destroys every Scene of the Game.
*/
~Game();
/**
* @brief Starts the Game/Game-Loop.
*/
void start();
/**
* @brief Sets the current scene.
*
* @param[in] scene The scene
*/
void setCurrentScene(EScene scene);
private:
/**
* @brief Helper function for initializing everything before starting the Game. Creates all Sprites and stuff...
*/
void init();
sf::RenderWindow window; // Window of the Game.
FpsCounter fpsCounter; // Fps Counter of the Game.
InputHandler inputHandler; // InputHandler for input Actions that are always available in the game (Not Scene dependant).
VBox menu;
std::map<EScene, Scene*> scenes; // All Scenes of the Game.
Scene* currentScene; // Scene that is currently active (is drawn and updated).
TranslateAnimation* menuForward;
TranslateAnimation* menuBackward;
ButtonMenu* buttonMenu;
float windowWidth;
float windowHeight;
};
}
#endif
Game.cpp
#include "Game.h"
#include <SFML/System/Clock.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Window/VideoMode.hpp>
#include "../menu/TextBox.h"
#include "../animation/FadeAnimation.h"
#include "../menu/Button.h"
#include "../menu/ButtonMenu.h"
#include "InputHandler.h"
#include "Scene.h"
#include "Factory.h"
namespace ProjectSpace
{
Game::Game(std::string windowTitle, WindowStyle style)
: window{sf::VideoMode{sf::VideoMode::getDesktopMode().width, sf::VideoMode::getDesktopMode().height}, windowTitle, style},
fpsCounter{"rsrc/fonts/arial.ttf"}, currentScene{nullptr}, windowWidth{(float)sf::VideoMode::getDesktopMode().width},
windowHeight{(float)sf::VideoMode::getDesktopMode().height}
{
window.setFramerateLimit(60);
init();
}
Game::Game(unsigned int screenWidth, unsigned int screenHeight, std::string windowTitle, WindowStyle style)
: window{sf::VideoMode{screenWidth, screenHeight}, windowTitle, style}, fpsCounter{"rsrc/fonts/arial.ttf"},
currentScene{nullptr}
{
// Makes sure that not more than 60 Frames per Second are calculated. Save Ressources.
window.setFramerateLimit(60);
init();
}
Game::~Game()
{
for (auto const& p : scenes)
{
delete p.second;
}
}
void Game::start()
{
sf::Clock clock{};
// This is main Game-Loop
while (window.isOpen())
{
// At the start of each iteration "clock.restart()" will return the time that the last iteration took.
// This is so that all objects of the current iteration know how much time has passed since the last frame.
sf::Time time = clock.restart();
float frameTime = time.asSeconds();
// std::cout << "frameTime: " << frameTime << std::endl;
// This is the Event-Loop (Hier muessen wir nochmal gucken, was wir damit anstellen koennen. Ist wohl ziemlich wichtig in SFML.)
sf::Event event;
while (window.pollEvent(event))
{
// For example by pressing the Close-Button on a Window, a Closed Event is created.
if (event.type == sf::Event::Closed)
{
window.close();
}
}
currentScene->update(time);
inputHandler.update(time);
fpsCounter.update(time);
menu.update(time);
menuForward->update(time);
menuBackward->update(time);
buttonMenu->update(time);
// Default way of drawing stuff in SFML. Clear the window, draw you'r stuff and then display it.
window.clear();
window.draw(*currentScene);
window.draw(fpsCounter);
window.draw(menu);
window.display();
}
}
void Game::setCurrentScene(EScene scene)
{
if (scenes.count(scene) == 0)
{
std::cout << "@Game::setCurrentScene(): Given scene is not known to Game." << std::endl;
return;
}
currentScene = scenes[scene];
}
void Game::init()
{
// Creating Levels
scenes[EScene::DEBUG] = Factory::CREATE_DEBUG_SCENE(window);
scenes[EScene::LEVEL_ONE] = Factory::CREATE_DEBUG_SCENE_2(window);
currentScene = scenes[EScene::DEBUG];
// Creating Menu
Button* btn = new Button{[this]()
{
window.close();
}, window, "EXIT"};
Button* btn2 = new Button{[this]()
{
window.close();
}, window, "EXIT"};
Button* btn3 = new Button{[this]()
{
setCurrentScene(EScene::DEBUG);
}, window, "Debug Level"};
Button* btn4 = new Button{[this]()
{
setCurrentScene(EScene::LEVEL_ONE);
}, window, "Level 1"};
menu.addMenuElements({btn, btn2, btn3, btn4});
menu.setPosition(-10, 50);
menu.setSpacing(5);
buttonMenu = new ButtonMenu{{btn, btn2, btn3, btn4}, &inputHandler};
menuForward = new TranslateAnimation{&menu, sf::Vector2f{-250, 50}, sf::Vector2f{-10, 50}, 0.5f};
menuBackward = new TranslateAnimation{&menu, sf::Vector2f{-10, 50}, sf::Vector2f{-250, 50}, 0.5f};
// Setting up input handler
inputHandler.add([this]()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
menuBackward->stop();
menuForward->start();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::R))
{
menuForward->stop();
menuBackward->start();
}
});
}
}
GitHub Repo: https://github.com/Dante3085/ProjectSpace