1
\$\begingroup\$

i have a question on designing a HTML5 2D game renderer.

I've read that it's better to not let entities draw themselves but instead put all the drawing functions in the renderer. After coding for a bit, I realise that this does have its advantages. For instance, I can have a list of helper functions in my renderer system, like drawRotatedImage(), getSprite(), setFontSize(), plus the context variable etc.

However, wouldn't this lead to a very bloated renderer class, with functions like drawTree(), drawMonster(), drawPlayer(), drawBall() or whatever entity?

Also, how would i easily map the entity objects to their corresponding draw functions? Would i have to do an if (entity.type == 'monster') this.drawMonster(); else if (entity.type == 'tree') this.drawTree(); kind of thing?

\$\endgroup\$
1
  • \$\begingroup\$ Make a Sprite class that has draw() method. Sprite is not game entity, you may have something like Skeleton class that when constructed will create a Sprite. And sprites only responsibility is to draw the sprite. \$\endgroup\$
    – Kikaimaru
    Oct 12, 2013 at 16:21

3 Answers 3

1
\$\begingroup\$

It would appear that you're implementing this design pattern in a way that causes new issues. The good news is, you've observed the problems with your implementation!

Wouldn't this lead to a very bloated renderer class?
How would I easily map the entity objects to their corresponding draw functions?

Consider refactoring your code so you have a more "templated" design. Rather than having all these different functions drawPlayer(), drawMonster, drawTree(), etc., consider analyzing the similarities between these functions and turning them into a single function. One way to do this is to store all the information that would be different per entity (position, sprite reference, rotation) in the entity itself. Then, you can have one function in your renderer that looks like this:

drawSprite(Sprite &sprite, float x, float y, float rotation);

Now, in each of your entities, there is no need to make a call to its own special draw function because you have one generalized function. Simply call drawSprite and pass the proper arguments.

With some clever inheritance or composition, you should find yourself writing very little rendering code after you take care of this.

Edit: I'd also like to point out that this has the advantage of decoupling rendering code from entities. You may find this useful for drawing things out of the game's context, like rendering your main character's sprite next to the title at the main menu, or showing item graphics in an inventory.

\$\endgroup\$
8
  • \$\begingroup\$ Thanks for the quick response! But from what I understand, this seems to only work for entities with very simple draw functions. For instance, what if I want some entities to have a HP bar above them? Or others to have a 'stunned' sprite on top of their sprites? Or a circle around the entity to indicate that it's selected? \$\endgroup\$
    – Wakaka
    Oct 12, 2013 at 16:24
  • \$\begingroup\$ @Wakaka Those would be additional components or arguments passed in to the rendering system. \$\endgroup\$
    – House
    Oct 12, 2013 at 16:32
  • \$\begingroup\$ @Byte56 Hmm that's true. So for each entity, I guess I will have some visual data in the form of an object like entity.visualData = {sprite: .., x: .., y: .., rotation: .., alpha: .., stunned: true, selected: true}, which will be computed before the rendering phase. Then the renderer will loop through all entities and call this.drawEntity(entity.visualData)? \$\endgroup\$
    – Wakaka
    Oct 12, 2013 at 16:39
  • \$\begingroup\$ But in the drawEntity() function, won't I still need to do some if statement handling? Something like if (visualData.stunned) drawStunned(visualData); and if (visualData.selected) drawSelected(visualData);? \$\endgroup\$
    – Wakaka
    Oct 12, 2013 at 16:42
  • \$\begingroup\$ You can create the rendering components (sprite, health bar, status, etc.) as abstract classes and do a foreach loop to render each component. It's something that's pretty specific to your design, so it's hard to say exactly what you should do. \$\endgroup\$
    – House
    Oct 12, 2013 at 16:44
0
\$\begingroup\$

I recommend that you do have functions in your renderer called "drawTree", "drawHouse" etc.

To call these, you can pass your renderer object into your objects (polymorphic) "draw" function. Instead of the objects drawing themselves, they just pass off the relevant information into the renderer. What information that is, depends on the kind of object.

So the Ball's "draw" function looks a bit like

function draw(renderer) {
   renderer.drawBall(this.location); // other params as necessary
}

Also if you have some object which doesn't need rendering (say it is a game-logic object), then you can just have it have a "noop" rendering method.

\$\endgroup\$
2
  • \$\begingroup\$ This seems like a good idea to prevent the if statements I mentioned! It seems pretty convenient, but I just wonder if it's a good practice to have the renderer call the entity's draw function, which in turns directs back to one of the renderer's functions? \$\endgroup\$
    – Wakaka
    Oct 13, 2013 at 3:48
  • \$\begingroup\$ I can't say that it's really "good practice" or not, except that it seems to work. NB: if you have many different different types of thing in your game (e.g. 1000 different types on monsters), they are likely to be represented by fewer classes in your code. So it you won't end up with 1000 drawMonsterxxx methods, rather just one drawMonster which takes some parameters to know which kind of monster to draw. \$\endgroup\$
    – MarkR
    Oct 13, 2013 at 20:21
0
\$\begingroup\$

I tried to model a render system for an html5 game and learned the following:

We use render code separated from other game code with the intention of change graphic api at any time without having to touch anything except the renderer implementation. But, for html5, do we need this? Canvas is already all the multi platform it can be. The browsers are already responsible of implement the platform specific part.

A render system, if used, must be very generic. I don't think a method called renderATree() should be in the renderer. The sprite representing a tree must contain the data that you need to pass to a render generic function to produce a result that looks like a tree.

sprite.img = "media/img/trees.png"; // many types of trees in a single png file
sprite.left = 0; // we configure the sprite to represent one of those trees
sprite.top = 0;
sprite.width = 32;
sprite.height = 32;
sprite.offsetx = -16; // the sprite will be renderer centered in the x axis
sprite.offsety = -32;

renderer.renderSprite(sprite, posx, posy, rotation, scale);

If this where C++ renderSprite would probably contain api specific calls (DirectX, OpenGL), but being a html5 game, it probably contains only canvas calls. Having a render system then is only a choice of code organization.

If you still need a method like renderTree or renderMonster then I would make them part of the sprite or entity.

The Tree "class" then share the same prototype of the sprite "class" but define its own render method. In javascript this does not look so unnatural, I remember doing something like this:

function Sprite ()
{
  // The sprite interface (some vars and some functions)
}

function newSoldier ()
{
  var spr = new Sprite();
  var spr.render = renderSoldier; // written somewhere, contains the canvas calls to render a soldier.
  return spr;
}

And It was used like this:

scene.add(newSoldier());

When using the "Each sprite know how to render itself" approach a render system became unnecessary. Then I ended with a main loop that used functionality of the scene "class" to efficiently cull the scene and then loop through a sprite array representing the visible part of the scene calling each sprite render function. As sprites are allowed to contain render code, anything can be an sprite including a health bar that needs no image data and is renderer only with lines of different colors (easy done with canvas calls).

Also note that with the second approach sprites and entities are the same thing. I called them sprites because it was 2D game. In the first approach sprite was a "structure" that contains data for the renderer renderSprite function and a separate entity "class" that refers to a sprite was needed.

In resume, my position is:

  • If implementing a render system include only generic functionality on it (probably unnecessary on most htm5 games)

  • If you choose "Each sprite know how to render itself" having canvas calls inside the render functions does not harm in this case.

\$\endgroup\$
2
  • \$\begingroup\$ I think you might need pluggable renderers in HTML5, because a) Different HTML5 platforms have very different performance characteristics (e.g. mobiles/tablets) so may need different implementations b) Some platforms may support different feature sets (e.g. webgl) c) You might want to run your code with no renderer at all, e.g. on server-side under nodejs or similar. \$\endgroup\$
    – MarkR
    Oct 13, 2013 at 20:23
  • \$\begingroup\$ It's true that performance differs. That is a point in favor of a renderer interface with different implementations. For a 2D game, write a webgl renderer and a 2D context renderer does not convince me. If the game needs webgl to perform ok, It will probably be too slow in 2D context anyway. \$\endgroup\$ Oct 14, 2013 at 19:07

You must log in to answer this question.

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