Upgrading to melonJS 1.0

August 27, 2014 André Schmitz 0 Comments


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.

0 comentários :