ActionScript 3.0 Particle Systems #4: Bubbles

Yet more ActionScript 3.0 again! This time it’s a kind of 2D particle fountain. New in this iteration is code so we can pause and resume the animation instead of just start/stopping it, and each bubble’s colour gets subtley modified via colorTransforms.

Last one for the day methinks – I’m knackered. Happy, but knackered :)

As usual, source code and flash files after the jump for those of a particularly masochistic bent…

Flash File (ActionScript 3.0) Code:

import flash.events.KeyboardEvent;
import Bubble;
 
// Function to return us a random number which is within a specified range
function randRange(low:Number, high:Number):Number {
	var randNum:Number = (Math.random() * (high - low)) + low;
	return randNum;
} 
 
// Define our vector (just a dynamic array with a specied type in AS3!)
var bubbleVect:Vector.<Bubble> = new Vector.<Bubble>;
 
// Define our number of bubbles to display on the screen and a start/stop animation flag
var numberOfBubbles:uint = 60;
var toggleAnimation:Boolean = false;
var bubblesExist:Boolean = false;
 
// Create our controls message, center it and add it to the screen
var msg:ControlsMessage = new ControlsMessage();
msg.x = stage.stageWidth / 2;
msg.y = 50;
addChild(msg);
 
// Create a flag to only show our controls message once
var showMsg:Boolean = true;
 
function addBubbles():void
{
	// Create variables to store our x & y speeds, which get passed to the Bubble constructor
	var bubbleXSpeed:Number;
	var bubbleYSpeed:Number;
 
	// Create however many Bubbles we've entered into our numberOfBubbles variable
	for (var loop:uint = 0; loop < numberOfBubbles; loop++) {
 
		// If there are no bubbles on the stage, then create a new instance of Bubble,
		// sets it's properties and add it to our Vector of Bubbles
		if (bubblesExist == false)
		{
			bubbleVect.push(new Bubble(bubbleXSpeed, bubbleYSpeed));
 
			// Set the initial x (horizontal) and y (vertical) location on stage 
			bubbleVect[loop].x = 250;
			bubbleVect[loop].y = 245;
 
			// Randomise alpha
			bubbleVect[loop].alpha = Math.random();
 
			// Randomise our bubble size to between 30% and 40% of the symbol's original size
			var sizeScale:Number = Math.random() * 0.4;
			if (sizeScale < 0.3) { sizeScale = 0.3; }
			bubbleVect[loop].scaleX = sizeScale;
			bubbleVect[loop].scaleY = sizeScale;	
 
			// Randomise our Bubble colour
			bubbleVect[loop].randomiseBubbleColour();
 
			// Add the Bubble to the stage
			addChild(bubbleVect[loop]);
		}
		else
		{
			// Otherwise re-instate the event listener on each bubble leaving
			// existing properties alone (so we continue where we left off)
			bubbleVect[loop].rebindBubble(); 
 
		} // End of if bubblesExist condition
 
	} // End of for loop
 
	// Set our flag to say the bubbles now exist
	bubblesExist = true;
 
} // End of addBubbles function
 
function removeBubbles():void
{
	// Loop through our vector of bubbles and...
	for (var loop:uint = 0; loop < numberOfBubbles; loop++) {
 
		// ...call our custom destructor to unbind any Bubble eventListeners
		bubbleVect[loop].BubbleDestructor();
	}
 
} // End of removeBubbles function
 
function bubbleController(e:KeyboardEvent):void
{
	// If the animation is stopped and a key is pressed, add the Bubbles and flip the flag
	if (toggleAnimation == false) 
	{
		addBubbles();
		toggleAnimation = true;
 
		// Only remove message after first display, as it'll only be on stage once
		if (showMsg == true) 
		{
			showMsg = false;
			removeChild(msg);
		}
	}
	else {
		// Otherwise animation must be running and we want it to stop, so remove the
		// bubbles and flip the flag
		removeBubbles();
		toggleAnimation = false;
	}
 
} // End of bubbleController function
 
// With all our functions in place, bind the Bubble controller function to a keypress
stage.addEventListener(KeyboardEvent.KEY_DOWN, bubbleController);

ActionScript 3.0 Bubble Class Code:

package
{
	// Import the classes we're going to use
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.geom.ColorTransform;
 
	public class Bubble extends MovieClip
	{
		// Define our class properties
		var xSpeed:Number;
		var ySpeed:Number;
		var alphaDecay:Number;
		var fadeBack:Boolean;
 
		// Define our class static variables
		static var gravity:Number = 0.1;
		static var maxXSpeed:Number = 4;
		static var maxYSpeed:Number = -5;
 
		// Class Constructor
		public function Bubble(theXSpeed:Number, theYSpeed:Number):void
		{
			// Add an event listener to each instance of a bubble so that it's updated every
			// time there's a new frame (default: 24 times a second, i.e. 24fps)
			this.addEventListener(Event.ENTER_FRAME, updateBubble);
 
			// Randomise the x and y speed of the Bubble between a given range
			// and set a random alpha (transparency) decay speed
			this.xSpeed = (Math.random() * maxXSpeed) - (maxXSpeed / 3.5);
			this.ySpeed = (Math.random() * maxYSpeed) + (maxYSpeed / 2);			
			this.alphaDecay = Math.random() / 50;
 
			// Initially, we don't want our our Bubbles to quickly fade in
			this.fadeBack = false;			
		}
 
		// Destructor to unbind the bubble's ENTER_FRAME event listener when we destroy it
		public function BubbleDestructor():void
		{
			this.removeEventListener(Event.ENTER_FRAME, updateBubble);
		}
 
		public function rebindBubble():void
		{
			this.addEventListener(Event.ENTER_FRAME, updateBubble);
		}
 
		public function randomiseBubbleColour():void
		{
			var myColourTransform:ColorTransform = this.transform.colorTransform;
 
			// This will change the color of all layers and all sub-symbols within this symbol:
			//myColourTransform.color = 0xff0000;
 
			// Shift each colour channel (you can shift in the range: -255 to 255).
			myColourTransform.redOffset   = (Math.random() * 200) - 100; // Range: -255 to +255
			myColourTransform.greenOffset = (Math.random() * 200) - 100; // Range: -255 to +255
			myColourTransform.blueOffset  = (Math.random() * 200) - 100; // Range: -255 to +255
 
			// This number will multiply by the green channel only (decimal value).  There is also a redMultiplier and blueMultiplier.
			// This will essentially saturate/desaturate the color channel.
			// colorTransform.greenMultiplier = 2;
 
			// re-assign the ColorTransform back to this symbol
			this.transform.colorTransform = myColourTransform;
		}
 
		// Function to update a bubble, bound to Event.ENTER_FRAME, so called once per frame 
		public function updateBubble(e:Event):void
		{
			// Add a small amount to our y speed to simulate gravity
			this.ySpeed += gravity;
 
			// Add our randomised x and y speeds to our Bubble position
			this.x += xSpeed;
			this.y += ySpeed;
 
			// Subtract a small amount from our object's alpha so it fades out
			this.alpha -= this.alphaDecay;
 
			// Reset bubble properties when it's faded out so much it's completely transparent
			if (this.alpha <= 0) {
				// Reset the initial x and set the y location of our bubbles 
				this.x = 250;
				this.y = 245;
 
				// Re-randomise our x and y speeds within a given range
				this.xSpeed = (Math.random() * maxXSpeed) - (maxXSpeed / 3.5);
				this.ySpeed = (Math.random() * maxYSpeed) + (maxYSpeed / 2);
 
				// Re-randomise our Bubble colour
				this.randomiseBubbleColour();
 
				// Set out fadeBack flag to true
				this.fadeBack = true;
			}
 
			// When a bubble has completely faded out fade it back in quickly
			if (this.fadeBack == true) {
				this.alpha += 0.10;
 
				// When we're fully opaque again stop making any further fadeBack alpha changes
				if (this.alpha >= 1) {
					this.fadeBack = false;
				}
			}
 
		} // End of updateBubble function
 
	} // End of Bubble class
 
} // End of package

In accordance with the prophesy, Adobe Flash CS4 files for the above can be found: here.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.