Working on JavaScript lighting

Posted on March 28, 2012 | 7 minute read

Over the past couple of days I’ve been experimenting with JavaScript lighting in Impact. It’s been…a process…and it is still very far from complete, but I feel like I’m making progress.

The idea is this: There is an entity on the screen that emits light. By light I don’t mean a glow visual effect that doesn’t really do anything. I mean other elements of the level have to be revealed as the entity moves past them, so it actually has to be turned into a “proper” light source that has an impact on the visibility of other objects.

Requirements:

  1. Parts of the level have to be hidden and revealed depending on the light emitting entity’s position

  2. The level of light has to change according to other attributes that will be changing during run-time

  3. The lightening and darkening of parts of the level has to look smooth and natural - visuals are very important

Right now I have #1 sorted. The other two…are in progress.

Implementation options

Attempt 1 - transparency mask

My first thought was to create a PNG transparency mask. So have a black image that’s the size of the game window with a transparent gradient in the place the light emitting entity would sit. Pros:

  • Easy to implement

  • Gradient makes lighting boundaries look smooth

  • Can animate mask image to make light ‘flicker’ etc

Cons:

  • More difficult to make mask follow the light emitting entity unless the light emitting entity stays in one position on the screen the entire time (with the level background scrolling behind it). This is not the case - the light emitting entity will have to do a lot of moving around.

  • More difficult to implement levels of lighting. We’d need a different mask image for each lighting level

  • From what I can see the mask just does not look as natural as it could when revealing areas of the screen

It looks like this (as a quick example):

[caption id="attachment_47696” align="aligncenter” width="300” caption="Transparency mask”]JavaScript lighting - transparency mask[/caption]

In short, I don’t think this is a good idea. Not that I couldn’t make it work, but I would not be completely satisfied with this option and think that there must be a better way.

Attempt 2 - Modifying background tiles based on adjacency

The second idea I had was to make a new layer in the Weltmeister level editor and assign a tilesheet to it that has a range of tiles from fully transparent to completely black (like a shadow scale). By default the entire level will look black - the shadow map layer will cover all other layers of maps, entities, etc. Then implement the lighting effect by first finding the tile that the light entity is currently in front of and then finding tiles that are adjacent to it, and tile adjacent to those tiles, etc. Proceed by switching out tiles in the shadow map layer with more transparent versions depending on what tiles they are adjacent to - the most transparent tiles would be the closest to the player.

This technically worked, just in a really bad way. Adjacency calculations were imprecise and the tile switching looked way too obvious and unnatural. If I wanted to come up with a way to make the lighting transitions look smooth and natural I’d need to be more precise than just adjacency. I’d need to be able to find the position of every tile on the screen and manipulate that tile according to distance from the light source.

Attempt 3 - calculate precise distances

I got this working, but again it’s still not good enough. Instead of trying to detect what tiles are adjacent to each other, I did this:

  1. Have a for loop generate coordinates of all tiles in the game window (note: not the entire level, which scrolls) by stepping through in a for loop and adding +40px (which is the size of the tiles) to each x and y value until it hits 960 and 640 (which are the width and height of the HTML5 canvas)

  2. Store each of these sets of coordinates in an array

  3. Generate a transparent “grid block” entity, basically just a 40px by 40px invisible square

  4. Spawn instances of this grid block entity at every location in the array

  5. I can now use Impact’s .distanceTo method to calculate the exact distance from every instance of this grid block entity to the light source

  6. From here I can switch out the tiles behind these entities (that are in the same positions) according to how far away the light source is

Steps 1, 2, 3, and 4 above are done like this: [crayon] y: 0, x: 0,

init: function(){ this.grid = []; this.defaults = {lives: this.lives, score: this.score}; for (this.y = 0; this.y < = 640; this.y = this.y + 40) { for (this.x = 0; this.x <= 960; this.x = this.x + 40) { // console.log(this.x,this.y); this.grid.push({x: this.x,y: this.y}); } } for (var i = 0; i < this.grid.length; i++) { ig.game.spawnEntity( EntityGridtile, this.grid[i].x, this.grid[i].y); console.log(‘drawing ' + i); } }, [/crayon]

5 and 6 are in the grid block entity: [crayon] update: function(){ this.vel.x = ig.game.player.vel.x; this.vel.y = ig.game.player.vel.y; this.dist = this.distanceTo( ig.game.player ); var percentage = 100 * this.dist / 960;

 if (percentage < 3) {
      ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 64 );
 }
 else if (percentage < 5) {
      ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 63 );
 }
 else if (percentage < 7) {
      ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 62 );
 }

 // Etc...Eventually if this way worked I'd find a better way of doing the above instead of if statements
 this.parent();

} [/crayon] _ This kind of worked in that it reveals the level as the light source moves past and looks better than method #2 above, but it is still not good enough. It looks like this:

[caption id="attachment_47689” align="aligncenter” width="300” caption="JavaScript lighting with tile replacement”]`[/caption]

This could maybe be polished up, but I think that this method would never create the impression of completely smooth lighting transitions and for this particular game it’s important.

Untangling a working example

There is already a game created with the same engine that has the kind of lighting I’m looking for. Although it’s using lighting as a visual fog of war effect only, which is much more simplistic than the lighting requirements for this game, the shadow tile opacity transitions look smooth and natural as the player moves. This is what we need.

So in an attempt to understand what exactly the creator is doing I’ve been eh…“unbaking” the baked JavaScript code. Basically I need to understand what’s happening there and how this person is achieving the smooth, natural tile transitions in their shadow layer. It’s going to be an effort because the game itself is introducing many new techniques that I’ve never used before.

I finished the detangling of the baked code this morning and in an effort to understand what’s going on the task for tonight is to go through file by file and comment everything to myself, to ensure that I know what everything does (not just the lighting, but all parts of the game). This initially seems like a very roundabout and lengthy way to get to a lighting effect, but I think it will be worth it in the end. The next step will be to strip down everything and leave just the lighting, and then work from there.

Because this lighting is, like I said, still very simplistic compared to what I need (we need to use lighting as a dynamic gameplay element that has a major influence on the player’s experience and actions, not just a visual effect) the code will end up being pretty much deconstructed and put back together with new requirements. But it is going to be a step in the right direction.




Categories:game dev dev games
comments powered by Disqus