Elastic Collision Detection between Entities in Flash AS3


What we have got here today is a nice simple example of some Flash AS3 physics source code! Real-time collision between several varying size and mass circles. Along with that of course, is some boundary detection to keep our floating objects in frame of view. This flash physics source code is simple to read, and easy to understand. Coming in at not even 100 lines of actionscript code, you’ll be well on your way to more advanced examples of elastic collision in applications and flash games.

Source code can be found right here:

Open up the “circle_collision.fla”, and then run it by pressing Ctrl + Enter in Flash.

And a demo of this application is here:

Now, in case you need a refresher on what “elastic collision” actually is, its a transfer of momentum between two moving (or static) objects. When the balls bounce in the flash physics demo above, they don’t just simply bounce off each other, there is actually a transfer of energy between the two. Mass has an effect on what happens, and if a tiny ball is moving incredibly fast, and collides with a nearly static large ball, the large one will get a small push, absorbing most of the energy into its mass rather than into its momentum/velocity. To achieve this flash physics effect though, is actually quite simple, and doesn’t take much more as3 code rather than implementing collision with the lack of elasticity.

With that, let’s begin.

  1. After opening up a new default 550px by 400px, AS3 file; draw a nice round circle with a thick black border (maybe stroke of 8-12). Select -> Right Click -> Convert to Movieclip, and label it as “circle”.  Before pressing okay though, check the “Export for Actionscript”! You can keep it as the default “circle” export name, we’ll be using that reference in the code.
  2. Once the circle movieclip is created, we need to convert the inner_circle as well.  Inside the “circle” movieclip, click to select the inner, colored part; and then Right Click -> Convert to Movieclip, and label it as “inner_circle”.  That specific instance of the inner_circle should be named “inner“.  This is the way we are going to reference it in the code, through circle.inner (using a pointer).
  3. Once these two embedded movieclips are successfully in your library, it is time to write the code.  No circles instances are needed on the stage, you can leave it blank.
  4. The actionscript 3 source code is actually pretty simple, just two functions and some initialization.
import flash.geom.Point;

var NUMBER_OF_CIRCLES:Number = 15;

var circle_array:Array;
var vector_array:Array;
var mass_array:Array;

The import call is so we can use the Point class for distance calculations, it allows for more optimized code, and easy distance calculation.  NUMBER_OF_CIRCLES is just a constant to tell the rest of the actionscript program how many circles we want bouncing around.

And the last thing, 3 arrays.  circle_array actually contains all of our AS3 MovieClip instances.  vector_array is multidimensional, each array index contains two values in [0] and [1]; the x and y velocities for our specific circle.  The last one, mass_array contains masses for each circle, so we know how collisions can resolve in regards to momentum transfer.

Next up is the initialization function:

function init(){
    circle_array = new Array();
    vector_array = new Array();
    mass_array = new Array();
   
    for(var i:Number = 0; i < NUMBER_OF_CIRCLES; i++){
       
        circle_array[i] = new circle();
       
        circle_array[i].width = 15 + (Math.random() * 25);
        circle_array[i].height = circle_array[i].width;
       
        circle_array[i].x = (circle_array[i].width/2) + (Math.random() * (550 - circle_array[i].width*(3/2)));
        circle_array[i].y = (circle_array[i].height/2) + (Math.random() * (400 - circle_array[i].height*(3/2)));
       
        var new_color:uint = 0x000077 * Math.random();
        new_color += 0x000088;
       
        switch(i%3){
            case 0:
                new_color = new_color << 16;
                break;
            case 1:
                new_color = new_color << 8;
                break;
        }
        //new_color = new_color << 16;
        var myColor:ColorTransform = this.transform.colorTransform;
        myColor.color = new_color;
        circle_array[i].inner.transform.colorTransform = myColor;
       
        addChild(circle_array[i]);
       
        circle_array[i].id = i;
       
        vector_array[i] = new Array(Math.random() * 10, Math.random() * 10);
        mass_array[i] = circle_array[i].width * 0.10;
    }
   
    stage.addEventListener(Event.ENTER_FRAME, circle_update);
}

Obviously, first, we need to initialize the three arrays. Once inside the loop, quite a few things happen.

First, we call the constructor for the new circle, and insert it into the array, according to the loop variable i.

Set a random width so we get variate sizes of circles (minimum 15, max 40), and set the height equal to the same since circles are equal width and height (just the diameter).

Next thing, is a random position on the screen. Of course we want to constrain it into the region of view, so that’s the reason for all the accessing of the circle’s widths and heights, so we can be also close as possible to the edge without actually crossing over.

The next portion, about color, is easily the most confusing thing.  First, I set new_color to a random value between 0×000000 and 0×000077, which, according to our RGB setup, gives us a dark blue to black color. I then add 0×000088 to the mix, setting new_color now to a medium to extremely bright blue color.  The switch case breaks our creation of circles into three discrete chunks. A set that, when divided by three, has remainder 0, 1 ,or 2.  If the remainer is zero, I’m shifting left by 16 binary bits, which gives us a value between 0×880000 and 0xFF0000 (red!). If remainder is one, I’m shifting by 8 binary bits, giving us the new range 0×008800 to 0x00FF00 (green!). If remainder is two, I don’t do anything (leave it blue!).  Set the new_color to circle_array[i].inner to actually update our inner_circle, and we’re on to the next step.

addChild(circle_array[i]) actually puts our instance of the circle on the stage so it can be seen.

We set the id of the circle to be the number it was assigned with, for cleanliness.

And then store the data for the other two arrays.  In vector array, we initialize a sub-array of length two, each containing values between 0-10. The first represents our x velocity, the second our y velocity.  As for the mass, we set it to 1/10th the radius. While the 10% is arbitrary, it keeps the consistent ratio that, the bigger the circle, the more mass it has.

Once outside of the loop, we add the ENTER_FRAME event listener to our other function, so the balls actually can be updated from frame-to-frame.

That is it for the initialization of this actionscript3 source code.

__________________________________________________________________________________________

The final function, circle_update!

function circle_update(e:Event){
   
    for(var i:Number = 0; i < NUMBER_OF_CIRCLES; i++){
        circle_array[i].x += vector_array[i][0];
        circle_array[i].y += vector_array[i][1];
       
        //bounding wall collisions
        if(circle_array[i].x + (circle_array[i].width * 0.50) >= 550){ vector_array[circle_array[i].id][0] *= -1; circle_array[i].x = 550 - circle_array[i].width * 0.50; }
        if(circle_array[i].x - (circle_array[i].width * 0.50) <= 0){ vector_array[circle_array[i].id][0] *= -1;  circle_array[i].x = circle_array[i].width * 0.50; }
   
        if(circle_array[i].y + (circle_array[i].width * 0.50) >= 400){ vector_array[circle_array[i].id][1] *= -1; circle_array[i].y = 400 - circle_array[i].height * 0.50;  }
        if(circle_array[i].y - (circle_array[i].width * 0.50) <= 0){ vector_array[circle_array[i].id][1] *= -1; circle_array[i].y = circle_array[i].height * 0.50;   }
    }
   
    //ball to ball collisions
    for(var g:Number = 0; g < NUMBER_OF_CIRCLES; g++){
        var g_pos:Point = new Point(circle_array[g].x, circle_array[g].y)
        for(var k:Number = g + 1; k < NUMBER_OF_CIRCLES; k++){
            var dist = Point.distance(g_pos, new Point(circle_array[k].x, circle_array[k].y));
            if( dist < ((circle_array[g].width * 0.50) + (circle_array[k].width * 0.50)) ){
                //they're colliding!
                //elastic response
                var mass1:Number = mass_array[g];
                var mass2:Number = mass_array[k];
                var mass_sum = mass1 + mass2;
                var velX1:Number = vector_array[g][0];
                var velX2:Number = vector_array[k][0];
                var velY1:Number = vector_array[g][1];
                var velY2:Number = vector_array[k][1];
               
                var new_velX1 = (velX1 * (mass1 - mass2) + (2 * mass2 * velX2)) / mass_sum;
                var new_velX2 = (velX2 * (mass2 - mass1) + (2 * mass1 * velX1)) / mass_sum;
                var new_velY1 = (velY1 * (mass1 - mass2) + (2 * mass2 * velY2)) / mass_sum;
                var new_velY2 = (velY2 * (mass2 - mass1) + (2 * mass1 * velY1)) / mass_sum;
               
                vector_array[g][0] = new_velX1;
                vector_array[k][0] = new_velX2;
                vector_array[g][1] = new_velY1;
                vector_array[k][1] = new_velY2;
               
                circle_array[g].x += new_velX1;
                circle_array[g].y += new_velY1;
                circle_array[k].x += new_velX2;
                circle_array[k].y += new_velY2;
            }
        }
    }
}

 

Next up for this actionscript3 source code is this update function.  Let’s go through the source in some detail.

First thing that happens in the loop is I update the position of my circle via it’s X and Y velocities. Pretty simple.

circle_array[i].x += vector_array[i][0];
circle_array[i].y += vector_array[i][1];

Next up are the boundary collisions against the four walls.  The special thing about these is that all the circles have varying radii, so we have to be picky about testing for the boundary collisions in flash.

Since the circles are indexed from their absolute center, when a ball is touching the boundary, it’s actually the x-position PLUS the radius. Once collision is detected, we simple need to reset the circle’s position to the edge (precaution in case the circle was moving extremely fast, and will thereby get pinned outside the stage), adn flip the corresponding velocity.  Flip X for the left and right walls, flip Y for the top and bottom walls. That’s it!

//bounding wall collisions
        if(circle_array[i].x + (circle_array[i].width * 0.50) >= 550){ vector_array[circle_array[i].id][0] *= -1; circle_array[i].x = 550 - circle_array[i].width * 0.50; }
        if(circle_array[i].x - (circle_array[i].width * 0.50) <= 0){ vector_array[circle_array[i].id][0] *= -1;  circle_array[i].x = circle_array[i].width * 0.50; }
   
        if(circle_array[i].y + (circle_array[i].width * 0.50) >= 400){ vector_array[circle_array[i].id][1] *= -1; circle_array[i].y = 400 - circle_array[i].height * 0.50;  }
        if(circle_array[i].y - (circle_array[i].width * 0.50) <= 0){ vector_array[circle_array[i].id][1] *= -1; circle_array[i].y = circle_array[i].height * 0.50;   }

Only thing left is our elastic collisions, the meat of this flash actionscript 3 tutorial. Since every single circle needs to view every other circle, every single frame, we need a double embedded for loop.

So, if I have 10 circles, the inner loop will run 10 times, and for the second, it will run 9 (as we don’t need to repeat test with the first ball), for the third, 8, and so on..  Giving us a final loop count of 10+9+8+7+6+5+4+3+2+1 = 55!  In general purpose, this is (n^2 + n) / 2  which, in just general asymptotic running time, is O(n^2).  Not too bad I suppose, but if you’re running this on a slower PC, you may have frame-rate issues for a large amount of colliding objects.

Before we start the second, internal for loop, we store a copy of the first circle’s position in a temporary Point object, for quick reference. Given any two circles, of any size, collision can be tested very easily.  If the distance between the two circles (from their centers) is less than the sum of circle1′s radius and circle2′s radius, they’re touching!

Once a flash entity collision is confirmed, we need to find the resulting forces and directions for each circle.  What I am following in this program is an Elastic collision setup. A nice, general purpose formula can be seen right here..

v1 is our new velocity, u1 is our old velocity, u2 is the other circle’s old velocity, and m1 and m2 are the respective masses for each.

This is the exact equation we need, but it only accounts for one dimension, of one circle. We need to do this calculation four times, for X on circle1, Y on circle1, X on circle2, and Y on circle2. But.. we have all the data needed to complete the calculations, and converting this formula to actionscript 3 code, we get the following:

//elastic response
var mass1:Number = mass_array[g];
var mass2:Number = mass_array[k];
var mass_sum = mass1 + mass2;
var velX1:Number = vector_array[g][0];
var velX2:Number = vector_array[k][0];
var velY1:Number = vector_array[g][1];
var velY2:Number = vector_array[k][1];
               
var new_velX1 = (velX1 * (mass1 - mass2) + (2 * mass2 * velX2)) / mass_sum;
var new_velX2 = (velX2 * (mass2 - mass1) + (2 * mass1 * velX1)) / mass_sum;
var new_velY1 = (velY1 * (mass1 - mass2) + (2 * mass2 * velY2)) / mass_sum;
var new_velY2 = (velY2 * (mass2 - mass1) + (2 * mass1 * velY1)) / mass_sum;
               
vector_array[g][0] = new_velX1;
vector_array[k][0] = new_velX2;
vector_array[g][1] = new_velY1;
vector_array[k][1] = new_velY2;

 

Now, that last part where we actually update positions again, a second time in the frame, we need to do this to prevent the circles from “sticking” to each other.  If both balls are heading the same direction and collide, and we don’t update position a second time, there’s a much higher chance that the circles will end up overlapping, bouncing around the screen together and clumped, which looks bad.  We separate them by an extra boost to help prevent this output. To see what I’m talking about, remove those couple lines of code, and check it out in the flash tutorial demo.

 

Once more line of code though before you can compile, you need to actually call the initialization function!

 init();

At that, we are officially done!  The background art was something I just quickly sketched for presentation, and instead of a black ring around the circles, I’ve got a glowy gradient. But I’ll leave that to you guys (or just steal it from the source lol).

This type of physics is perfect for a bouncy type flash game, or maybe a pool flash game. All you’d need to do is change the art,  fire velocities only on hitting the cue ball, etc.. and of course a points system.

I hope you enjoyed this collision detection flash tutorial, and also hope you like the free actionscript3 source code.

 

-Dave at the Flash Cove (http://flashcove.net)

 

About Dave

Hey Guys! I'm Dave, an Ohio State CSE alumni who just recently started work in the industry. I'm a flash hobbyist at heart, and love making flash games and tutorials when the time permits. Check out what flash tutorials are available on the site, and don't forget to "Like Us!" on Facebook!