Skip to content

Frequently Asked Questions

Olivier Biot edited this page Apr 16, 2014 · 83 revisions

Table of Contents

  1. How to optimize your game for performance
  2. How to package your game for Windows, Mac, or Linux
  3. How to access object properties or methods from another object
  4. How to enable and use the object pooling feature
  5. Handling mouse or touch events on multiple overlapping objects

1. How to optimize your game for performance :

  • Try a lower fps rate. does your game really requiring 60fps ?
  • Make sure to use a [rendering method] (http://melonjs.github.io/docs/me.sys.html#preRender) adapted to your game, enabling it per layer (layers with sparse tiles should not have the pre rendering enabled). Layers with very few tiles draw faster dynamically, because only a few small draw operations occur, instead of one giant draw operation using mostly invisible pixels (context.drawImage() is not optimized for invisible pixels).
  • Enabling [double buffering] (http://melonjs.github.io/docs/me.video.html#init) could help as well to decrease the amount of draw operations (works entirely with an off-DOM canvas and it is copied to the visible canvas at the end of the rendering cycle).
  • Enabling and using [Object Pooling] (http://melonjs.github.io/docs/me.pool.html) will help reducing memory usage by reusing object and lowering the use of garbage collector.
  • Try wrapper solution like [CocoonJS] (http://ludei.com/tech/cocoonjs) that provide Canvas accelerated environments on mobile devices.
  • If you use TexturePacker disable texture rotation, as it forces melonJS to apply a default rotation angle when drawing sprites and, based on how many sprites are to be displayed, can impact performance.
  • Use PNG images with alpha channel instead of the "transparent color" settings in Tiled tilesets. Even though the performance burden will occur only on load, you will notice it frequently if the map is loaded more than once while playing.
  • The game resolution is one of most important factor that influences the performance, try lowering the resolution in the call to [me.video.init] (http://melonjs.github.io/docs/me.video.html#init). Also it might be best to disable auto-scaling video.
  • Optimize your assets, using a Texture Atlas (like [Texture Packer] (http://www.codeandweb.com/texturepacker/) or [Shoebox] (http://renderhjs.net/shoebox/)) and use fixed sized assets that are square and powers-of-2, like 512x512px, 1024x1024px, 4096x4096px or not square but powers-of-2, like 512x256px, 1024x512px, 4096x2048px (this optimize the use of GPU memory).
  • Optimize your Tiled maps, the same should be square and powers-of-2, like 512x512px, 1024x1024px, 4096x4096px or not square but powers-of-2, like 512x256px, 1024x512px, 4096x2048px (this also optimize the use of GPU memory).
  • For optimizing many entities (like characters) melonJS provides you with some useful features like culled updates (configurable with [me.Renderable.alwaysUpdate] (http://melonjs.github.io/docs/me.Renderable.html#alwaysUpdate) - off by default, so off-screen entities do not use any additional CPU time for AI) and avoiding garbage collection stalls using entity pooling.
  • Avoid calling [me.game.world.collide()] (http://melonjs.github.io/docs/me.ObjectContainer.html#collide) in every entity! This function is very slow, and careful consideration should go into which entities call it. For most games, the main player should be the only entity calling this function, with other entities responding to the collisions in their own onCollision callback. Your own character's projectiles might also call it, but only if you really NEED your character to throw projectiles. If not, throw out the idea of projectiles and save a LOT of CPU time!!
  • Avoid drawing text with the me.Font class more than necessary; it is best to render the text to an off-screen canvas: me.video.getContext2D(me.video.createCanvas(width, height)) only once in your entity constructor, then draw the offscreen canvas each frame.
  • There are a ton of good programming practices you can follow that will avoid unnecessary calculations. For example, it is faster to check an object property directly: if (this.last_animation === "walk") than it is to call a function that provides the same information: if (this.isCurrentAnimation("walk"))
  • Avoid Number.prototype.degToRad() and friends - use radians directly to express rotation angles. For example, a full turn is always Math.PI * 2, a half turn is Math.PI, a quarter turn is Math.PI * 0.5, etc.
  • Use the Chrome JavaScript profiler or similar to locate problem areas.

2. How to package your game for Windows, Mac, or Linux :

  • There are several solutions out-there, but one that is known to work very well with melonJS is [Node-Webkit] (https://github.com/rogerwang/node-webkit), a stand-alone chromium wrapper that will let you package and distribute your games natively for Windows, Mac, and Linux.

3. How to access object properties or methods from another object :

game.player = me.game.world.getChildByName("player")[0];
  • The return value is an array. So when you have one player object, it will be the only element in the array.
  • Now you can access properties and methods on the "player" object through game.player:
// Get current player position
var player_pos = game.player.pos;

// call one of the player function
game.player.foo();

4. How to enable and use the object pooling feature :

Object Pooling allows you to greatly reduce the overhead of creating entities programmatically, and it is very useful with entities that are created and destroyed often (like bullet or laser entities). By using this feature, new instances of your entity won't be created each time you need a new one (which increases memory usage and potentially slowing your game), but rather activated or pulled from the pool. When an entity is then destroyed, it is placed back into the pool to await the next request.

So, how to use this great stuff? Let's have a look first at how to manually add an new entity (here EntityLaser) to the game manager:

// Create a new laser object
var myLaser = new EntityLaser("laser", this.pos.x , this.pos.y);
// Add the laser to the game manager with z value 3
me.game.world.addChild(myLaser, 3);

Now to use entity pooling, the first thing is to associate a unique identifier to the object's class within the entity pool. This action has to be done one time only, preferably in your main loaded function:

me.pool.register("laser", EntityLaser, true);

Here we define "laser" as the unique identifier for the EntityLaser class in the entity pool. Also note that true is used as the last parameter to actually enable the object pooling for this particular entity type.

After this is done, we are ready to create new entities as before, with a slight difference on the object instantiation itself:

// Create a new laser object
var myLaser = me.pool.pull("laser", this.pos.x, this.pos.y);
// Add the laser to the game manager with z value 3
me.game.world.addChild(myLaser, 3);

As you can notice, we no longer directly instantiate the object but request a new instance of the entity identified by the "laser" id, which was declared to use our EntityLaser class.

One specific point of attention when using this is the object properties initialization, as you should carefully ensure that all of them are properly set/reset to their default value in the entity init method.

That's it!


5. Handling mouse or touch events on multiple overlapping objects :

For most uses, the mouse/touch listeners will work great as long as the rects are in different parts of the screen. If any of them overlap you can run into trouble quickly. One obvious example is when registering mouse/touch events for the same me.Rect on multiple objects; wanting to use me.game.viewport is rather common.

The trouble starts when you want to release the event, because a callback isn't provided to me.input.releasePointerEvent() specifying which callback should be released. Instead, it releases all callbacks on the provided me.Rect. (Side note: This may change in a future version of melonJS.)

As an immediate workaround, consider changing your code to register the event in a single "top level" object, and then delegate the event using the super awesome minpubsub!

First, the top-level event handler should be updated to only re-publish the event:

me.input.registerPointerEvent("pointerdown", me.game.viewport, function (event) {
    me.event.publish("pointerdown", [ event ]);
});

Then setup some event listeners:

this.pointerDown= me.event.subscribe("pointerdown", function (event) {
    console.log(event.pointerId, event.gameX, event.gameY); // etc ...
});

When you are ready to destroy the object which has an open subscription, you must unsubscribe:

me.event.unsubscribe(this.pointerDown);

And you can safely destroy the event delegator when you no longer need to handle any mouse/touch events:

me.input.releasePointerEvent("pointerdown", me.game.viewport);