Showing posts with label melonJS. Show all posts

Upgrading to melonJS 2.1


With the release of melonJS 2.1, is time to conduct a upgrade to this solid version of the engine, with nice features as:

- New screen independent scaling methods
- Improved collision handling and detection
- Support for Physics Editor tool, to define entities collision shapes
- Added support for pointerenter and pointerleave events
- High-precision color transform with more accurate color effects
- Several improvements in the WebGL renderer (reached a beta quality level)
- Added support for the new Tiled 0.11 object id property
- 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 1.0.2, you need to update first to 1.1.0 version (check the post Upgrading to melonJS 1.1), after to 2.0.2 version (check the post Upgrading to melonJS 2.0) and finally to 2.1.3 version.

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


Before the Upgrade


The melonJS 2.1 is a major release and include some breaking changes which forces you to make 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 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
spritewidth -> framewidth
spriteheight -> frameheight
me.TextureAtlas -> me.video.renderer.Texture
me.video.shader.gltexture2d() -> me.video.shader.createTexture()
The me.video.init has arguments more flexible and robust requiring three params: width, height and a list of properties for the various options (the pattern function(x, y, {settings}) for constructors signature will be applied to others melonJS functions in future):

// Before melonJS 2.1
me.video.init("screen", me.video.CANVAS, 800, 600, true, "auto", false);

// After melonJS 2.1
me.video.init(800, 600, {
    wrapper: "screen", 
    renderer: me.video.CANVAS,     
    scale: "auto", 
    scaleMethod: "fill-max",
    doubleBuffering: true,
    transparent: true
}); 
The new and functional scaling mode is enabled through the scaleMethod param set in me.video.init and are detailed in the melonJS Blog:

- fit (default): Letterboxed; content is scaled to design aspect ratio.
fill-max: Canvas is resized to fit maximum design resolution; content is scaled to design aspect ratio.
- flex-width: Canvas width is resized to fit; content is scaled to design aspect ratio.
flex-height: Canvas height is resized to fit; content is scaled to design aspect ratio.
- stretch: Canvas is resized to fit; content is scaled to screen aspect ratio.

Check also the addition of the "transparent" parameter to enable support for the new opaque canvas feature and the removal of the old maintainAspectRatio replaced by the new scaling method.

The me.sys.scalingInterpolation setting for enable or disable the anti-aliasing is avaliable now directly in the me.video.init antiAlias params:

// Before melonJS 2.1
me.sys.scalingInterpolation = true;

// After melonJS 2.1
me.video.init(800, 600, {
    wrapper: "screen", 
    renderer: me.video.CANVAS,
    doubleBuffering: true, 
    scale: "auto",      
    antiAlias: true
});


Let's Rock


In order to standardize objects constructor in melonJS, me.Sprite, me.AnimationSheet and me.ImageLayer signature have been update to follow the pattern function(x, y, {settings}):

// Before melonJS 2.1
var sprite = new me.Sprite(20, 80, me.loader.getImage("image"));
var animationSheet = new me.AnimationSheet(30, 50, image: me.loader.getImage("animationsheet"), spritewidth: 32, spriteheight: 32});

// After melonJS 2.1
var sprite = new me.Sprite(20, 80, {
   image: me.loader.getImage("image")
});

var animationSheet = new me.AnimationSheet(30, 50, {
  image: me.loader.getImage("animationsheet"),
  framewidth: 32,
  frameheight: 32
});
Most keywords and name lookups have been made case-sensitive now. This affects file names, pooling, and container child searches and may give more or less work as the patterns used in your project. To perform a case-insensitive search, you can use Javascript RegExp in container child searches:

// Before melonJS 2.1 - Get objects named "Player" or "player"
me.game.world.getChildByProp("name", "PLAYER");

// After melonJS 2.1 - Use the RegExp for Case Insensitive Search
me.game.world.getChildByProp("name", /player/i);
Lastly, remove the references to body.updateBounds() because this function is automatically called by the engine now, providing an extra facility to the developer.


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.

The practical and functional screen scaling modes, the better support to WebGL and the easy migration proccess (if compared with the whirlwind of changes and new features introduced with melonJS versions 1.1 and 2.0), make the upgrade a good and right choice. 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 2.0


With the release of melonJS 2.0, is time to conduct a upgrade to this solid and awesome version of the engine, packed with nice features, as:

- New Shape based collision (replaced the Tile based collision)
- Added support for Tiled 0.10 allowing shape transforms (scaling and rotation) and TMX Tileset Animations
- Automatic collision response handling
- Physics bodies now support multiple shapes
- Many fixes and improvements with collision detection in isometric maps
- Initial WebGL support (alpha quality)
- Fixed various bugs and issues

Check the 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 1.0.2, you need to update first to 1.1.0 version (check the post Upgrading to melonJS 1.1) and after to 2.0.2 version.

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


Before the Upgrade


The melonJS 2.0 ends a cycle of major changes started from version 1.0, in order to make the engine most concise, fast, robust, cleaner and future proof, with the replacement of the naive collision mechanism. It's a major release and include some breaking changes so you should do adjustments in your game code. Probably, the most work to be done for this version is the adjustment of all Tiled maps, due the replacement of Tile based collision to Shape based collision, finalizing the implementing of the new collision mechanism!

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
debugPanel -> me.debug.Panel
me.Sprite.resize() -> me.Sprite.scale()
me.PolyShape -> me.Polygon
me.game.world.collide -> me.collision.check
The me.Body now supports multiple shapes and the me.Body.getShape() function now needs an index as argument, returning the shape specified at the index:

// Before melonJS 2.0 - Body has only one shape
var bounds = this.body.getShape().bounds;

// After melonJS 2.0 - Body has only one shape, see index = 0
var bounds = this.body.getShape(0).bounds;
The functions flipX and flipY have been removed from me.Entity and me.Body and are available only through the entity renderable component:

// Before melonJS 2.0
game.Entity = me.Entity.extend({
    init: function(x, y, settings){
        // Call the constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        // Set the entity renderable
        this.renderable = game.texture.createSpriteFromName("sign.png");

        // Check for entity renderable flip - passed as constructor param
        this.flipX(settings.flip); 
    }
});

// After melonJS 2.0
game.Entity = me.Entity.extend({
    init: function(x, y, settings){
        // Call the constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        // Set the entity renderable
        this.renderable = game.texture.createSpriteFromName("sign.png");

        // Check for entity renderable flip - passed as constructor param
        this.renderable.flipX(settings.flip); 
    }
});
Update the HUD pattern making it a floating container, because a bug in previous version allowed non-floating containers to always be within the viewport. For this, only add this.floating = true; in the HUD Container init function.


Let's Rock


With the new collision mechanism, the entities now automatically respond to collision, not requiring that me.Body have a explicit onCollision function callback, being necessary directly in me.Entity:

// Before melonJS 2.0
var entity = me.Entity.extend({
    init: function(x, y, settings) {
        // Call the constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        // Set the 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;
 
        // Apply physics to the body (this moves the entity)
        this.body.update();

        // Handle collisions against other shapes
        me.collision.check(this, true, this.collideHandler.bind(this), true);

        this._super(me.Entity, "update", [dt]);
        return true; 
    },

    // Collision handler
    collideHandler: function(response) {
        // Check for collision with enemy
        if (response.b.body.collisionType === me.collision.types.ENEMY_OBJECT) { 
            // 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 this way, the callbacks used in this.body.onCollision and me.collision.check() are joined into a single callback onCollision defined in me.Entity (check the new signature of me.collision.check() function in the Online Documentation):

// After melonJS 2.0
var entity = me.Entity.extend({
    init: function(x, y, settings) {
        // Call the constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        // Set the default velocity
        this.body.setVelocity(3, 15);       
    },
   
    // Update logic
    update: function(dt) {
        // Change velocity 
        this.body.vel.x -= this.body.accel.x * me.timer.tick;
 
        // Apply physics to the body (this moves the entity)
        this.body.update();

        // Handle collisions against other shapes
        me.collision.check(this);

        this._super(me.Entity, "update", [dt]);
        return true; 
    },    

    // Collision callback
    onCollision: function(response, other) {
        // Check for collision with enemy
        if (other.body.collisionType === me.collision.types.ENEMY_OBJECT) { 
            // 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();
        }

        // Disable collision
        this.body.setCollisionMask(me.collision.types.NO_OBJECT);

        // Remove from game
        me.game.world.removeChild(this);
    }
});
The Number.prototype.random(min, max) function has changed to match the standard Javascript function Math.random(), using the range [min, max), i.e, from min (inclusive) up to but not including max (exclusive):

// Before melonJS 2.0 - Random Number between 0 and 5
var random = Number.prototype.random(0, 5);

// After melonJS 2.0 - Random Number between 0 and 5 (Normal Syntax)
var random = Number.prototype.random(0, 6);

// After melonJS 2.0 - Random Number between 0 and 5 (Short Syntax)
var random = (0).random(6);


Come Get Some


Completing the new changes to collision system, the legacy Tile based collision mechanism has been replaced with a Shape based collision mechanism, using the same SAT implementation of the entities (added in version 1.1). This change requires adjusting the layer collision of all maps from Tiled, which can be a time consuming work, depending on the complexity and number of maps!

Before melonJS 2.0, you used a Tiled Tile Layer to define the world collision shapes, in accordance with the tiles types supported by engine (using the "metatiles" solid, platform, ladder, slope and breakable):

Tiled Level with Tiles Collision Layer

After melonJS 2.0, you should use a Tiled Object Layer and the world collision shapes can be defined using all standard objects from Tiled, like Polyline, Polygon (only convex with clockwise winding) and Ellipse, giving greater freedom and creativity to define the game collisions, not getting stuck with fixed shapes!

Tiled Level with Shape Collision Layer

Remember that you must use a Tiled Object Layer with name "collision" so the engine detects the collision layer properly. With this change in collision shapes the engine no more support builtin Platforms and Ladders tiles and they must be implemented using the new onCollision response callback:

// melonJS 2.0 - me.Entity onCollision callback
onCollision: function(response, other) {
    if (other.body.collisionType === me.collision.types.WORLD_SHAPE) {
        // Simulate a platform - property defined in Tiled Object
        if (other.type === "platform") {
            if (this.body.falling && !me.input.isKeyPressed('down') &&
               (response.overlapV.y > 0) && 
               (~~this.body.vel.y >= ~~response.overlapV.y)) {
                // Disable collision on the x axis
                response.overlapV.x = 0;

                // Respond to the Platform (it is solid)
                return true;
            }

            // Don't respond to the Platform (pass through)
            return false;
        }
    }

    // Make the object solid
    return true;
}
With the new Shape collision mechanism, some functions related to collisions have been removed from engine:

// Functions removed from Engine
me.Body.onslope
me.Body.onladder
me.Body.disableTopLadderCollision
me.Body.canBreakTile
me.Body.onTileBreak
me.Body.collisionMap
me.game.collisionMap
These functions can now be implemented through smart use of the onCollision callback with a similar logic as previously demonstrated to simulate a platform.


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 shape based collisions, bug fixes and performance enhancements. 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.

The melonJS 2.0 is a solid and stable version, demonstrating the maturity and evolution that the engine achieved during the last three years. Congrats melonJS Team for the job well done!

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.