Wednesday February 22nd 2012

Flixel Showcase: Fall

Another ‘seasonal’ special effect! This one’s (obviously) about fall :P

This movie requires Flash Player 9

There’s several things going on here:

  • First, the tree and it’s leaves are all randomly generated. The tree is comprised of “TreeBits”, which are 1×1 black FlxSprites with a ‘weight’ assigned to them. These are placed in the area around the top of the trunk randomly, but slightly denser towards the middle mass of the tree.
  • The leaves are placed on a layer on top of the branches. These are also 1×1 FlxSprites with a random color assigned to them, and a random weight (but much lighter than the branches). These are scattered all over, and a little outside the area of the branches, and then several are randomly placed just above the ground.
  • Some of the other graphics are setup including the background, and the very, very subtle mist layer in front of everything else. These are the only 2 ‘external’ images used – I didn’t feel like messing around with gradients in code.
  • The wind is set to be randomly blowing somewhere between -10 and 10, and it’s ‘goal’ strength is randomly set within the same range.
  • Then it starts running: the wind will gradually increase or decrease until it hit’s it’s ‘goal’ strength, and then it will be assigned a new random goal. So the wind will always be gusting and it might increase or decrease or reverse directions.
  • Each of the individual leaves in the tree will be sent a ‘push’ command, with the current wind strength and direction. Based on the leaf’s weight, it will move a slightly random amount in the direction of the wind (it might not move at all). Fluctuations are built in so they’ll look more like they’re flailing back and forth. The same thing happens with the branches, but they weigh more and so move less with less fluctuation.
  • If a leaf is pushed to it’s limit, there’s a random chance it will break away from the tree and start falling. Falling leaves are simply pushed a slightly randomized amount based on their weight and the wind, with some up/down fluctuation, and a little side-to-side fluctuations. Branches won’t break off.
  • Falling leaves don’t collide with anything except the ground, or leaves that are on the ground. Once it collides, the leaf gets marked as ‘on the ground’ so that it can collide with other leaves.
  • Leaves on the ground that do not have a leaf directly above them can be randomly picked up by the wind again. It takes a little more force from the wind to pull them off the ground than out of the tree, and it’s randomized.
  • Besides the leaves, there are also little wisps of dust or particles being pushed by the wind. These can randomly appear just off the screen if the wind is strong enough in either direction and fly quickly across the screen with some variant in their up/down movement and opacity. If the wind slows down enough, they’ll disappear.
  • To top it all off, random ‘ghosts’ will appear from the ground, fade in and then fade out again with some randomness in their y velocity and some side-to-side movement. These ghosts are made up of individual blocks of FlxSprites that change their opacity separately depending on the Sprite above’s opacity.
  • Also, the leaves on the tree are finite. If you leave it run long enough, the tree will be bare. Leaves that enter from off the screen are infinite.

And that’s it! I had fun playing with this, and I would like to make many more in the future :) Let me know what you think!

You can take a look at my code to see how I’m doing things:

PlayState

package  
{
  import org.flixel.FlxPoint;
  import org.flixel.FlxSprite;
  import org.flixel.FlxState;
  import org.flixel.FlxGroup;
  import org.flixel.FlxTileblock;
  import org.flixel.FlxG;
  import org.flixel.FlxU;
  import com.gskinner.utils.Rnd;
   
  public class PlayState extends FlxState
  {
    [Embed(source = 'back.png')] private var ImgBack:Class;
    [Embed(source = 'mist.png')] private var ImgMist:Class;
    private var sky:FlxSprite; // our background sky
    private var ground:FlxSprite; // our ground object
    public static var wind:Number = 0.0; // direction and force of the wind
    private var mist:FlxSprite;
    private var treegrp:FlxGroup; // all of our tree pixels...
    public static var leafgrp:FlxGroup; // all of our leaves in a group
    private var timer:Number = 0;
    private var goal:Number = 0;
    private var mistDir:int = -1;
    private var wispBackGrp:FlxGroup;
    private var wispFrontGrp:FlxGroup;
    private var ghostgrp:FlxGroup;
   
    public function PlayState()
    {
      // the background graphic
      sky = add(new FlxSprite(0, 0, null).loadGraphic(ImgBack, false, false, FlxG.width, FlxG.height, true)) as FlxSprite;
      // our different layers
      wispBackGrp = add(new FlxGroup()) as FlxGroup;
      ghostgrp = add(new FlxGroup()) as FlxGroup;
      treegrp = add(new FlxGroup()) as FlxGroup;
      leafgrp = add(new FlxGroup()) as FlxGroup;
      wispFrontGrp = add(new FlxGroup()) as FlxGroup;
      // build the tree!
      BuildTree();
      BuildLeaves();
      // this is the mist graphic that goes on top of everything else.
      mist = add(new FlxSprite( -250 + (FlxG.width / 2), FlxG.height-45, null).loadGraphic(ImgMist, false, false, 500, FlxG.height, true)) as FlxSprite;
      mist.moves = true;
      mist.solid = false;
      mist.alpha = 0.44;
      // the ground sprite
      ground = add(new FlxSprite( -50, FlxG.height - 5, null).createGraphic(FlxG.width + 50, 20, 0xff000000, true, "ground")) as FlxSprite;
      ground.fixed = true;  // set it to fixed, not moves, and solid for collision.
      ground.moves = false;
      ground.solid = true;
     
      //lets seed the ground with some leaves already:
      var tL:LeafBit;
      for (var tY:int = FlxG.height -6; tY > FlxG.height - 8; tY--)
      {
        for (var tX:int = -50; tX <= FlxG.width+50; tX++)
        {
          if (Rnd.boolean(0.5))
          {
            tL = leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1, 2)) as LeafBit) as LeafBit;
            tL.solid = true;
            tL.falling = true;
            tL.landed = false;
          }
        }
      }
     
      // set the wind's speed and goal
      wind = Rnd.float( -10, 10);
      goal = Rnd.float( -10, 10);
    }
   
    private function BuildTree():void
    {
      //build our tree - this gets a bit complicated...
      // start at the bottom
      for (var tY:int = FlxG.height - 6; tY > FlxG.height - 25; tY--)
      {
       
        for (var tX:int = (FlxG.width/2) - 2; tX <= (FlxG.width/2) + 2; tX++)
        {
          treegrp.add(new TreeBit(tX, tY,50) as TreeBit);
        }
      }
     
      // bottom block of branches
      for (tY = FlxG.height -20; tY > FlxG.height - 25; tY--)
      {
        for (tX = (FlxG.width / 2) -10; tX <= (FlxG.width / 2) + 10; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.2, 0.4)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(4,6)) as TreeBit);
          }
        }
      }
     
      //make the central mass of branches
      for (tY = FlxG.height - 25; tY > FlxG.height - 36; tY--)
      {
        for (tX = (FlxG.width / 2) - 6; tX <= (FlxG.width / 2) + 6; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.5, 0.7)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(6,8)) as TreeBit);
          }
        }
      }
     
      //make the left-side mass of branches
      for (tY = FlxG.height - 23; tY > FlxG.height - 36; tY--)
      {
        for (tX = (FlxG.width / 2) - 18; tX <= (FlxG.width / 2) - 6; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.1, 0.4)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(4,6)) as TreeBit);
          }
        }
      }
     
      //make the right-side mass of branches
      for (tY = FlxG.height - 23; tY > FlxG.height - 36; tY--)
      {
        for (tX = (FlxG.width / 2) + 6; tX <= (FlxG.width / 2) + 18; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.1, 0.4)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(4,6)) as TreeBit);
          }
        }
      }
     
      //make the upper middle mass of branches
      for (tY = FlxG.height - 36; tY > FlxG.height - 47; tY--)
      {
        for (tX = (FlxG.width / 2) - 14; tX <= (FlxG.width / 2) + 14; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.2, 0.6)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(3,5)) as TreeBit);
          }
        }
      }
     
      // make the top branches
      for (tY = FlxG.height - 47; tY > FlxG.height - 52; tY--)
      {
        for (tX = (FlxG.width / 2) - 8; tX <= (FlxG.width / 2) + 8; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.1, 0.3)))
          {
            treegrp.add(new TreeBit(tX, tY, Rnd.integer(2,4)) as TreeBit);
          }
        }
      }
    }
   
    private function BuildLeaves():void
    {
      // bottom block of branches
      for (var tY:int = FlxG.height -18; tY > FlxG.height - 25; tY--)
      {
        for (var tX:int = (FlxG.width / 2) - 12; tX <= (FlxG.width / 2) + 12; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.3, 0.5)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
     
      //make the central mass of branches
      for (tY = FlxG.height - 25; tY > FlxG.height - 36; tY--)
      {
        for (tX = (FlxG.width / 2) - 6; tX <= (FlxG.width / 2) + 6; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.6, 0.8)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
     
      //make the left-side mass of branches
      for (tY = FlxG.height - 21; tY > FlxG.height - 38; tY--)
      {
        for (tX = (FlxG.width / 2) - 20; tX <= (FlxG.width / 2) - 6; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.2, 0.5)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
     
      //make the right-side mass of branches
      for (tY = FlxG.height - 21; tY > FlxG.height - 38; tY--)
      {
        for (tX = (FlxG.width / 2) + 6; tX <= (FlxG.width / 2) + 20; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.2, 0.5)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
     
      //make the upper middle mass of branches
      for (tY = FlxG.height - 36; tY > FlxG.height - 47; tY--)
      {
        for (tX = (FlxG.width / 2) - 16; tX <= (FlxG.width / 2) + 16; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.3, 0.7)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
     
      // make the top branches
      for (tY = FlxG.height - 47; tY > FlxG.height - 54; tY--)
      {
        for (tX = (FlxG.width / 2) - 10; tX <= (FlxG.width / 2) + 10; tX++)
        {
          if (Rnd.boolean(Rnd.float(0.2, 0.4)))
          {
            leafgrp.add(new LeafBit(tX, tY, Rnd.integer(1,2)) as LeafBit);
          }
        }
      }
    }
   
    override public function update():void
    {
      if (timer <= 0)
      {
        // move the wind's strength
        wind += Rnd.float(0.1, 0.3) * (goal/Math.abs(goal));
        timer = 0.2;
        // push each branch
        var t:TreeBit;
        for each (t in treegrp.members)
          t.push(wind);
        //push each leaf
        var l:LeafBit;
        for each (l in leafgrp.members)
          l.push(wind);
      }
      else
        timer -= FlxG.elapsed;
       
      // here's some logic to get the mist moving and to stay in the bounds.
      if (Math.abs(wind) >= Math.abs(goal))
        goal = Rnd.float( -10, 10);
      mist.velocity.x = wind;
      if (mist.x < -500 + FlxG.width) mist.x = -500 + FlxG.width;
      else if (mist.x > 0) mist.x = 0;
     
      // collide leaves with other leaves and the ground
      leafgrp.collide(leafgrp);
      ground.collide(leafgrp);
     
      //randomly add new leaves from off the screen...
      if (Rnd.boolean(0.01))
        SpawnLeaf();
      // random wisps
      if (Rnd.boolean(0.01))
        SpawnBackWisp();
      if (Rnd.boolean(0.005))
        SpawnFrontWisp();
      // random ghosts
      if (Rnd.boolean(0.001))
        SpawnGhost();
     
      super.update();
    }
   
    private function SpawnBackWisp():void
    {
      // spawn a random wisp off the screen.
      var tW:WispBit;
      tW = wispBackGrp.getFirstAvail() as WispBit;
      if (tW == null)
      {
        if (wind < 0)
          tW = wispBackGrp.add(new WispBit(FlxG.width + 1, Rnd.integer( -1, FlxG.height - 4))) as WispBit;
        else if (wind > 0)
          tW = wispBackGrp.add(new WispBit(-1, Rnd.integer( -1, FlxG.height - 4))) as WispBit;
      }
      else
        tW.reset(FlxG.width + 1, Rnd.integer( -1, FlxG.height - 4));
    }
    private function SpawnFrontWisp():void
    {
      // spawn a random wisp off the screen in the foreground.
      var tW:WispBit;
      tW = wispFrontGrp.getFirstAvail() as WispBit;
      if (tW == null)
      {
        if (wind < 0)
          tW = wispFrontGrp.add(new WispBit(FlxG.width + 1, Rnd.integer( -1, FlxG.height - 4))) as WispBit;
        else if (wind > 0)
          tW = wispFrontGrp.add(new WispBit(-1, Rnd.integer( -1, FlxG.height - 4))) as WispBit;
      }
      else
        tW.reset(FlxG.width + 1, Rnd.integer( -1, FlxG.height - 4));
     
    }
   
    private function SpawnGhost():void
    {
      // spawn a random ghost
      var g:Ghost;
      g = ghostgrp.getFirstAvail() as Ghost;
      if (g == null)
        ghostgrp.add(new Ghost(Rnd.integer(2, FlxG.width - 7), Rnd.integer(FlxG.height + 2, FlxG.height - 4))) as Ghost;
      else
        g.reset(Rnd.integer(2, FlxG.width - 7), Rnd.integer(FlxG.height + 2, FlxG.height - 4));
    }
   
    private function SpawnLeaf():void
    {
      // spawn a random leaf off the screen.
      var tL:LeafBit;
      tL = leafgrp.getFirstAvail() as LeafBit;
      var pos:FlxPoint = new FlxPoint(0,0);
      if (wind > 0)
      {
        pos.x = -1;
        pos.y = Rnd.integer( -FlxG.width, FlxG.width - 6);
      }
      else if (wind < 0)
      {
        pos.x = FlxG.width + 1;
        pos.y = Rnd.integer( -FlxG.width, FlxG.width - 6);
      }
      else
      {
        if (Rnd.boolean())
        {
          pos.x = -1;
          pos.y = Rnd.integer( -FlxG.width, FlxG.width - 6);
        }
        else
        {
          pos.x = FlxG.width + 1;
          pos.y = Rnd.integer( -FlxG.width, FlxG.width - 6);
        }
      }
      if (tL != null)
        tL.reset(pos.x,pos.y);
      else
        tL = leafgrp.add(new LeafBit(pos.x, pos.y, Rnd.integer(1, 2))) as LeafBit;
      tL.solid = true;
      tL.falling = true;
      tL.landed = false;
    }
  }

}

TreeBit.as

package  
{
  import org.flixel.FlxPoint;
  import org.flixel.FlxSprite;
  import org.flixel.FlxObject;
  import org.flixel.FlxG;
  import com.gskinner.utils.Rnd;
 
  public class TreeBit extends FlxSprite
  {
    public var weight:Number = 0; // this is how much this part of the tree 'weighs'. The heavier it is, the more wind force it takes before it should move...
    private var init:FlxPoint;
    public function TreeBit(X:Number, Y:Number, w:int):void
    {
      super(X, Y, null);
      createGraphic(1, 1,0xff000000, false, null);
      width = 1;
      height = 1;
      weight = w;
      reset(X, Y);
    }
   
    public function push(Force:Number):void
    {
      // take the amount of force being applied, subtact the weight of this bit (+/- a random amount), the remainder is how much it moves.
      var m:Number = Math.abs(Force) - weight;
      var dir:int = Force / Math.abs(Force);
      // theres a change the branch will move, and if it doesn't it goes back to where it started
      if (Rnd.boolean(Rnd.float(0.1, 0.3)) && m > 0)
        x = init.x +(dir * Math.ceil(m/2));
      else
        snapback();
    }
   
    override public function reset(X:Number, Y:Number):void
    {
      super.reset(X, Y);
      init = new FlxPoint(X, Y);
      fixed = false;
      moves = true;
      solid  = true;
      alpha = 1;
    }
   
    public function snapback():void
    {
      x = init.x;
      y = init.y;
    }
   
    override public function update():void
    {
      super.update();
    }
  }

}

LeafBit.as

package
{
  import org.flixel.FlxPoint;
  import org.flixel.FlxSprite;
  import org.flixel.FlxObject;
  import org.flixel.FlxG;
  import com.gskinner.utils.Rnd;
 
  public class LeafBit extends FlxSprite
  {
    public var weight:Number = 0; // affects how much the wind moves this leaf
    private var init:FlxPoint; // the 'starting' point of the leaf to snap back to
    private var colors:Array; // array of colors
    public var falling:Boolean = false; // if the leaf is falling or not
    public var landed:Boolean = false; // if the leaf is on the ground or not
   
    public function LeafBit(X:int, Y:int, w:int)
    {
      super(X, Y, null);
     
      colors = new Array();
      // this is an array of possibly 'fall' colors for our leaves.
      colors.push(0xffb14211);
      colors.push(0xffd9541a);
      colors.push(0xff663408);
      colors.push(0xffbc5815);
      colors.push(0xffecb338);
      colors.push(0xffbf1313);
     
      createGraphic(1, 1, colors[Rnd.integer(0, 5)], false, null);
      width = 1;
      height = 1;
      weight = w;
      reset(X, Y);
    }
   
    public function push(Force:Number):void
    {
      // take the amount of force being applied, subtact the weight of this bit (+/- a random amount), the remainder is how much it moves.
      var m:Number = Math.ceil((Math.abs(Force) - weight) / 2);
      var dir:int = Force / Math.abs(Force);
      if (landed)
      {
        //if we landed, then we can only move if the wind is strong, and if we're not under another leaf
        if (m > 2)
        {
          if (Rnd.boolean(0.3))
          {
            var tP:FlxSprite = new FlxSprite(x, y - 1);
            tP.createGraphic(1, 1, 0x00000000);
            if (!tP.collide(PlayState.leafgrp))
            {
              velocity.y = -((dir * m) * Rnd.float( -0.15, 0.15));
              falling = true;
              landed = false;
            }
          }
        }
      }
      else if (!falling)
      {
        //if we're not falling, the wind might push us a little bit (or not) if it pushes too hard, we might start falling.
        if (Rnd.boolean(Rnd.float(0.1, 0.3)) && m > 0)
        {
          if (m > 2)
            if (Rnd.boolean(0.01))
            {
              falling = true;
              acceleration.y = weight;
              solid = true;
            }
            else
              x = init.x + (dir * 2);
          else
            x = init.x + (dir * m);
        }
        else
          snapback();
      }
      else
      {
        // if we're falling, the velocity can randomly change, but it's based on the wind.
        velocity.x = ((dir * m) * Rnd.float(0.85, 1.15)) + Rnd.integer( -2, 2);;
        velocity.y += ((m * 0.5) * Rnd.float(-0.15, 0.15)) * Rnd.sign(0.3);
      }
     
    }
   
    override public function reset(X:Number, Y:Number):void
    {
      super.reset(X, Y);
      init = new FlxPoint(X, Y);
      fixed = false;
      moves = true;
      solid  = false;
      alpha = 1;
    }
   
    public function snapback():void
    {
      // put the leaf back where it started
      x = init.x;
      y = init.y;
    }
   
    override public function update():void
    {
      // if the leaf falls too far off the screen, we'll kill it
      if (y > FlxG.height) kill();
      super.update();
    }
   
    override public function hitBottom(Contact:FlxObject, Velocity:Number):void
    {
      // we only want to land on top of the ground or other leaves that have landed.
      var hit:Boolean = false;
      if (Contact is LeafBit)
      {
        var f:LeafBit = Contact as LeafBit;
        if (!f.landed) hit = true;
      }
      else hit = true;
      if (hit)
      {
        super.hitBottom(Contact, 0);
        // when we do land on something, we stop our movement and set falling to false
        velocity.x = 0;
        falling = false;
        landed = true;
      }
    }
   
  }

}

WispBit.as

package
{
  import org.flixel.FlxPoint;
  import org.flixel.FlxSprite;
  import org.flixel.FlxObject;
  import org.flixel.FlxG;
  import com.gskinner.utils.Rnd;
 
  public class WispBit extends FlxSprite
  {
   
    private var lastdir:Number = 0; // this is to rememer which way we were blowing. If the wind changes too much, we'll die.
    private var fading:Boolean = false;
   
    public function WispBit(X:int, Y:int):void
    {
      super(X, Y, null);
      createGraphic(1, 1, 0xffffffff);
      width = 1;
      height = 1;
      reset(X, Y);
    }
   
    override public function reset(X:Number, Y:Number):void
    {
      super.reset(X, Y);
      alpha = Rnd.float(0.1, 0.6);
      lastdir = PlayState.wind;
      fading = false;
    }
   
    override public function update():void
    {
      if (dead || !exists) return;
      if (fading)
      {
        // if we're fading out, decrease our opacity each tick and then kill us for real when it's 0.
        if (alpha <= 0)
        {
          dead = true;
          exists = false;
        }
        else
          alpha -= 0.2;
      }
      else if (Math.abs(PlayState.wind) < 3) kill(); // if the wind stops or slows down too much, we just die right away.
      else if (lastdir < 0) // if we were going one way and then the wind suddenly reversed on us, we die.
      {
        if (x < 0) kill();
        if (PlayState.wind >= 0) kill();
      }
      else if (lastdir > 0)
      {
        if (x > FlxG.width) kill();
        if (PlayState.wind <= 0) kill();
      }
      // our speed and direction is randomly based on the wind.
      velocity.x = (PlayState.wind * 20) * Rnd.float(0.75, 1.25);
      velocity.y += (PlayState.wind * 0.05) * Rnd.float(-0.25, 0.25);
      lastdir = PlayState.wind;
      super.update();
    }
   
    override public function kill():void
    {
      // if we get killed, we really just want to fade out.
      fading = true;
    }
  }

}

Ghost.as

package
{
  import org.flixel.FlxPoint;
  import org.flixel.FlxSprite;
  import org.flixel.FlxObject;
  import org.flixel.FlxG;
  import org.flixel.FlxGroup;
  import com.gskinner.utils.Rnd;
 
  public class Ghost extends FlxGroup
  {
   
    private var ghostParts:Array; // this array will hold all of our ghost's parts
    private var fading:Boolean = false; // track if we're fading in or not
    private var timer:Number = 0;
   
    public function Ghost(X:Number, Y:Number):void
    {
      super();
      x = X;
      y = Y;
      ghostParts = new Array();
      // our ghost is made up of smaller FlxSprites so that we have more control over alpha.
      ghostParts.push(add(new FlxSprite(0, 0).createGraphic(5, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 1).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(2, 1).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(4, 1).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 2).createGraphic(5, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 3).createGraphic(2, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(3, 3).createGraphic(2, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 4).createGraphic(2, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(3, 4).createGraphic(2, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 5).createGraphic(5, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 6).createGraphic(5, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 7).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(2, 7).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(4, 7).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(0, 8).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(2, 8).createGraphic(1, 1, 0xffffffff)));
      ghostParts.push(add(new FlxSprite(4, 8).createGraphic(1, 1, 0xffffffff)));
      reset(X, Y);
    }
   
    override public function reset(X:Number, Y:Number):void
    {
      super.reset(X, Y);
      x = X;
      y = Y;
      var gP:FlxSprite;
      // set each ghost part to be almost invisible.
      for each (gP in ghostParts)
        gP.alpha = 0.1;
      fading = false;
      timer = 0;
      velocity.y -= Rnd.float(16,24); // set random starting y velocity
    }
   
    override public function update():void
    {
      //basically, we go through each ghost part, starting with the top, and if we're fading in make it more opaque until it's full,
      // each piece after the first's alpha is based on the level right above it so we get a gradual effect of most opaque->least opaque
      // when ghost is faded in all the way, turn on 'fading' and then we reverse the process.
      if (dead || !exists) return;
      if (timer <= 0)
      {
        var gP:FlxSprite;
        var i:int;
        if (fading)
        {
          if (ghostParts[0].alpha <= 0) kill();
          else
          {
            for (i = 0; i < ghostParts.length; i++)
            {
              switch(i)
              {
                case 0:
                  ghostParts[i].alpha -= 0.15;
                  break;
                case 1:
                case 2:
                case 3:
                  ghostParts[i].alpha = ghostParts[0].alpha -0.1;
                  break;
                case 4:
                  ghostParts[i].alpha = ghostParts[2].alpha -0.1;
                  break;
                case 5:
                case 6:
                  ghostParts[i].alpha = ghostParts[4].alpha -0.1;
                  break;
                case 7:
                case 8:
                  ghostParts[i].alpha = ghostParts[5].alpha -0.1;
                  break;
                case 9:
                  ghostParts[i].alpha = ghostParts[7].alpha -0.1;
                  break;
                case 10:
                  ghostParts[i].alpha = ghostParts[9].alpha -0.1;
                  break;
                case 11:
                case 12:
                case 13:
                  ghostParts[i].alpha = ghostParts[10].alpha -0.1;
                  break;
                case 14:
                case 15:
                case 16:
                  ghostParts[i].alpha = ghostParts[11].alpha -0.1;
                  break;
              }
            }
          }
        }
        else
        {
          if (ghostParts[0].alpha >= 1) fading = true;
          else
          {
            for (i = 0; i < ghostParts.length; i++)
            {
              switch(i)
              {
                case 0:
                  ghostParts[i].alpha += 0.15;
                  break;
                case 1:
                case 2:
                case 3:
                  ghostParts[i].alpha = ghostParts[0].alpha -0.1;
                  break;
                case 4:
                  ghostParts[i].alpha = ghostParts[2].alpha -0.1;
                  break;
                case 5:
                case 6:
                  ghostParts[i].alpha = ghostParts[4].alpha -0.1;
                  break;
                case 7:
                case 8:
                  ghostParts[i].alpha = ghostParts[5].alpha -0.1;
                  break;
                case 9:
                  ghostParts[i].alpha = ghostParts[7].alpha -0.1;
                  break;
                case 10:
                  ghostParts[i].alpha = ghostParts[9].alpha -0.1;
                  break;
                case 11:
                case 12:
                case 13:
                  ghostParts[i].alpha = ghostParts[10].alpha -0.1;
                  break;
                case 14:
                case 15:
                case 16:
                  ghostParts[i].alpha = ghostParts[11].alpha -0.1;
                  break;
              }
            }
          }        
        }
        velocity.x = Rnd.float(-2,2)/4; // we change x velocity randomly
        timer = FlxG.elapsed*3.5;
      }
      else
        timer -= FlxG.elapsed;
      super.update();
    }
   
  }

}

You can find the complete source here on GitHub.

Previous Topic:
Next Topic:

One Comment for “Flixel Showcase: Fall”


Leave a Comment

More from category

HALOWEENIES!

Mwa ha ha! Cower in fear puny mortals! The realm of the dead has burst upon your world and nothing is safe! This movie [Read More]

Flixel SNOWcase: Snow

In an effort to retain at least some of my sanity, I spent some time just goofing around with Flixel and came up with [Read More]

Twitter: TileIsle

Donators

Help us Out!







Write your comment within 199 characters.