Jump to content
  • Advertisement
Sign in to follow this  
BurrickSlayer

About separating game logic from rendering

This topic is 2102 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone,

lately I was thinking about how I could separate game logic from rendering for a personal game project of mine. I tend to think a lot before starting to write any code. Many times, however, I fail to come up with a suitable solution, so I thought it might be a good idea to ask here for help and suggestions.

First let me tell you that the game I work on is similar to the Zelda, Chrono Trigger or Secret of Mana games as far as the visual appearance is concerned. So basically I have a tile map with sprites moving around.

The game's scene is organized into layers with each layer referring to tile maps, entities and other stuff to render. Entities are the base structure from which concrete game objects are derived. Layers are used to group objects for rendering only, so entities from layer 1 can still logically interact with entities from layer 2. The scene also contains invisible objects like trigger areas that are not assigned to any layer. I don't use a typical scene graph as the scene doesn't have a deep transform hierarchy.

The scene can be rendered in different ways:
  • By default the scene is rendered as expected when starting and playing the game.
  • The scene can also be rendered in edit mode when the ingame editor is fired up. In edit mode trigger areas and other normally invisible objects are drawn. The rendering of other objects might be altered or filtered out completely (for example if I don't want a specific layer to be drawn).
  • As a third option the scene might also be used to draw a mini-map on screen.
I have never separated game logic from rendering that much before so I can only guess what I have to do to achieve this. Maybe I should have three different renderers like DefaultRenderer, EditRenderer and MinimapRenderer. They could all have a draw method that takes a scene as an argument: DefaultRenderer::draw(const Scene&). I'm not sure though on how the renderers should figure out what to draw and how to draw it.

Let's just assume I have a Scene class and a Monster class that look like the following pseudo C++ code (I use SFML if that makes a difference). For the sake of simplicity the scene contains nothing but entities.
 
 
class Scene
{
    public:
        void draw() const
        {
            for (auto& entity : entities)
            {
                window.draw(*entity);
            }
        }

    private:
        std::unordered_set<std::unique_ptr<Entity>> entities;
};

class Monster : public Entity
{
    public:
        Monster() :
            Entity(),
            sprite(someSpriteSheet, monsterTextureRect)
        {}

    private:
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;

    private:
        sf::Sprite sprite;
};
 
 
How do I move the render logic out of these classes? Should I store the sprites in the scene directly so the renderers can iterate them easily?

Is having several renderers the right approach after all?

I would very much appreciate any help and suggestions on how to do this properly.

Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement

Don't put a draw function in the game objects.

 

Instead of drawing via the entity list, maintain a separate list of sprites and draw from that. Side advantage is that an entity can have multiple sprites, and non-entities can have sprites (e.g. menus) and you don't have to write any extra drawing loops.

 

That's assuming the game objects don't have a bunch of unique rendering code in each one. But if they do, see if you can factor it out into a few more generic "renderable" classes which can go in the same list with the sprites, or another separate list, depending on what works best.

Share this post


Link to post
Share on other sites

I really like component based architecture. It basically allows you to decouple almost everything in exchange for a little extra processing effort.

 

You could separate the render part in a component and add it to a component-list owned by the entity. When you need to render, check for a render component and make it render itself or make it so that a standardized component function update() or similar does the necessary steps.

 

This can be done for physics, AI, you get the drill.

#include <vector>

class component {
   public virtual update(); //standardized update function
   private static std::vector<component> control_list; // Add all components here when they are created, maybe remove when destroyed
};

class render_component: public component {
    private:
        drawable *sprite; //your actual drawable object
        game_entity *owner; //the owner of this component

    public:
        render_component(drawable *d, game_entity *e){
            sprite = d;
            owner = e;
        }

        update (){
            sprite.draw( &owner.getPosition() ); // Do specific code, decoupled from the entities.
        }
};

class game_entity {
        private std::vector<component> component_list;
        public:
            registerComponent(*component); //adds a component
            removeComponent(*component); //removes a component
            flushComponents(); // removes all components
}

This allows for different render behavior (something that draws on top of everything like smoke, birds or clouds);

Multiple sprites (say an ogre and a poisonous cloud over him without the need for a sprite for it, nor two entities);

Weird behavior sprites (like a reverse_render_component that draws the animation backwards or slow_render_component);

you get the drill.

 

Forgive me for my simplistic code, I just meant to illustrate it.

 

I recommend you to read the sources from a book, download available here (under sample material tab):

http://www.jblearning.com/Catalog/9781556220784/student/

 

these source codes basically taught me the little I know about messaging and components, but even this little study has proven itself enough for a lot of things. I ended up buying the book.

Edited by dejaime

Share this post


Link to post
Share on other sites

Each entity should have a set of standard structures that encode the visual appearance of the object. The renderer can simply use that data to draw.

 

I would have an Entity with pointers to Transform, Mesh and Material, so that's all the renderer needs to know about.

Share this post


Link to post
Share on other sites

Thank you very much for all your suggestions. They help me a lot.

@haegarr
 

Iterating the scene can be done by a visitor, i.e. an object external to the scene. This isn't to done by the renderer. Gameplay and editor may be modes of this iteration.

I haven't used visitors before but iterating over the drawables seems to be a nice application for this pattern. I'll definitely try this out.
 

There is no need for several renderers just to enable triggers to be drawn. Either put or put not a primitive for it into the rendering job list.

I should be able to handle this gracefully using visitors. It's a question of visit or not to visit then.

@HappyCoder
 

Try the module approach, have a GameObject that has all of its subparts interchangeable. You will not override the GameObject.

I tried to implement a component system some time ago but I wasn't successful back then. Having never used a component system before (just heard about them) I was uncertain about several implementation details. For example I couldn't decide on how to establish communication between the components - should I just use something simple like this.owner.getComponent("transform") from within components or should I rather use a more elaborate messaging system? I'm still tempted to give it another try, though, because I quite like the idea behind component systems.

I guess I have to take a deeper look into this once again. The many threads on this forums about implementing such systems should prove helpful to me.

@DekuTree64
 

Instead of drawing via the entity list, maintain a separate list of sprites and draw from that. Side advantage is that an entity can have multiple sprites, and non-entities can have sprites (e.g. menus) and you don't have to write any extra drawing loops.

Sounds very reasonable to be. I'll do it this way and keep all drawables in a separate list.
 

That's assuming the game objects don't have a bunch of unique rendering code in each one. But if they do, see if you can factor it out into a few more generic "renderable" classes which can go in the same list with the sprites, or another separate list, depending on what works best.

SFML provides a Drawable interface class that sprites, shapes etc. are all derived from. I'll just derive a drawable group class from it that can contain other drawables. This should do the trick.

@dejaime
 

I really like component based architecture. It basically allows you to decouple almost everything in exchange for a little extra processing effort.

I like component systems as well and as I said in my answer to HappyCoder I'll check them out once more.
 

I recommend you to read the sources from a book, download available here (under sample material tab):

http://www.jblearning.com/Catalog/9781556220784/student/

I already own this book. In fact it was my first book about game programming. Back then I didn't know any C++ but I liked the book nevertheless and I can still recommend it today. That's where I learnd pathfinding, spatial grids and steering behaviours from.

However, I already forgot about the messaging system presented in the book. Thanks for bringing this to mind! This should be of assistance in making a component system.

@Norman Barrows

Currently my plan is to decouple the things like that:

the list of drawables (sprites, primitives, groups etc.)
the list of entities
the RenderVisitor that visits the drawables (the "filters" are implemented by visitors derived from this one)
the camera/view (this is already done by SFML)

@Goran Milovanovic
 

I would have an Entity with pointers to Transform, Mesh and Material, so that's all the renderer needs to know about.

Yes, I would like to do it in a similar way and have an entity hold a pointer to a drawable (where a drawable can be a sprite, a primitive, a group of drawables etc.).

Share this post


Link to post
Share on other sites
Hello again,

I've changed quite a few things in my code. Now I have a simple scene tree that contains transformations and drawables. Originally I wanted to keep the drawables out of the tree (it was supposed to be a transformation tree) but as the sprite and shape classes provided by SFML are both drawable and transformable I figured it would be reasonable to just leave them in there.

I have also implemented a RenderVisitor that iterates the scene tree and collects the drawables. As to that I still have a question though. The visitor iterates the tree in depth-first order and thus the scene is also drawn in that order. This isn't quite the order I want the scene to be drawn, so I need to sort the drawables somehow.

I can think of two ways to achieve this:

1. Sort the scene nodes in-place, so the visitor iterates them in the correct order.
2. Sort the the render list after the visitor has finished building it.

Though I like the second approach better, I don't know how to express the rendering order in a value so it can be used by a sorting algorithm.

Let's assume my scene looks like this:
 
                      Root
                       |
          --------------------------
          |                        |
        Layer1                   Layer2
          |                        |
  -----------------            ---------
  |       |       |            |       |
  A       B       C            F       G
          |
      ---------
      |       |
      D       E

So there is a root node, two layer nodes and some other nodes denoted A to G.

Nodes A, B, C, F and G are used by entities. A, for example, represents an entity that consits of only one sprite. B represents an entity that consits of two sprites (D and E). Consequently D and E should always be drawn along with B.

This is the order in which the nodes are currently drawn (depth-first order):
 
                       0
                       |
          --------------------------
          |                        |
          1                        7
          |                        |
  -----------------            ---------
  |       |       |            |       |
  2       3       6            8       9
          |
      ---------
      |       |
      4       5

This drawing order is mostly like I want it: The first layer and its children are drawn before the second layer and its children, node B and its children are drawn together etc.

However, the nodes used by the entities should be drawn in the order according to their Y screen coordinate so that entities at the bottom of the screen appear as if they were in the foreground.

So let's assume I want to change the drawing order of nodes A, B and C so that B and its children are drawn first, then C, and last A:
 
                       0
                       |
          --------------------------
          |                        |
          1                        7
          |                        |
  -----------------            ---------
  |       |       |            |       |
  6       2       5            8       9
          |
      ---------
      |       |
      3       4

If I was sorting the scene nodes in-place this wouldn't be a problem as I could just specify sorting rules for each group of children. But as it is here all drawables are just in a plain list (assembled by the visitor) and I need some kind of uniform sorting value to sort the whole list. (I hope you know what I mean.)

Any ideas how I could do this?
Or would sorting the scene nodes in-place not be that bad (it feels somewhat dirty)?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!