Banner
AS3: Simple Light and Shadows

Game developers spend a lot of time thinking about light. The reason is simple: light can make or break a game. Light can be used both for effect and as a gameplay element- darkness can be used to mask enemies and provide surprise, and simple effects like a flash of white coupled with a clap of thunder can provide a sense of mood.

In this tutorial I will be going through some basic tactics for creating light and shadows in an ActionScript 3 based Flash game.

I'll start off by saying that there are a number of different ways to do this, and this is probably one of the laziest. The really ambitious way to create lights and shadows in Flash would be to dynamically draw everything using the flash.graphics methods, but I'm aiming for something that isn't going to make our heads explode. The method I'll be covering today just uses a couple clips in the library, a dynamic mask, and just enough math to glue it all together.

Getting Started

First, let's see what the finished product will look like. There's really no reason for you to continue reading this tutorial if you think that my method looks like crap, is there?

To move your character, click on the screen and use the arrow keys to move around. Your mouse controls the flashlight.

This example is basically composed of six classes, the hero, the enemy, the flashlight, the shadows, the light mask, and the background. As the hero moves each enemy will update to have its shadow point away from the player. This is as simple as rotating the shadow to face away from the hero. In order to show the flashlight I use a mask that shows the background texture, this mask lines up with a clip that fades at the edges to give the impression of the light fading.

Now, without the black background it becomes a bit more apparent what is going on behind the scenes:

On the white background you can see that I am just turning the shadows away from the player based on the relative position of the enemy to the player. The flashlight is just a masked area that shows the brick below it.

The first thing that I did was create a background for the player's light to illuminate. For this I just imported a brick texture and created a movie clip for it. If you check in the library of LightSources.fla you will see a Sprite named Background. All this is used for is a floor texture. I add it to the stage first in LightSource.as because it will be drawn underneath everything else.

The next thing I do in LightSource.as is create holders for each of our different object types. I like to use arrays for each type of object because I find it makes it easy to walk through them. In this case I have enemies, shadows, and penums. Penums is sort of a strange one, each enemy actually has not one, but two shadows. The reason I do this is to give the effect of a penumbra, which is basically a partially shadowed region at the edges of the shadow. This isn't really necessary, but I find it adds a nice touch.

One of the keystones of this method is a very simple function I call CloneSprite. Basically all this does is copies the x, y, and rotation from one sprite to another. The reason this is so handy is because the flashlight and light mask always need to have the same position and rotation as the hero. I also use this to establish the initial positions of all the shadows.

//Duplicate sprite x, y, and rotation
private function CloneSprite(cloneFrom:Sprite, cloneTo:Sprite):void
{
	cloneTo.x = cloneFrom.x;
	cloneTo.y = cloneFrom.y;
	cloneTo.rotation = cloneFrom.rotation;
}

The meat of this technique is the Update function, which is run by a timer. The Update function goes through each enemy and rotates the shadows away from the player. One thing to note when rotating things in flash is that when you get the angle to an object using Math.atan2 the result is returned in radians, not degrees, so you have to convert the resulting value by multiplying it by 180 over PI. I like to create a constant for this to save a bit of memory, so the Flash player doesn't have to divide by PI a dozen times every Update cycle.

private function Update(evt:TimerEvent):void 
{
	stage.focus = this;
	
	//Move the hero
	hero.x += heroVX;
	hero.y += heroVY;
	
	//Position the light and the light mask based on mouse position relative to hero
	CloneSprite(hero, light);
	light.rotation = (Math.atan2(-(mouseX - hero.x), mouseY - hero.y) * convertRad) - 180;
	CloneSprite(light, lightmask);
	
	//Position the shadows
	for each (var s:Shadow in shadows)
	{
		s.rotation = (Math.atan2( -(light.x - s.x), light.y - s.y) * convertRad);
		s.scaleX = Math.min(100, Distance(s, hero)) / 100;
		s.alpha = Math.min(100, Distance(s, hero)) / 100;
	}
	
	//Position the penumbras
	for each (var p:Shadow in penums)
	{
		p.rotation = (Math.atan2( -(light.x - p.x), light.y - p.y) * convertRad);
		p.alpha = Math.min(100, Distance(p, hero)) / 100;
	}
}

As the player gets closer to the enemy I change the alpha on the shadow a bit to make it seem like the light is a bit stronger closer to the source, and I also change the scaleX to make the shadow slim down a bit. For the penumbras I just left out the scaling.

Rotate cone from baseOne of the important things to note about the way I have set upo my lights and shadows is that they rotate from the base of the cone. When you first create the flashlight, light mask, and shadows you need to make sure that their centre is near the narrow portion of the cone.

And that's about it. If you have any questions be sure to comment below, I will try to get back to you as soon as possible!


blog comments powered by Disqus
 

Share

Development Tutorial Updates

EgoAnt Development Tutorials

Game Profile

Tilt!

Tilt was a fun game to create, it presented a lot of challenges for me as a developer, as I wanted to ensure that I had an easy to use level builder to go along with it.

Play Tilt!