6
\$\begingroup\$

I've always loved making games and using SFML, and up until a few days ago, I've had to re-write code over and over again. So I decided to write a game engine so I can reuse my code.

I've never done something like this before. I've read about it, and I watched a few videos. I'd love if you could look around, give me some ideas. Keep in mind I've only worked on this for less than a week.

GitHub

So far, I've just re-skinned SFML functions and classes using my own classes, but I've changed some functions around and made things a bit simpler.

Is it, for example, any good creating my own Sprite class that is basically a re-skin of the SFML sprite class, with perhaps some added functionality, or should my resource manager just return an SFML sprite, which the user of the engine can just use all the in-built functionality for the sf::Sprite?

Here is a very simple program that creates a sprite and a text and draws them on the screen. And we can use WASD to move the sprite.

#include <Tengine2D\ResourceManager.hpp>
#include <Tengine2D\Window.hpp>
#include <Tengine2D\Keyboard.hpp>
#include <Tengine2D\Clock.hpp>
#include <Tengine2D\Text.hpp>
#include <Tengine2D\Vector2.hpp>
#include <Tengine2D\GameState.hpp>
#include <Tengine2D\Camera.hpp>

#include <iostream>

int screenWidth = 1600;
int screenHeight = 640;

t2d::Clock _clock;
t2d::GameState gameState;

int main()
{

    t2d::Window _window;
    _window.create(screenWidth, screenHeight, "BELLO!");

    gameState = t2d::GameState::PLAY;

    // Text
    t2d::ResourceManager::createText("Text", "Fonts/times.ttf");
    t2d::ResourceManager::getText("Text").setCharacterSize(50);
    t2d::ResourceManager::getText("Text").setStyle(t2d::TextStyle::Bold);
    t2d::ResourceManager::getText("Text").setColor(t2d::Color::Magenta);
    t2d::ResourceManager::getText("Text").setPosition(screenWidth / 4.0f, screenHeight / 2.0f);
    t2d::ResourceManager::getText("Text").setString("Hello World");

    // Sprite
    t2d::ResourceManager::createSprite("RedPlayer", "Sprites/RedPlayer.png");
    t2d::ResourceManager::getSprite("RedPlayer").setPosition(screenWidth / 4.0f, screenHeight / 2.0f);

    while (gameState != t2d::GameState::QUIT)
    {
        float delta = _clock.restart().asSeconds();

        while (_window.pollEvent())
        {
            if(t2d::Keyboard::keyPressed(t2d::Keyboard::Escape))
            {
                gameState = t2d::GameState::QUIT;
            }
        }

        if (t2d::Keyboard::isKeyPressed(t2d::Keyboard::D))
        {
            t2d::ResourceManager::getSprite("RedPlayer").translate(400 * delta, 0.0f);
        }
        if (t2d::Keyboard::isKeyPressed(t2d::Keyboard::A))
        {
            t2d::ResourceManager::getSprite("RedPlayer").translate(-400 * delta, 0.0f);
        }
        if (t2d::Keyboard::isKeyPressed(t2d::Keyboard::S))
        {
            t2d::ResourceManager::getSprite("RedPlayer").translate(0.0f, 400 * delta);
        }
        if (t2d::Keyboard::isKeyPressed(t2d::Keyboard::W))
        {
            t2d::ResourceManager::getSprite("RedPlayer").translate(0.0f, -400 * delta);
        }

        _window.clear(t2d::Color(150, 150, 5, 255));
        _window.draw(t2d::ResourceManager::getSprite("RedPlayer"));
        _window.draw(t2d::ResourceManager::getText("Text"));
        _window.display();
    }

    return 0;
}
\$\endgroup\$
  • 3
    \$\begingroup\$ I have literally never seen an include statement use "\" (as opposed to "/") until today... \$\endgroup\$ – Dair Oct 15 '16 at 22:42
  • \$\begingroup\$ What world do you live in then @Dair? Black-slash usage in include directives and paths are extremely common for Windows users, especially since VS uses (or used to?) back-slashes by default. But it's still not recommended, as it will cause issues on other systems. \$\endgroup\$ – Lukas Jan 15 '18 at 17:51
  • 1
    \$\begingroup\$ @Lukas I don't use windows so there's that haha. \$\endgroup\$ – Dair Jan 15 '18 at 18:44
2
\$\begingroup\$

Personally, I see no gain in abstracting SFML at this stage. In general I only see two situations where abstracting such a fundamental library really makes sense and that would be for:

  1. If you want to fully test your code. In that case you need to be able to decouple from the "system" and thus from SFML.
  2. If your engine has advanced to a stage where it isn't just a cheap tech demo, but actually something real, used for some real games. Only then does it make sense to think about, whether it would make sense to refactor the code base to potentially allow for different "backends".

Additionally, option 2 is rarely possible to do cleanly, since all the frameworks offer a different set of functionalities and switching between them with a single API on top, doesn't really work and limits the set of features to be used of said library.

As for your code and question, there's a reason why SFML splits font and text, texture and sprites, soundbuffer and sound, etc. The point is that you have a heavy resource (font, texture, soundbuffer) and a light "instance" object (text, sprite, sound). That way you can move, copy, manipulate the light instances all you want and not run into any issues performance wise, while the heavy resources need to be loaded once and then kept alive as long as the light instances are using it.

Your resource manager as such shouldn't hold on to text objects or sprites and instead just handle the heavy resources and ensure that they stay alive as long as they're used. I recommend to take a look at Thor's resource management well and Thor in general as it's a nice addition to SFML.

How you want to store text or sprites is up to you and depends on the kind of game. For most simple games, it's enough to save them directly in a game state class and if you need lots, then just use a std::vector but keep in mind the advantages and limitations of used containers.

\$\endgroup\$
  • \$\begingroup\$ On the contrary, I think abstracting SFML is a great idea. New projects have a tendency to borrow from old projects, so why not do it right the first time? Being able to properly test the full code is a must if the project becomes a couple of magnitudes larger. \$\endgroup\$ – Mast Jan 15 '18 at 20:27
  • 1
    \$\begingroup\$ If, if, if. That's the problem. Sure it's a good idea, but if you never make it to a functional engine that real games are built on, what's the point? Write games, not engines. I seriously have yet to see a released game that took the engine-first approach. Over-engineering is one of the biggest issues with aspiring game devs and "let's do it properly from the start"-mentality goes exactly in the direction. YAGNI (You Aren't Gonna Need It) and KISS (Keep It Simple Stupid) are way better approaches. \$\endgroup\$ – Lukas Jan 15 '18 at 22:58
  • \$\begingroup\$ I'm well aware of those. But in this case, it's already there. Don't spend effort trying to hack it out again. That's a guaranteed waste of time. \$\endgroup\$ – Mast Jan 16 '18 at 6:06
  • \$\begingroup\$ Also, I think YAGNI is overrated if you don't have a clear long-term plan. With short projects like this, over-engineered is better than code riddled with bugs. Too much crap code ends up in long-term projects after all. \$\endgroup\$ – Mast Jan 16 '18 at 6:13
  • \$\begingroup\$ @Lukas Actually it's a good idea to write a 2D engine for practice. It helps you to understand how things work and why a developer might have implemented it their way in an engine like Unity or Unreal. Take a component system for instance. When I first saw the UE component system it seemed very overcomplicated and unnessesary to me since I were like: "Pff, I can put that into an actor". Since I know, why there is a component system now, I am a lot less annoyed by components and actually I use them more often and in a better way. For a serious project you should always use a prebuild engine. \$\endgroup\$ – Mango Apr 10 '18 at 6:23

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.