Search code examples
javascripthtml5-canvasmelonjs

How to change color of a sprite over time in melonJS


I want to add some trail effect to a moving object that will fade over time. This is what I've got so far:

game.Trail = me.Entity.extend({

  init:function (x, y, settings)
  {
    this._super(me.Entity, 'init', [
      x, y,
      {
        image: "ball",
        width: 32,
        height: 32
      }
    ]);

    (new me.Tween(this.renderable))
    .to({
        alpha : 0,
    }, 5000)
    .onComplete((function () {
        me.game.world.removeChild(this);
    }).bind(this))
    .start();
  },

  update : function (dt) {
    this.body.update(dt);
    return (this._super(me.Entity, 'update', [dt]) || this.body.vel.x !== 0 || this.body.vel.y !== 0);
  }
});

Demo (move with WASD or arrow keys)

Here is a link to the full project to test locally.

But I want to change the colors of the items in the trail in the same way the fading is done.

In phaser this could be done tinting the sprite, but I have no clue about how to achieve that on melonjs.

Note: if the effect can be done with basic shapes instead of images that will work too.


Solution

  • With the melonJS canvas renderer, you'll have to add tinting by overriding the draw method on your sprite or renderable object. The CanvasRenderingContext2D API has some useful utilities for doing RGBA fills and so on that can tint the destination canvas. Since "tinting" is not built into melonJS, you'll need to keep a temporary canvas context to tint your sprites non-destructively.

    Minimal example (non-destructive, but does not handle transparency well):

    draw : function (renderer) {
        renderer.save();
    
        // Call superclass draw method
        this._super(me.Entity, "draw", [ renderer ]); // XXX: Assumes you are extending me.Entity
    
        // Set the tint color to semi-transparent red
        renderer.setColor("rgba(255, 0, 0, 0.5)");
    
        // Fill the destination rect
        renderer.fillRect(this.pos.x, this.pos.y, this.width, this.height);
    
        renderer.restore();
    }
    

    A more involved option is using the CanvasRenderingContext2D API to create the temporary canvas context; copy the original sprite to the texture, apply the tint while respecting transparency, using clip if you have to.


    In the melonJS WebGL renderer, you just have to change the value of the global renderer color before the draw and restore the original value after. Minimal example:

    draw : function (renderer) {
        renderer.save();
    
        // Set the tint color to semi-transparent red
        renderer.setColor("rgba(255, 128, 128, 1)");
    
        // Call superclass draw method
        this._super(me.Entity, "draw", [ renderer ]); // XXX: Assumes you are extending me.Entity
    
        renderer.restore();
    }
    

    This works in WebGL because the shader is already setup to multiply the source image by the global color value. You'll get a different color result from the CanvasRenderer option above because WebGL is happiest with premultiplied alpha. (In this example, the value of the blue and green components in the source image will be reduced by half, making the sprite appear more red.)

    Feel free to play with it a bit, but in general you'll get far more control over rendering in WebGL, and in fact you have the option to customize the compositor and shaders if you need to do really crazy effects.