Actionscript 3 Tutorial (Bubble Collision Game)

How to Program in Flash

Actionscript Game Demo

Learn how to program Flash apps in this tutorial. We will go through the creation of circle collision physics using Adobe Flash and Actionscript. Upon clicking on the stage a new circle with random properties will be created. You will learn Actionscript 3, as you build and program a simple bubble collision game demo.

The Actionscript programming code has collision detection, and will make the bubbles bounce off each other. There are math functions in the code based on the formula for Pythagorean Theorem. The game comes with a nice ocean background. There is no scoring system in this demo, but with a little work you can program a good Flash Actionscript game in no time.

Knowledge of programming in Flash Actionscript 3.0 is preferred to follow this tutorial as efficiently as possible. Some knowledge of Math is also a big plus. For demonstration and simplicity purposes, the code will be written on the timeline.

Files:

BubbleCollision.zip

Video:

Step 1)

Draw a circle on the stage with equal width and height of 16 pixels. Convert it to a symbol of type MovieClip and set the origin point at its center. In the Library, double click on its AS Linkage section and write in Circle. This will automatically generate a class with that name for it. In your timeline, right click on the first frame on your Actions layer and go to Actions.

 

Step 2)

We are going to start by importing some classes we will need. Keep these at the top for better readability later on.


import flash.events.Event;

import flash.geom.Point;

import flash.events.MouseEvent;

 

Then we will include some math functions. Keep these at the bottom for better readability later on.


function distanceBetweenPoints(p1:Point, p2:Point):Number

{

     return Math.sqrt(Math.pow(Math.abs(p1.x - p2.x), 2) + Math.pow(Math.abs(p1.y - p2.y), 2));

}

function circleCollision(c1:Circle, c2:Circle):Boolean

{

     return (distanceBetweenPoints(new Point(c1.x, c1.y), new Point(c2.x, c2.y)) <= c1.scaleX * BASE_RADIUS + c2.scaleX * BASE_RADIUS);

}

function pointAtPositionAngle(p1:Point, p2:Point):Number

{

     return Math.atan2(p2.y - p1.y, p2.x - p1.x) / Math.PI * 180;  

}

 

Let’s examine the above code…the first function is distanceBetweenPoints. Its arguments are of type Point. Point is a Flash class from the flash.geom package. It has 2 properties: x and y.

What this function does is calculate the distance between 2 points, as the name suggests.

This is done using the Pythagorean Theorem.

An example using this image would be to have point A be the top of the side “a” and point B at the furthest right of the side “b”. The calculated distance would equal to “c”.

The next function is circleCollision.

This function determines whether or not there is a collision between 2 circles. The arguments are of type Circle – the class we associated with our circle MovieClip. Later on we will add dynamic properties to the instances of this class and these are accessed in this function. Basically, if the distance between the 2 center points of the circles is less than or equal to the sum of their radiuses, there is a collision.

The last function in the code above is pointAtPositionAngle. This function returns the angle between point1 and point2. This is done using the function Math.atan2. Math.atan2 is a function mostly present only in programming languages. It is an inverse trigonometric function and is like arctangent with 2 arguments. The first argument is the y axis difference between point2 and point1, while the second argument is the x axis difference between point2 and point1. The function of course returns a value in radians, so we divide the value by Math.PI and multiply it by 180 to get a value in standard flash angles.

 

Step 3)

After the imports, add the following code:


var circles:Array = new Array();

var i:uint;

var j:uint;

const BASE_RADIUS:uint = 8;

const BOUNCE_SPEED:uint = 2;

addEventListener(Event.ENTER_FRAME, enterFrameHandler);

function enterFrameHandler(event:Event):void

{

     for (i = 0; i < circles.length; i++)

     {

           circleMovement(i);

           collisionWithOtherCircles(i);

     }

}

Here we declare an Array that will store all the circles on stage. We also declare helper loop variables: i and j. The constant BOUNCE_SPEED is the speed at which circles bounce away upon collision. The constant BASE_RADIUS is the original radius of our circle MovieClip(Half its width).

Next we add an event listener for enter frame. Here we loop through all the circles in the circles Array and we call 2 functions for each: circleMovement(i) and collisionWithOtherCircles(i).

“circleMovement”, as the name suggests, moves around the circle with index (i). It also makes it wrap around the stage if it is outside of it.

“collisionWithOtherCircles” handles the collision with all other circles and the appropriate reaction for both participants of the action.

 

Step 4)

Here are those 2 functions. Add them below the “enterFrameHandler” function above.


function circleMovement(i:uint):void

{

     circles[i].x += circles[i].xInc;

     circles[i].y += circles[i].yInc;

     if (circles[i].x - circles[i].width / 2 > 550)

     {

           circles[i].x = 0 - circles[i].width / 2;

     }

     if (circles[i].x + circles[i].width / 2 < 0)

     {

           circles[i].x = 550 + circles[i].width / 2;

     }

     if (circles[i].y - circles[i].height / 2 > 400)

     {

           circles[i].y = 0 - circles[i].height / 2;

     }

     if (circles[i].y + circles[i].height / 2 < 0)

     {

           circles[i].y = 400 + circles[i].height / 2;

     }

}

function collisionWithOtherCircles(i:uint):void

{

     for (j = 0; j < circles.length; j++)

     {

           if (i != j)

           {

                if (circleCollision(circles[i], circles[j]))

                {

                      var ang:Number = pointAtPositionAngle(new Point(circles[i].x, circles[i].y), new Point(circles[j].x, circles[j].y));

                      if (ang + 180 <= 180)

                      {

                           ang += 180;

                      }

                      else

                      {                              

                           ang = -(180 - ang);

                      }

                      ang = ang * Math.PI / 180;

                      circles[i].xInc = Math.cos(ang);

                      circles[i].yInc = Math.sin(ang);

                      circles[i].x += circles[i].xInc * BOUNCE_SPEED;

                      circles[i].y += circles[i].yInc * BOUNCE_SPEED;

                      ang = pointAtPositionAngle(new Point(circles[j].x, circles[j].y), new Point(circles[i].x, circles[i].y));

                      if (ang + 180 <= 180)

                      {

                           ang += 180;

                      }

                      else

                      {                              

                           ang = -180 - ang;

                      }

                      ang = ang * Math.PI / 180;

                      circles[j].xInc = Math.cos(ang);

                      circles[j].yInc = Math.sin(ang);

                      circles[j].x += circles[j].xInc * BOUNCE_SPEED;

                      circles[j].y += circles[j].yInc * BOUNCE_SPEED;

                      break;

                }

           }

     }

}

In circle movement the designated circle’s coordinates get increased by its x and y speed. We will add these dynamic properties in a later function.

The next lines deal with the wrapping when outside the visible stage area. The function “collisionWithOtherCircles” is where most things come together. It starts with a loop going through the array circles with the helper variable j.

Next, it checks whether i is not the same as j. After all, a circle cannot collide with itself. If there is a collision between these 2 circles then the bouncing takes action. A temporary variable called “ang” is created. It then gets assigned the return value of the “pointAtPositionAngle” function.

The arguments are the positions of the first circle “(circle[i])” and the positions of the second circle “(circle[j])”. Then “ang” gets increased by 180 to get its opposite side value.

A conditional is added to prevent going outside the boundaries of the Flash angle values (-180 to 180). Then “ang” is converted to radians “(ang * Math.PI / 180)” and the first circle (circle[i])’s position gets increased by the cosine and sine values of “ang” (X and Y axis respectively). Next, the same is repeated for circle[j].

 

Step 5)

Below all this code, add an event listener to the stage of type MouseEvent.CLICK. In the click handler function we will create a new circle on the stage, if doing so is appropriate for the current situation.


stage.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(event:MouseEvent):void

{

     var c:Circle = new Circle();

     c.x = mouseX;

     c.y = mouseY;

     c.scaleX = Math.max(1, Math.random() * 3);

     c.scaleY = c.scaleX;

     if (collisionWithAnyCircle(c))

     {

           c = null;

     }

     else

     {

           if (Math.random() > 0.7)

           {

                c.xInc = 1;

           }

           else if (Math.random() > 0.4)

           {

                c.xInc = -1;

           }

           else

           {

                c.xInc = 0;

           }

           if (Math.random() > 0.7)

           {

                c.yInc = 1;

           }

           else if (Math.random() > 0.4)

           {

                c.yInc = -1;

           }

           else

           {

                if (c.xInc == 0)

                {

                      c.yInc = 1;

                }

                else

                {

                      c.yInc = 0;

                }

           }

           if (c.xInc < 1)

           {

                c.xInc = -Math.max(1, c.xInc * Math.random() * 5);

           }

           else

           {

                c.xInc = Math.max(1, c.xInc * Math.random() * 5);

           }

           if (c.yInc < 1)

           {

                c.yInc = -Math.max(1, c.yInc * Math.random() * 5);

           }

           else

           {

                c.yInc = Math.max(1, c.yInc * Math.random() * 5);

           }

           addChild(c);

           circles.push(c);

     }

}

So basically here, a new instance of the class Circle is created and is assigned its coordinates to the mouse position. It also gets assigned a random scale. But before it can be added to the stage, we must check if it overlaps with any other circle. This is done by using the function collisionWithAnyCircle.

In case there is a collision, the circles value is set to null and it never gets instantiated. Otherwise, it gets added to the stage and gets assigned values for xInc and yInc.

There are Dynamic properties. All classes which are Dynamic (Like the ones generated automatically by Flash), can be added properties at runtime. The values of these are randomized. After the assignments of these values, the newly created circle gets added to the stage and to the array of circles.

And here is the “collisionWithAnyCircle” function:


function collisionWithAnyCircle(c:Circle):Boolean

{

     for (i = 0; i < circles.length; i++)

     {

           if (circleCollision(c, circles[i]))

           {

                return true;

           }

     }

     return false;

}

 

Step 6)

You can now proceed to making the graphics suitable to your needs. For example, it can be made into an underwater scene where the circles will be bubbles as below. (Watch the video to see how I did this).

actionscript 3.0 game demo

After studying this tutorial, you will find that programming in Flash Actionscript 3 is not that hard. I will leave it to you to finish the work. Good luck!!

​Add this page to your Website, Blog, or Forum. Let your friends know about this tutorial:<a href= “http://www.tutorialboneyard.com/flash-actionscript-tutorial” target=”_blank”>Bubble Collision Demo</a>

More Flash tutorials:

Tweet or give it a thumbs up: