How to create light effects in Phaser 3

2018-03-01 Joachim Grill Get Sourcecode from GitHub

Prerequisites

You already have normal maps for your sprites.

To learn how to create normal maps have a look on this tutorial.
For more details about the SpriteIlluminator tool visit its documentation page.

Sprite sheets require a corresponding normal map sheet

The sprite sheet containing the normal maps must be packed with the same layout as the sheet containing the sprite images. TexturePacker has a dedicated mode to support this. This tutorial describes normal map packing in detail.

Throw light on your sprite

To load a sprite image together with its normal map, the file names of the image and its normal map are passed to this.load.image() as an array. In the following example the normal maps are marked with a _n suffix.

After loading the images, the sprite can be placed as usual. To enable dynamic lighting the corresponding shaders are activated using setPipeline('Light2D'). Important: Light effects require a browser with WebGL support.

function preload()
{
    this.load.image('character', ['assets/character/01.png', 'assets/character/01_n.png']);
}

function create()
{
    var character = this.add.sprite(250, 300, 'character');
    character.setPipeline('Light2D');
}

As we haven't added any light source yet, the scene remains black. Add these to lines to the create() function to switch on a light:

    var light  = this.lights.addLight(500, 250, 200);
    this.lights.enable().setAmbientColor(0x555555);

This adds a point light at position (500,250) with radius 200. The ambient light increases the brightness of all pixels of the scene, independent of the position of the point light.

How to use sprite sheets with normal maps

For sprite sheets the normal maps must also be packed on a sheet. It must be loaded with a separate this.load.image() call. Then we have to link both textures:

function preload()
{
    this.load.atlas('character_sheet', 'assets/character.png', 'assets/character.json');
    this.load.image('character_sheet_n', 'assets/character_n.png');
}

function create()
{
    var normals = this.textures.get('character_sheet_n').getSourceImage();
    this.textures.get('character_sheet').setDataSource(normals);
}

Adding sprites and animations works as usual, it's just the setPipeline('Light2D') call which is additionally needed to enable lighting:

    // add sprite from sheet
    var character_anim = this.add.sprite(550, 300, 'character_sheet', '01');
    character_anim.setPipeline('Light2D');

    // play animation
    var frameNames = this.anims.generateFrameNames('character_sheet', { start: 1, end: 8, zeroPad: 2 });
    this.anims.create({ key: 'walk', frames: frameNames, frameRate: 10, repeat: -1 });
    character_anim.anims.play('walk');

Play around with it

To illuminate the sprites from different directions we attach the light position to the mouse pointer:

    this.input.on('pointermove', function (pointer) {
        light.x = pointer.x;
        light.y = pointer.y;
    });

Here's the result:

Finally

That's all for this tutorial. You should now have learned how you use normal maps for dynamic lighting and how to use sprite sheets with normal maps.

Did you like the tutorial? Please share!

Source code available for download

The source code is available on GitHub. Clone it using git:

git clone https://github.com/CodeAndWeb/Phaser3-Lighting.git

or download one of the archives:

Phaser3-Lighting.zip Phaser3-Lighting.tar.gz