Search code examples
actionscript-3flashdynamicgradientalpha

AS3: How do I implement a dynamic radial alpha gradient to obfuscate the stage?


I have a player that can move around the stage.

I want to have a small illumination of the stage around the player such that enemies and the environment become harder to see the further out they are. I've been able to achieve this to a degree by using these answers here AS3: beginGradientFIll() doesn't make me a gradient!

Additionally, I'd like to be able to dynamically control the alpha such that when the player presses a designated key, more of the stage becomes illuminated. I've also been able to achieve this to an extent.

Here's the SWF so far. http://www.fastswf.com/9cOxDwo

Arrow keys to move and Z to increase illumination.

The problem that I have is that

a) the gradient is too harsh and

b) pressing Z reveals the entire stage whereas I just want to increase the radius of illumination.

Here's what my code looks like.

    var circle:Shape = new Shape;

    public function player():void
    {
        this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }

    private function onAddedToStage(event:Event):void
    {
        var matrix:Matrix = new Matrix();
        matrix.createGradientBox(1000, 1000)            
        circle.graphics.beginGradientFill(GradientType.RADIAL, [0x000000, 0x000000], [0, 1], [0, 255])
        circle.graphics.drawCircle(0, 0, stage.stageWidth)
        circle.graphics.endFill()
        addChild(circle);

And to control the alpha I've just simply be using:

    private function lux():void
    {
        circle.alpha = 0
    }

    private function onFrame(event:Event):void
    {
        if(circle.alpha < 1)
        {
            circle.alpha += 0.005
        }

To solve b), could I somehow dynamically control the alpha parameter in the beginGradientFill() function?

I've also been looking at this but couldn't figure out how to mask the stage instead of a clip.

Save me AS3 gods.

Thanks.


Solution

  • Just a quick guide on setting up the overlays the manual way:

    1. First, you will need a container (MovieClip), with its blendmode set to "LAYER" (in the Property Inspector panel [I can supply an image if you don't know where that is]). This step is important in order to get the following children MovieClips' blendmode working together.
    2. Next, you can create a child Movieclip, full stage size, dark rectangle with the desired alpha value (for your shadow, where the items will be completely unlit). This can be a simple rect shape, #000000 pure black with barely any transparency (say... 95% alpha). Once you have that Movieclip created, set its blendmode to LAYER (or MULTIPLY might work too).
    3. For the light, you can create another child Movieclip (placed on top of the above "shadows" overlay). Create a circle with a radial gradient (lets say white, from purely opaque 100% alpha to completely transparent 0% alpha). Make it the size you need for whatever the default lit-range is in your game. Then, set that Movieclip's blendmode to ERASE.

    If all is done correctly (I hope I described all the steps correctly!), when you navigate back to the timeline where the container holds this entire setup, you should see the blendmode of the light "masking" the portion out of the shadow layer (only this does it smoother taking the gradient's alpha into account, compared to traditional masking in Flash layers).

    You will still need code to tell the light Movieclip to follow your player / light-source, which could be approached in two ways:

    • Move only the light gradient. or -Move the entire container, BUT, be sure you stretch out the shadow clip further iut of the stage size so that if your player is close to any edges of the stage, the shadow won't suddenly be cutoff on the opposite side(s) of the screen.

    After all this said and done, large screen changes in traditional Flash vector rendering tends to be slow in performance. The only recommendation I can give you after that is consider using a Stage3D powered engine (Starling, Genome2D, etc.) but that would be a large change to your game - and beyond the scope of this question.

    EDIT: to give you a better answer regarding the hierarchy of DisplayObjects, here's a graph
    (albeit made in ugly ASCII characters)

    + Root
    |
    |- Your HUD / UI elements (if applicable)
    |
    |--+ Container, set to "LAYER" (MovieClip, follows player position)
    |  |
    |  |-Erasing Light, set to "ERASE" (MovieClip)
    |  |-Shadow Rect, set to "LAYER" or "MULTIPLY" (MovieClip)
    |
    |- Your Player
    |- Enemies, Items, Background Etc.
    

    You can also view a screencast tutorial here:
    Quick Tutorial on making a Radial Gradient light & shadow setup
    https://www.screenr.com/6MJN

    EDIT 2021: Sorry folks, but looks like that ScreenR recording vanished from the internet since they got acquired by Articulate. Tried the shortlink /6MJN on it, but no dice. Considering Flash is pretty much fully exterminated everywhere now, this recording probably doesn't matter much anymore.