ZEEF HTML5 Game Development


Check this ZEEF curated directory with several resources for develop games using HTML5, Javascript and WebGL. Some resources can be used for general game development.


ZEEF is a place where you find and create lists of links about your favourite topics. It's a very powerful way to share information where everybody wins!

Implementing Particles with melonJS


With the popularity of games on smartphones and browsers, 2D gaming is back on the scene, recalling the classic age of the late '80s and early '90s. At that time, most games used animation techniques with images (sprite sheets) to display special effects such as explosions, fire, or magic. In current games we can use particle systems, generating random and interesting visual effects in them. In this post I will briefly describe what a particle system is and how to implement models using the HTML5 engine melonJS.

Note: Although the examples are specific to the melonJS game engine using JavaScript as a programming language, the concepts and ideas can be adapted for other languages ​​or game engines as well.


Packt Publishing mission is to help the world put software to work in new ways, through the delivery of effective learning and information services to IT professionals.

Upgrading to melonJS 1.1


With the release of melonJS 1.1, is time to conduct a upgrade to this new version of the engine, packed with really nice features, as:

- New blazing-fast inheritance pattern (Jay Inheritance)
- New SAT (Separate Axis Theorem) based collision algorithm
- Super fast collision detection across a spatial partitioning algorithm (QuadTree)
- Added collision filtering support
- Entity object redesign with a full polygon shape based physic body implementation
- Several objects renamed  for a cleaner  and standardized API
- Fixed various bugs and issues

Check the melonJS Blog for a official announcement and get the lastest version in melonJS Site.

The recommended is to migrate the current version used in the game to one immediately more recent facilitating the upgrade process (some melonJS versions have API breaks). For example, if your game uses melonJS 0.9.11, you need to update first to 1.0.2 version (check the post Upgrading to melonJS 1.0) and after to 1.1.0 version.

Below we will check some of the necessary changes to upgrade from melonJS 1.0.2 (previous version) to melonJS 1.1.0 (current version when writing this post). For a complete list of changes visit the melonJS Upgrade Guide.


Before the Upgrade


The melonJS 1.1 is a major release and include several breaking changes which forces you to make several adjustments in your game code.

Before starting the migration, check the Upgrade Guide and the Changelog of engine and decide if the amount of work for the upgrade compensates the incredible new features of this melonJS version!

Don't forget to always have a backup of the last game version before starting the upgrade process!


Piece of Cake


Start renaming the functions, objects and variables changed in this version, which can be easily performed via a Search and Replace (or Refactor) available in your preferred Code Editor (or IDE):

// Old Code -> New Code
me.ObjectEntity -> me.Entity
me.SpriteObject -> me.Sprite
me.ObjectContainer -> me.Container
me.video.getWidth -> me.video.renderer.getWidth
me.video.getHeight -> me.video.renderer.getHeight
me.video.getSystemCanvas -> me.video.renderer.getCanvas
me.video.getSystemContext -> me.video.renderer.getContext
me.video.getScreenCanvas -> me.video.renderer.getScreenCanvas
me.video.getScreenContext -> me.video.renderer.getScreenContext
me.video.applyRGBFilter -> me.video.renderer.applyRGBFilter
me.video.getContext2d -> me.CanvasRenderer.getContext2d
If you used the onDestroyEvent callback from me.Renderable, the same should be renamed to onDeactivateEvent. Note that the function onDestroyEvent remains only to me.ScreenObject.

In preparation to WebGL support (probably available in version 1.2.0), the call to me.video.init has a new parameter informing the type of renderer (currently accepts only me.video.CANVAS):

// Before melonJS 1.1
me.video.init("game", 400, 300, true, "auto");

// After melonJS 1.1
me.video.init("game", me.video.CANVAS, 400, 300, true, "auto");

Let's Rock


The inheritance pattern has changed significantly, moving from John Resig Simple Inheritance to the new and fast Jay Inheritance, forcing various adjustments in the code. In the melonJS 1.0 and previous, you have something like:

// Before melonJS 1.1
var sprite = me.SpriteObject.extend({
  // Property
  active: yes,
  
  // Constructor
  init : function(x, y, sprite) {
    this.parent(x, y, sprite);
  },
 
  // Draw
  draw: function(context) {
    // Draw only if is active
    if (this.active)
       this.parent(context);
  }
});
To make performance improvements and enforce better rules you need to invoke a _super method (instead of parent), passing the base class itself, a string representing the method name, and an array of the arguments:

// After melonJS 1.1
var sprite = me.Sprite.extend({
  // Constructor
  init : function(x, y, sprite) {
    this._super(me.Sprite, "init", [x, y, sprite]);

    // Properties must be set in the init method only!
    this.active = yes;
  },
 
  // Draw
  draw: function(renderer) {
    // Draw only if is active
    if (this.active)
       this._super(me.Sprite, "draw", [renderer]);
  }
});
At first this change can scare, but the benefits with increased performance and reduced memory consumption compensate. For a more detailed explanation of the great new inheritance pattern, see the post about Jay Inheritance.

For better performance, the me.Renderable, me.Rect, me.PolyShape and me.Ellipse now requires (x,y) integer values over a me.Vector2d instance:

// Before melonJS 1.1
var myRenderable = new me.Renderable(new me.Vector2d(20, 50), 200, 300);
var myRect = new me.Rect(new me.Vector2d(30, 60), 150, 50);

// After melonJS 1.1
var myRenderable = new me.Renderable(20, 50, 200, 300);
var myRect = new me.Rect(30, 60, 150, 50);
The me.AnimationSheet now requires x, y and a settings hash to its constructor:

// Before melonJS 1.1
var animation = new me.AnimationSheet(10, 20, me.loader.getImage("sprite"), 64, 32);

// After melonJS 1.1
var animation = new me.AnimationSheet(10, 20, {
    image: me.loader.getImage("sprite"),
    spritewidth: 64,
    spriteheight: 32
});

Come Get Some


For a faster execution time, the me.Entity (me.ObjectEntity in the previous version) has been redesigned using a composition approach (lowering the amount of object properties), instead of inheritance based design used in the previous version. In this way, the me.Entity has a new me.Body child object that will hold all physic and collision properties and functions.

// Before melonJS 1.1
var entity = me.ObjectEntity.extend({
  // Constructor
  init: function(x, y, settings) {
    this.parent(x, y, settings);

    // Set the gravity
    this.gravity = 2;

    // Set the velocity
    this.setVelocity(5, 8);
  },

  // Update logic
  update: function(dt) {
      // Apply gravity
      this.vel.y += this.gravity;      

      // Update entity Position
      this.updateMovement();

      this.parent(dt);
      return true;
  }
});

// After melonJS 1.1
var entity = me.Entity.extend({
  // Constructor
  init: function(x, y, settings) {
    this._super(me.Entity, "init", [x, y, settings]);

    // Set the gravity
    this.body.gravity = 2;

    // Set the velocity
    this.body.setVelocity(5, 8);
  },

  // Update logic
  update: function(dt) {
      // Apply gravity
      this.body.vel.y += this.body.gravity;     

      // Update entity Position
      this.body.update();

      this._super(me.Entity, "update", [dt])
      return true;;
  }
});
Thus, the following functions have been renamed or assigned through the me.Body child object:

// Physics Old Code -> Physics New Code
this.accel -> this.body.accel
this.canBreakTile -> this.body.canBreakTile
this.disableTopLadderCollision -> this.body.disableTopLadderCollision
this.falling -> this.body.falling
this.gravity -> this.body.gravity
this.jumping -> this.body.jumping
this.maxVel -> this.body.maxVel
this.onladder -> this.body.onladder
this.onslope -> this.body.onslope
this.shapes -> this.body.shapes
this.vel -> this.body.vel
this.addShape -> this.body.addShape
this.getShape -> this.body.getShape
this.setShape -> this.body.setShape
this.setFriction -> this.body.setFriction
this.setMaxVelocity -> this.body.setMaxVelocity
this.setVelocity -> this.body.setVelocity
this.collisionMap -> this.body.collisionMap

// Collision Old Code -> Collision New Code
this.updateMovement() -> this.body.update()
me.game.world.collide(this) -> me.collision.check(this, true, this.collideHandler.bind(this), true)
The melonJS 1.1 has now a full SAT collision algorithm, for polygon collision detection and more accurate collision response. The previous collision check function has therefore been deprecated and replaced by a new me.collision.check function. Refined collision filtering is now possible through the use of me.body.setCollisionMask function (that defines what should collide with what).

// Before melonJS 1.1
var entity = me.ObjectEntity.extend({ 
    init: function(x, y, settings) {
        // Call the constructor
        this.parent(x, y , settings);

        // Default velocity
        this.setVelocity(3, 15);
    },
 
    // Update logic
    update: function(dt) {
        // Change velocity
        this.vel.x -= this.accel.x * me.timer.tick;
    
        // Check for collision with environment
        this.updateMovement();
  
        // Check for collision with enemies or objects
        var res = me.game.world.collide(this);

        if (res) {
            // Change velocity
            this.vel.y -= this.maxVel.y * me.timer.tick;

            // Change position
            this.pos.x -= 20;
        }
  
        this.parent(dt);
        return true;
    },
 
    // Collision callback
    onCollision: function() {
        // Disable collision
        this.collidable = false;
  
        // Remove from game
        me.game.world.removeChild(this);
    }
});

// After melonJS 1.1
var entity = me.Entity.extend({   
    init: function(x, y, settings) {
        // Call the constructor
        this._super(me.Entity, 'init', [x, y , settings]);
       
        // Default velocity
        this.body.setVelocity(3, 15);  
       
        // Set the collision callback function
        this.body.onCollision = this.onCollision.bind(this);          
    },
   
    // Update logic
    update: function(dt) {
        // Change velocity      
        this.body.vel.x -= this.body.accel.x * me.timer.tick;
            
        // Check for collision with environment
        this.body.update();
       
        // Check for collision with enemies or objects
        me.collision.check(this, true, this.collideHandler.bind(this), true);
           
        this._super(me.Entity, "update", [dt]);
        return true;       
    },
   
    // Collision handler
    collideHandler: function(response) { 
        // Change velocity    
        this.body.vel.y -= this.body.maxVel.y * me.timer.tick;                 

        // Change position
        this.pos.x -= 20;

        // Update the entity bounds since we manually changed the position
        this.updateBounds();
    },
   
    // Collision callback
    onCollision: function(res, obj) {
        // Disable collision
        this.body.setCollisionMask(me.collision.types.NO_OBJECT);
       
        // Remove from game
        me.game.world.removeChild(this);
    }
});
In preparation to support WebGL, the me.video is now agnostic of rendering target, by adding the me.CanvasRenderer object,  passing it to the draw calls of all the objects added to the world container and other subsequent containers.

// Before melonJS 1.1
draw: function(context) {
    // Set the context color
    context.fillStyle = "#FFFFFF";

    // Draw a simple rectangle
    context.fillRect(this.pos.x, this.pos.y, this.width, this.height);
}

// After melonJS 1.1 - Getting directly the context
draw: function(renderer) {
    // Get the renderer context
    var context = renderer.getContext();
   
    // Set the context color
    context.fillStyle = "#FFFFFF";
   
    // Draw a simple rectangle
    context.fillRect(this.pos.x, this.pos.y, this.width, this.height);
}

// After melonJS 1.1 - Using me.CanvasRenderer methods (recommended)
draw: function(renderer) {
    // Draw a simple rectangle, using the integrated function
    renderer.fillRect(this.pos.x, this.pos.y, this.width, this.height, "#FFFFFF");
}

Work Done


After the completion of the steps above, you must run the game performing various tests to detect possible adjustments or fixes to be made. For possible questions, use the melonJS Forum or analyze directly the source code on GitHub Repo.

Even with the extra work, the upgrade compensates for the awesome collision detection, fast inheritance, bug fixes and performance enhancements, in addition to being better prepared for the next melonJS version (in development), with the replacement of the current limited tile collision layer by a shape based collision layer and the probable addition of WebGL support! And you can enjoy to review some "obscure" or "hackish" code, using the best practices you acquired in recent times or to implement new features in your game.

Upgrading to melonJS 1.0


With the release of melonJS 1.0, is time to conduct a upgrade to this new version of the engine, packed with several new features, as:

- Better audio support
- Proper support for all shapes type
- Embedded particle engine
- Better Core/Main Loop logic
- Performance improvements on the object pooling
- Support for HTML5 Fullscreen, Vibrate and VisiblityChange APIs
- Improved Camera management
- Several Tweaks and Fixes

Check the melonJS Blog for a official announcement and get the lastest version in melonJS Site.

The recommended is to migrate the  current version used in the game to one immediately more recent facilitating the upgrade process (some melonJS versions have API breaks). For example, if your game uses melonJS 0.9.7, you need to upgrade to versions 0.9.8 -> 0.9.9 -> 0.9.10 -> 0.9.11 and 1.0.2.

Below we will check some of the necessary changes to upgrade from melonJS 0.9.11 (previous version) to melonJS 1.0.2 (current version when writing this post). For a complete list of changes visit the melonJS Upgrade Guide.


Before the Upgrade


Starting from melonJS 1.0, the following pattern will be used for future releases:

Maintenance Releases (1.0.x): This is for bug fixes and with easy update process without the need for adjustment in your current game code (just replace the current melonJS file to the updated one).

Major Releases (1.x.x): This is for new releases filled with new features and can include multiple breaking changes forcing an upgrade in the game code (you need to make some adjustments in your game).

Before starting the migration, check the Upgrade Guide and the Changelog of engine and decide if the amount of work for the update compensates the incredible new features of each melonJS version!

Don't forget to always have a backup of the last game version before starting the upgrade process!


Piece of Cake


Start renaming the functions and variables changed in this version, which can be easily performed via a Search and Replace (or Refactor) available in your preferred Code Editor (or IDE):

// Old Code -> New Code
me.game.remove -> me.game.world.removeChild
me.game.add -> me.game.world.addChild
me.game.collide -> me.game.world.collide
me.game.getEntityByProp -> me.game.world.getChildByProp
me.save.delete -> to me.save.remove
me.entityPool.newInstanceOf -> me.pool.pull
me.entityPool.add -> me.pool.register
me.Rect.set -> me.Rect.setShape
me.Font.set -> me.Font.setFont
In addition to the above changes, the "event type" function param to register and unregister mouse events also changed from mousedown/mousemove to pointerdown/pointermove:

// Before melonJS 1.0
me.input.registerPointerEvent('mousedown', this, this.mouseDown.bind(this));
me.input.registerPointerEvent('mousemove', this, this.mouseMove.bind(this));
me.input.releasePointerEvent('mousedown', this);
me.input.releasePointerEvent('mousemove', this);

// After melonJS 1.0
me.input.registerPointerEvent('pointerdown', this, this.mouseDown.bind(this));
me.input.registerPointerEvent('pointermove', this, this.mouseMove.bind(this));
me.input.releasePointerEvent('pointerdown', this);
me.input.releasePointerEvent('pointermove', this);

Let's Rock


The Object update() function now has a dt (or delta) parameter that provides the elapsed time since the last update in milliseconds (this is a nice feature for timers or for objects sync). If you redefine the update function in your own object, be sure to pass the parameter when calling the parent function. Furthermore, for better standardization, change the updatefunction() to update: function (dt):

// Before melonJS 1.0
update: function() {
    this.updateMovement();
    if (this.vel.x != 0 || this.vel.y != 0) {
        this.parent();
        return true;
    }
    return false;
}

// After melonJS 1.0
update: function(dt) {
    this.updateMovement();
    if (this.vel.x != 0 || this.vel.y != 0) {
        this.parent(dt);
        return true;
    }
    return false;
}
Other change is the visible property was eliminated from the me.Renderable. To make an object invisible, the recommended way is to remove the object from the game (using me.game.world.removeChild) or make it fully transparent (using setOpacity). Another option is to set a "visible" variable in the object init and use it in the draw function:

game.myRenderable = me.Renderable.extend({
    // Constructor
    init: function() {
        // Set the object screen position
        this.parent(new me.Vector2d(80, 80), 200, 100);

        // Change renderable visibility
        this.visible = true; 
    },

    // Update logic
    update: function(dt) {
        return true;
    },

    // Draw canvas
    draw: function(context) {
        // Check object visibility
        if (this.visible) {
             // Change the canvas color
              context.fillStyle = '#fff';

             // Draw a simple rectangle
             context.fillRect(this.pos.x, this.pos.y, this.width, this.height);
        }
    }
});

Come Get Some


To avoid several small bugs difficult to fix, the me.ScreenObject no longer acts as a Renderable, i.e., the init, update and draw functions were removed, disabling the same being added to the world container.

// Before melonJS 1.0
game.TitleScreen = me.ScreenObject.extend({
    // Constructor
    init: function() {
      this.parent(true);
     
      // Load the background image
      this.bkg = me.loader.getImage("background");
     
      // Create font
      this.font = new me.Font("Arial", "20px", "#000000", "center");
    },
   
    // Run every time the screen is displayed
    onResetEvent: function() {
      // Register input keys
      me.input.bindKey(me.input.KEY.ENTER, "play", true);
      
      // Play title track
      me.audio.playTrack("title", false);
    },
   
    // Run when changing state
    onDestroyEvent: function() {
      // Unregister input keys
      me.input.unbindKey(me.input.KEY.ENTER);
      
      // Stop title track
      me.audio.stopTrack("title");
    },
   
    // Update logic
    update: function() {
      if (me.input.isKeyPressed("play")) {
        me.state.change(me.state.PLAY);
      }
     
      return true;
    },
   
    // Draw canvas
    draw: function(context) {
      // Draw the background     
      context.drawImage(this.bkg, 0,0);
     
      // Draw text message    
      this.font.draw(context, "PRESS ENTER TO PLAY", 400, 300);
    }
});
This change requires redesigning almost all screens and using a distinct object (me.Renderable with update/draw pattern) for each item on the screen:

// After melonJS 1.0
game.TitleScreen = me.ScreenObject.extend({       
    // Run every time the screen is displayed
    onResetEvent: function() {
      // Load the background image
      var bkg = new me.SpriteObject(0, 0, me.loader.getImage("background"));
     
      // Added the background image in the game
      me.game.world.addChild(bkg, 1);
     
      // Create a static text
      var text = new me.Renderable.extend({       
        init: function() {
            this.parent(new me.Vector2d(0, 0), 0, 0);
            this.font = new me.Font("Arial", "20px", "#000000", "center");
        },              
   
        update: function(dt) {
            return true;
        },
       
        draw: function(context) {
            this.font.draw(context, "PRESS ENTER TO PLAY", 400, 300);           
        }       
      });
     
      // Added the text in the game
      me.game.world.addChild(text, 2);
       
      // Register input keys
      me.input.bindKey(me.input.KEY.ENTER, "play", true);
     
      // Check for key press
      this.handler = me.event.subscribe(me.event.KEYDOWN, function (action, keyCode, edge) {
        if (action === "play") {               
            me.state.change(me.state.PLAY);
        }
      });
     
      // Play title track
      me.audio.playTrack("title", false);
    },
   
    // Run when changing state
    onDestroyEvent: function() {
      // Unregister input keys
      me.input.unbindKey(me.input.KEY.ENTER);
      me.event.unsubscribe(this.handler);
     
      // Stop title track
      me.audio.stopTrack("title");
    }
});
Another big change with melonJS 1.0 was the entity collision properties (collisionBox) replaced by a more "standardized" approach through the use of collision shapes, preparing the engine for the new collision system to be made ​​in version 1.1. The updateColRect and adjustSize functions have been removed and was added the addShapesetShape and getShape functions. For a detailed overview, check the Shapes Entry in the melonJS wiki.

Work Done


After the completion of the steps above, you must run the game performing various tests to detect possible adjustments or fixes to be made. For possible questions, use the melonJS Forum or analyze directly the source code on GitHub Repo.

Even with the extra work, the upgrade compensates for the new features, bug fixes and performance enhancements, especially on mobile devices. In addition, you can enjoy to review some "obscure" or "hackish" code, using the best practices you acquired in recent times or to implement new features in the game.

Vibrant Recycling 1.1.0 Launched


Vibrant Recycling 1.1.0 launched, with New Levels!

★ Two new scenarios: Forest (fewer levels) and City (increased difficulty)
★ Added 20 new levels
★ Better audio support for mobile
★ Minor tweaks