Spotlight effect in Starling

The problem: in your Starling app, you need to display your main screen as usual, but with a circular spotlight over it, while the rest of the screen is darkened. This is usually done to highlight an element and direct the user to tap it. Here I will show how to create the ‘spotlight’ effect in Starling for an entire container, without using up huge amounts of memory and without any performance hit.

 

spot02

There are many ways to achieve that, but most of them use custom shaders, or require complex drawing. In classic Flash you could create a vector shape with fillRect in the size of the entire screen, then somehow cut an oval shape in it, and place the shape on top of the display list. Replicating that in Starling would result in  a texture of that size, and that could be a problem in mobile apps that have limited texture space.

This calls for a more elegant solution, so I created this small class to do just that.

The Spotlight Class

The class extends Starling Image. Once initialized, it can be reused for multiple spotlights, with different sizes and ellipse shapes. It takes up a texture that is only a quarter of the screen in size and has a very simple interface.

To use it, simply create an instance of the Spotlight class (just once), providing the stage dimensions, and then call create() on it. Later,  just call updateSpot() with position and size info, like here:

	spotFx= new Spotlight(stage.stageWidth, stage.stageHeight);
	spotFx.create();
	//..
	addChild(spotFx);
	// ...	
	// place spotlight on bird	
	spotFx.updateSpot(bird1.x, bird1.y, 350, 200);

The class code is available on Gist here.

The code makes use of a circle mask image. Create one yourself, or use this one here.

Under the Hood

As already mentioned, the class extends a Starling image since it simply is a container for a texture. In this case, a render texture. When created, it creates a render texture with dimensions of half of the original, or a quarter of the size.

Then it creates an overlay Quad of the same size and sets the color and alpha (default values are dark gray with 70% alpha). Next is the circular mask – it is created from an image and just uses the alpha channel as a pixel mask.

The magic happens here, where we set the blend mode of the mask image to erase and draw it into the render texture:

	mSpot= new Image(Texture.fromBitmap(ovalImg, false));
	mSpot.pivotX= mSpot.width/2;
	mSpot.pivotY= mSpot.height/2;
 
	// Place spot and set blendMode to erase
	mSpot.x = mOverlay.width/2;
	mSpot.y = mOverlay.height/2;
	mSpot.blendMode= BlendMode.ERASE;
 
	// Draw the texture
	mBuffer.drawBundled(drawElements);

This can be used in many different ways. The most common is to update the class instance with the spotlight position and size with subsequent calls to updateSpot().

Note that the class is lightweight on memory and CPU and does not cause any performance hit. After all, this is just an image with a render texture. It is safe to use in Starling apps on mobile or desktop platforms.