Separation of game and rendering logic
Asked Answered
E

3

30

What is the best way to separate rendering code from the actually game engine/logic code? And is it even a good idea to separate those?

Let's assume we have a game object called Knight. The Knight has to be rendered on the screen for the user to see. We're now left with two choices. Either we give the Knight a Render/Draw method that we can call, or we create a renderer class that takes care of rendering all knights.

In the scenario where the two are separated the Knight should the Knight still contain all the information needed to render him, or should this be separated as well?

In the last project we created we decided to let all the information required to render an object be stored inside the object itself, but we had a separate component to actually read that information and render the objects. The object would contain information such as size, rotation, scale, and which animation was currently playing and based on this the renderer object would compose the screen.

Frameworks such as XNA seem to think joining the object and rendering is a good idea, but we're afraid to get tied up to a specific rendering framework, whereas building a separate rendering component gives us more freedom to change framework at any given time.

Expunge answered 3/5, 2010 at 7:24 Comment(1)
Ignore XNA's implementations. As much as I love it, the "default" way you see examples on the web and in books is awful. Every object knows about the Game base class and in turn, everything knows how to draw and so on. This is no good. Thomas' solution is ideal, and what many people do.Rase
M
7

I would endeavor to make your objects as generic as feasible and to avoid encoding gameplay facts into your code as much as possible.

E.g. you have an Entity or Object or Actor class, and that class has a pointer to its decision making (its AI) and a pointer to its graphical representation.

Its decision making is necessarily coupled to the gameplay facts; indeed it is a part of gameplay facts. So this is somewhere where you might name your decision maker "KnightAi" or something.

On the other hand, the graphical representation is just a bit of graphics. It'll be drawn the same as every other entity. You have a model or some bitmaps and some animations or not... This would/could just be called something like "Model" and will load the information that it's told to load the defines what the entity looks like to the player. When it comes to rendering your knight versus your horse versus a castle, you'll likely find that they all do the same thing just with different data. The sorts of data that they have is even all the same, it's just the contents that are different.

Say your entities were all represented as circles. You would just have them point to a CircleRenderer that took the size that they should be drawn. You wouldn't create a different renderer class for each different size (or color, or whatever) of circle you wanted!

Maeve answered 3/5, 2010 at 18:47 Comment(1)
A vague answer ... 'do the same thing just with different data' - describes the whole of programming. Renderers require gameplay facts - which way are you facing, what objects are you seeing, what lights are intersecting your view, what equipment you are using (that affect visuals). etc, etc. The graphical representation is not just a bit of graphics, it is tightly coupled with the game-state. That's why it can be difficult to decide how to handle it. Try building a dedicated rendering thread that requires thread safe, state snapshots. Then you'll know what I mean.Bloodstock
H
22

I would create a separate KnightRenderer class. Advantages:

  • Clean separation between the game logic and the rendering. The Knight himself might even run on a game server and not know anything about rendering at all.
  • Smaller, simpler classes, concerned with one and only one piece of functionality.

Everything the KnightRenderer needs to know about the Knight (position, status) has to be publically readable in Knight.

Properties specific to rendering would go into the KnightRenderer class. For example, maybe you want to make the knight flash when he's hit, so you would need to store a counter or time value.

Hesperidium answered 3/5, 2010 at 7:35 Comment(3)
I like your thoughts. Question: Where do you create the Knight Renderer? Do you directly reference it in Knight, or do you create it elsewhere and pass it on?Esbensen
@Hesperidium Would KnightRenderer contain actual (gl*/directx/whatever) code ?In case it contains it seems difficult to apply some optimisations and to switch implementation. Like KnightRenderOpelGL etc.Paranoid
If you want to be able to switch graphics API implementation later on, you could wrap that elsewhere in a singleton-like object, e.g. SpriteRenderer.drawSprite(x, y, sprite). KnightRenderer would only need to know about the SpriteRenderer API, not its implementation. This is just one option; without knowing the exact requirements and constraints it's hard to give a clear-cut answer.Hesperidium
M
7

I would endeavor to make your objects as generic as feasible and to avoid encoding gameplay facts into your code as much as possible.

E.g. you have an Entity or Object or Actor class, and that class has a pointer to its decision making (its AI) and a pointer to its graphical representation.

Its decision making is necessarily coupled to the gameplay facts; indeed it is a part of gameplay facts. So this is somewhere where you might name your decision maker "KnightAi" or something.

On the other hand, the graphical representation is just a bit of graphics. It'll be drawn the same as every other entity. You have a model or some bitmaps and some animations or not... This would/could just be called something like "Model" and will load the information that it's told to load the defines what the entity looks like to the player. When it comes to rendering your knight versus your horse versus a castle, you'll likely find that they all do the same thing just with different data. The sorts of data that they have is even all the same, it's just the contents that are different.

Say your entities were all represented as circles. You would just have them point to a CircleRenderer that took the size that they should be drawn. You wouldn't create a different renderer class for each different size (or color, or whatever) of circle you wanted!

Maeve answered 3/5, 2010 at 18:47 Comment(1)
A vague answer ... 'do the same thing just with different data' - describes the whole of programming. Renderers require gameplay facts - which way are you facing, what objects are you seeing, what lights are intersecting your view, what equipment you are using (that affect visuals). etc, etc. The graphical representation is not just a bit of graphics, it is tightly coupled with the game-state. That's why it can be difficult to decide how to handle it. Try building a dedicated rendering thread that requires thread safe, state snapshots. Then you'll know what I mean.Bloodstock
H
7

I know i'm coming late, but for future readers, you might be interested in a tutorial I wrote.

I'm no real expert but this is how I see proper separation of concerns:
http://aurelienribon.wordpress.com/2011/04/26/logic-vs-render-separation-of-concerns/

alt text

Herculean answered 17/1, 2011 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.