How to use dynamic 2D lighting in Cocos Creator

What you will learn
- Create normal maps and sprite sheets with SpriteIlluminator and TexturePacker.
- Load animation frames from a sprite sheet.
- Load a normal-map sprite sheet and add a Light2D effect to your animation.
Create your normal maps
The easiest way to create normal maps for your sprites is to use SpriteIlluminator. For a quick start:
- Drag and drop your sprites into the SpriteIlluminator window.
- Select all sprites.
- Apply the effects you like, e.g. Bevel, Emboss, or Structure.
- Publish the normal map images to the sprite directory using the
_nsuffix.
Create the sprite sheets
With TexturePacker you can pack sprites and normal maps on two different sprite sheets, using the same layout:
- Start TexturePacker and create a new Cocos2d project.
- Drag and drop sprites and normal maps into the TexturePacker window.
- Enable the "Pack with same layout" option in the Normal Maps section.
- Publish the sprite sheets.
Install Cocos Creator and the Light2D plugin
-
Go to the official Cocos Creator website and click the Download Cocos Dashboard button. Your browser will start the download automatically.
-
After installing Cocos Dashboard, you may need to register an account and log in.
-
Install the editor. In the left sidebar, click Installs, then click Install Editor in the upper-right corner. In the version list, choose 3.8.6. The editor downloads and installs automatically; you will receive a system notification when it is done.
-
Install the Light2D plugin. In the left sidebar, click Store, search for Light2D, and complete the installation and import steps.
Create the game scene
-
In the editor, create a new empty scene named
scene_game. Delete the default MainLight and MainCamera, and create a new UI Components → Canvas. -
Copy the LightSystemKeep node from the sample scene Scene_Light2D into your
scene_gamescene. -
Drag the prepared texture assets into the Canvas node. You will see the distant sky/mountains and the foreground ground/rocks/trees.
Add light sources
-
Select these nodes and add the Light2DReceiver component to them. The scene becomes darker because they are now affected by the global ambient light.
-
Under the Canvas node, create a new empty node named Light-Point and add the Light2DSource component to it. You will see a circular area in the center of the scene illuminated by the default point light. Adjust the parameters to see the effects.
-
Set the ReceiverLayer of the foreground nodes to LAYER1, and set the AffectLayerList of the light source to only LAYER1. This prevents the distant sky background from being illuminated.
-
For the foreground nodes, enable EnableNormalMap and assign the corresponding normal map texture. Move the light source node to observe the effect.
Create the character and animation
-
Drag a frame of the character asset into the scene as a new node named Role, and add the Animation component to it.
-
Create the corresponding frame animation AnimationClip. When done, click the Run button at the top of the editor to see the character walking in place.
Light the character
-
Add the Light2DReceiver component to Role, and set its ReceiverLayer to LAYER1.
-
Enable EnableNormalMap for Role and assign the corresponding normal map texture.
Make the character move
Add a movement script component, Role.ts, to the Role character so it moves to the right for a certain distance, then turns around and moves back, looping forever.
import { _decorator, Component, tween, Vec3 } from 'cc';
const { ccclass } = _decorator;
@ccclass('role')
export class role extends Component {
start() {
tween(this.node)
.by(16, { position: new Vec3(3400, 0, 0) })
.call(() => {
this.node.setScale(this.node.scale.x * -1, this.node.scale.y);
})
.by(16, { position: new Vec3(-3400, 0, 0) })
.call(() => {
this.node.setScale(this.node.scale.x * -1, this.node.scale.y);
})
.union()
.repeatForever()
.start();
}
}Add a follow script component, FollowForever.ts, to the Camera node, and set Target Node to the Role node.
import { _decorator, Component, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('FollowForever')
export class FollowForever extends Component {
@property(Node)
public targetNode: Node = null;
@property({})
public xFollow = true;
@property({})
public yFollow = true;
private _calcPos: Vec3 = new Vec3();
protected onEnable(): void {}
protected update(dt: number): void {
if (this.targetNode) {
const targetPos = this.targetNode.worldPosition;
const currentPos = this.node.worldPosition;
Vec3.moveTowards(this._calcPos, currentPos, targetPos, 100);
this.node.setWorldPosition(
this.xFollow ? this._calcPos.x : currentPos.x,
this.yFollow ? this._calcPos.y : currentPos.y,
currentPos.z
);
}
}
}Add a rotation script component, RotateForever.ts, to the Light-Point node, and set Target Node to the Role node.
import { _decorator, Component, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('RotateForever')
export class RotateForever extends Component {
@property(Node)
public targetNode: Node = null;
@property
public degSpeed: number = 90;
private radius = 0;
private angle = 0;
protected onEnable(): void {
if (this.targetNode) {
const targetPos = this.targetNode.worldPosition;
const currentPos = this.node.worldPosition;
const delta = Vec3.subtract(new Vec3(), targetPos, currentPos);
this.radius = delta.length();
this.angle = Math.atan2(delta.y, delta.x) * (180 / Math.PI) + 180;
}
}
protected update(dt: number): void {
if (this.targetNode) {
this.angle = (this.angle + dt * this.degSpeed) % 360;
const rad = this.angle * (Math.PI / 180);
const x = this.radius * Math.cos(rad);
const y = this.radius * Math.sin(rad);
const targetPos = this.targetNode.worldPosition;
this.node.setWorldPosition(targetPos.x + x, targetPos.y + y, 0);
} else {
this.node.angle = (this.node.angle + dt * this.degSpeed) % 360;
}
}
}-
Adjust the light source parameters: set InnerRadius to 4, OuterRadius to 900, LightIntensity to 2, and Falloff to 1 so the light covers a larger area and the attenuation is more natural.
-
Add more background and foreground nodes to expand the scene.
Run and preview
Click Run to see the vivid lighting scene.