1
\$\begingroup\$

I am planning to create a cRPG game engine that would allow to easily create games like Baldur's Gate. I already tried to do that once, but my knowledge was vastly insufficient at the time. I settled for C++, SFML and TGUI (a GUI library), as this is what I have most experience with. I've read this article on game engine architecture, and I have decided to follow that system's architecture (event/message driven), because I know how much of a unworkable hairball my previous attempt was.

I am mindmapping the concept and its architecture for the past few days now (something I didn't do at all previously); and I came up to the point of how I want to divide (or do I?) the gameplay and rendering. The rendering part is actually very simple, as SFML does bulk of the job. I concluded that I don't even know what I could place in the rendering system apart from drawing sprites. I also can't seem to find more resources about game engine systems, because every time I search for that I get system requirements for unity and unreal.

If I decide to go with the rendering system, what would be the best way to store and send sprites? I suppose that I need to make a sprite resource manager. If so, how is the best way to send them to the render system without coupling things too much? Or perhaps the sprite resource manager should reside in the render system itself?

EDIT: As of current, the planned architecture is divided into 6 systems (Input, Audio, Logging, GUI, Gameplay, Render). I will use Events for decoupling. The problem with this design is that i will have at least 3 direct connections between GUI, Gameplay and Render. Those are:

  1. Between GUI and Render. The TGUI library introduces a TGUI Window class that inherits from SFML one, and can be used as one. In my case, it will be a part of the Render system. Hovewer, the GUI system will need to have a pointer to the Window to register GUI widgets.
  2. Between Gameplay and Render. Where applicable, Actor class will have a AnimatedSprite, which i will implement on my own. It will be a part of the Gameplay system, and will have a pointer to the sprite in resource manager (in the Render system); it will just cut it depending on animation/frame. In some cases, when animations are not required, the Actor class will just have pointer to a normal sprite. To get those pointers a function in resource manager will be called from the Gameplay system.

The latter of two is the major problem; as the function could be called at any time. But, it would be a simple function like sf::Sprite * getResource(std::string name) so i think it wont present all those big bad problems of coupling.

\$\endgroup\$
  • 2
    \$\begingroup\$ There are as many valid ways to build a game engine as there are people building them. If the way you're working seems OK to you then it's probably OK. Can you give us a concrete example of the architecture you're considering currently, and where you think the coupling is too strong? With that in hand, we can propose ways to help you reduce unnecessary coupling. Without thar, we just don't know enough about your approach to know what fixes might help. \$\endgroup\$ – DMGregory Jul 27 at 17:57
  • \$\begingroup\$ Welcome to GDSE. Requests for resources are considered off-topic here, so I've removed that portion of your question. \$\endgroup\$ – Pikalek Jul 27 at 18:15
  • \$\begingroup\$ After reading my post for few times i am inclined towards the 'sprite resource manager inside render system' option. That means separate Gameplay and Render systems - that poses some questions. When i will initialize a new Actor (for example when summoning creatures), i will need to ask the resource manager - across two systems - for a sprite pointer directly. I believe this is one of the design problems laid out in the article i linked. Is there a way to circumvent this problem? Should i circumvent it at all? Thanks \$\endgroup\$ – Cyprinus Carpio Jul 27 at 18:42
  • 1
    \$\begingroup\$ That sounds more concrete than your original question..Edit your question to describe that issue in detail, and we can help you find solutions. \$\endgroup\$ – DMGregory Jul 27 at 20:00
1
\$\begingroup\$

Sprites

If I decide to go with the rendering system, what would be the best way to store and send sprites? I suppose that I need to make a sprite resource manager. If so, how is the best way to send them to the render system without coupling things too much? Or perhaps the sprite resource manager should reside in the render system itself?

I faced this problem recently. Look, there are three things:

  • Bitmaps
  • Textures
  • Sprites/Tiles

First, you will have a resource manager, and it is concerned with loading bitmaps. You then need to get texture objects (which belong to the rendering system):

  • Sometimes you want to copy bitmap to a texture directly. That is the common case.
  • Then, sometimes, you want to create bitmaps in memory instead of loading them from the resource manager, and then copy those to textures. Although, you probably will not need to do that (it can be useful when you are rolling your own GUI).
  • Occasionally you want to copy only parts of the bitmap to textures. That is the rare case, in fact, I do think you will be doing it at all.

Finally, a texture could have multiple sprites/tiles that you can render separately by using texture coordinates. That all happens inside the rendering system, completely hidden from the rest of the solution.

Note: You can decide to have a fixed size for all sprites set at the start of the rendering system. Having all the sprites be of that size will save passing the size of the sprites each time. On the other hand, it is not a bad idea to have a similar solution to display overlays of custom size. Although, I guess the GUI can do that.


Now, your game object will have to tell the rendering system what sprite from what texture they use when you create it or when it changes (for animation). Yet, they are not responsible for loading those sprites/tiles. The convention is that there is an error texture (usually a magenta and black checkers pattern) used when the requested texture is not available.

Usually you want to have only a handful of textures loaded and you want to load them when initializing the scene/map/scenario/room. Bitmaps are there to facilitate that. If you need to build an atlas at run time, you can do that with bitmaps. Hopefully you do not need that and you can just load a few bitmaps from the resource manager that has all the sprites/tiles you need.


GUI

Between GUI and Render. The TGUI library introduces a TGUI Window class that inherits from SFML one, and can be used as one. In my case, it will be a part of the rendering system. Hovewer, the GUI system will need to have a pointer to the Window to register GUI widgets.

The initialization of the window is neither.

Just because the rendering system does a lot of SFML calls does not mean that every SFML call belongs in the rendering system.

Instead, handling the window is its own thing. Mold the event loop into the game loop, and it will call both the rending and the GUI system, and also all other systems.


Animation

Between Gameplay and Render. Where applicable, Actor class will have a AnimatedSprite, which i will implement on my own. It will be a part of the Gameplay system, and will have a pointer to the sprite in resource manager (in the rendering system); it will just cut it depending on animation/frame. In some cases, when animations are not required, the Actor class will just have pointer to a normal sprite. To get those pointers a function in resource manager will be called from the Gameplay system.

As I was saying the game object needs to tell the rendering system about its sprite. However, I would avoid pointers across systems. Instead of a pointer, use an id that serves as index in a table.

You will have multiple sprites in the same texture, and you can change which one is the current sprite of a game object. That is one way to do animation. There is no cutting for that, the rendering system will be rendering a different part of the texture, that is all.

No resource manager is involved either. You load the textures before hand. Remember that multiple game objects could use the same textures. You do not want each game object loading the same texture, that is wasted effort.

Also, I was saying sprites/tiles, because it is very similar to a way to do the terrain of the map. In fact, that can be animated too (water animation, for example). However, the map will give an array of sprite ids to render instead of the single sprite id from the game object. Perhaps not what you want... you can always have a custom painted map.


Which brings me to: the camera. Yes, camera. It will be exposed by the rendering system for control, however figuring what is in view is responsability of the rendering system. You might want to send to the GPU only things that are nearby the camera.

In fact, having a way to query things that are nearby a position is a good idea for physics. Will you have physics? I guess for collisions with terrain obstacles. Oh, another use is for AI. You might want enemies to react when the player is nearby.

Now, the camera is not exactly a game object, and it is not exactly a system. However, having some sort of system or module dedicated to handle the camera is a good idea. You may want to move it in predefined paths for cinematics, also there are planty of ways to handle the camera in a 2D game, and none of that logic belongs in any particular visible object nor in the rendering system. It would be a camera manager or something like that (camera operator? cinematographer?).


I am assuming you will be writing shaders, and loading textures to the GPU. That is why rendering parts of a texture is completely hidden from the rest of the system (it is shader logic). Again, no cuttig required.

I said above that changing which one is the current sprite is a way to the animation. I am implying that there is another way. You can have the game object tell the rendering system about all the sprites of the current animation, and the rendering system can loop them. In fact, the shader code can loop them. Just remember to pass the time as a uniform and in the shader compute which one is the current sprite for the time. That way, animating a screen full of water tiles does not require to pass a bunch of sprite ids each frame.

\$\endgroup\$

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.