10
\$\begingroup\$

When making games you often create the following game object from which all entities inherit:

public class GameObject{
    abstract void Update(...);
    abstract void Draw(...);
}

So in you update loop you iterate over all the game objects and give them a chance to change state, then in the next draw loop you iterate again over all the game objects and give them a chance to draw themselves.

Although this works fairly well in a simple game with a simple forward renderer it often leads to a few gigantic game objects that need to store their models, multiple textures and worst of all a fat draw method that creates a tight coupling between the game object, the current render strategy and any rendering related classes.

If I were to change the render strategy from forward to deferred I would have to update a lot of game objects. And the game objects I make are not as reusable as they could be. Of course inheritance and/or composition can help me fight code duplication and make it a bit easier to change implementation but it still feels lacking.

A better way, perhaps, would be to remove the Draw method from the GameObject class altogether and create a Renderer class. The GameObject would still need to contain some data about it's visuals, like what model to represent it with and what textures should be painted on the model, but how this is done would be left to the renderer. However there are often a lot of border cases in rendering so although this would remove the tight-coupling from the GameObject to the Renderer, the Renderer would still have to be all knowing about all the game objects which would make it fat, all knowing and tightly-coupled. This would violate quite a few good practices. Maybe Data-Oriented-Design could do the trick. Game objects would certainly be data, but how would the renderer be driven by this? I'm not sure.

So I'm at a loss and can't think of a good solution. I've tried to use the principles of MVC and in the past I had some ideas about how to use that in games, but recently it doesn't seem as applicable as I thought. I would love to know how you all tackle this problem.

Anyway let's recap, I'm interested in how the following design goals can be achieved.

  • No rendering logic in the game object
  • Loose coupling between game objects and render engine
  • No all knowing renderer
  • Preferably runtime switching between render engines

The ideal project setup would be a separate 'game logic' and render logic project that don't need to reference each other.

This thought train all started when I heard John Carmack say on twitter that he has a system so flexible he can swap out render engines at run time and can even tell his system to use both renderers (a software renderer and a hardware-accelerated renderer) at the same time so he can inspect differences. The systems I've programmed so far aren't even near that flexible

\$\endgroup\$
0

2 Answers 2

7
\$\begingroup\$

A quick first step to uncoupling:

Game objects reference an identifier of what their visuals are but not the data, let's say something simple like a string. Example: "human_male"

Renderer is responsible for loading and maintaining "human_male" references and passing back to objects a handle to use.

Example in horrible pseudocode:

GameObject( initialization parameters )
  me.render_handle = Renderer_Create( parameters.render_string )

- elsewhere
Renderer_Create( string )

  new data handle = Resources_Load( string );
  return new data handle

- some time later
GameObject( something happens to me parameters )
  me.state = something.what_happens
  Renderer_ApplyState( me.render_handle, me.state.effect_type )

- some time later
Renderer_Render()
  for each renderable thing
    for each rendering back end
        setup graphics for thing.effect
        render it

- finally
GameObject_Destroy()
  Renderer_Destroy( me.render_handle )

Sorry for that mess, anyways your conditions are met by that simple change away from pure OOP based on looking at things like real world objects and into OOP based on responsibilities.

  • No rendering logic in the game object (done, all the object knows is a handle so it can apply effects to itself)
  • Loose coupling between game objects and render engine (done, all contact is via an abstract handle, states that can be applied and not what to do with those states)
  • No all knowing renderer (done, only knows about itself)
  • Preferably runtime switching between render engines (this is done at the Renderer_Render() stage, you do have to write both back ends though)

Keywords you can search on to go beyond a simple refactoring of classes would be "entity/component system" and "dependency injection" and potentially "MVC" GUI patterns just to get the old brain gears spinning.

\$\endgroup\$
1
  • \$\begingroup\$ This is extremely different from whatever I have done before, sounds like it's got quite a bit of potential. Luckily I'm not constrained by any existing engine so I can just tinker about. I'll also look up the terms you mentioned, although dependency injection always make my brain hurt :P. \$\endgroup\$
    – Roy T.
    Commented May 23, 2012 at 7:56
2
\$\begingroup\$

What I've done for my own engine is to group everything into modules. So I have my GameObject class and it holds a handle to:

  • ModuleSprite - drawing sprites
  • ModuleWeapon - firing guns
  • ModuleScriptingBase - scripting
  • ModuleParticles - particle effects
  • ModuleCollision - collision detection and response

So I have a Player class and a Bullet class. Both derive from GameObject and are added to the Scene. But Player has the following modules:

  • ModuleSprite
  • ModuleWeapon
  • ModuleParticles
  • ModuleCollision

And Bullet has these modules:

  • ModuleSprite
  • ModuleCollision

This way of organizing things avoids the "Diamond of Death" where you have a Vehicle, a VehicleLand and a VehicleWater and now you want a VehicleAmphibious. Instead you have a Vehicle and it can have a ModuleWater and a ModuleLand.

Added bonus: you can create objects using a set of properties. All you have to know is the basetype (Player, Enemy, Bullet, etc.) and then create handles to modules you need for that type.

In my Scene, I do the following:

  • Call the Update for all GameObject handles.
  • Do collision checking and collision response for those that have a ModuleCollision handle.
  • Call the UpdatePost for all GameObject handles to let know about their final position after physics.
  • Destroy objects that have their flag set.
  • Add new objects from the m_ObjectsCreated list to the m_Objects list.

And I could organize it further: by modules instead of by object. Then I would render a list of ModuleSprite, update a bunch of ModuleScriptingBase and do collisions with a list of ModuleCollision.

\$\endgroup\$
2
  • \$\begingroup\$ Sounds like composition to the max! Very nice. I don't see much rendering specific tips here though. How do you handle that, just by adding different modules? \$\endgroup\$
    – Roy T.
    Commented May 23, 2012 at 12:50
  • \$\begingroup\$ Oh yes. That's the downside to this system: if you have a specific requirement for a GameObject (for instance a way to render a "snake" of Sprites) you will either need to create a child ModuleSprite for that specific functionality (ModuleSpriteSnake) or add a new module altogether (ModuleSnake). Luckily they're only pointers, but I've seen code where GameObject did literally everything an object could do. \$\endgroup\$
    – knight666
    Commented May 23, 2012 at 18:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .