ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Balls chain with constraints, colision, mouse follow and the math needed
http://www.actionscript.org/resources/articles/701/1/Balls-chain-with-constraints-colision-mouse-follow-and-the-math-needed/Page1.html
Alejandro Quarto
Partner, Software Development/General Manager at Persuasive Games. In his role, Mr. Quarto is responsible for software development and testing, as well as studio management and strategy.
Prior to joining to Persuasive Games, Mr. Quarto started his career in interactive entertainment as a freelance programmer using different object-oriented programming tools specifically specializing in Macromedia Flash, Shockwave, and Java development platforms among others. In 2002 Mr. Quarto co-founded Platico Games! in Buenos Aires, Argentina. While at Platico Games Studio, Mr. Quarto produced a wide variety of video game projects including: advergames, educational games, and multiplayer games oriented to big communities.
These games have been developed for clients such as: Claxson Interactive Group, Budweiser, Mattel, Turbo Video Brazil, Canal 13 (TV) Argentina, and FM HIT in Chile. 
Quarto is currently teaching about Flash development, in an institute located in Buenos Aires. In his role as instructor, he teach how to develop webs and games, for online content. Personal Blog: http://www.alejandroquarto.com

 
By Alejandro Quarto
Published on November 24, 2007
 
This tutorial will cover the main idea behind the constraints when playing with balls, to be able to create a chain. I will also explain how to follow the mouse pointer by using math. Finally we are going to extend the math class, so you will have new methods like distance between points.

Take a look to the tutorial goal

In my first tutorial, i will try to explain a simple example. Sorry about my english and if you want to see all of this in spanish, you can try in my personal blog www.alejandroquarto.com.

Note: possibly, this is not the best way to do it, you can always improve my code. Anyway i want to keep it simple for learning, for those that are not the best coders in the world and that may want to get nice results in a few hours.

This is one of the possible results that you can get by doing this tutorial:



Basically we have a chain composed with an X amount of balls. The first one is always following the mouse pointer, then one after the other, will follow the next ball.

It seems simple, anyway, the complex part in this example is to be able to handle all the balls collision. So, we have to detect overlapping between balls and try to correct each position.

We will need to calculate distance between points for a lot of things in this example, so first, let's try to extend the Math class by adding some new useful methods. This will help you in the future, and you will avoid to re write functions in all your developments


Extending Math Class

I know, there are another ways to do this. Anyway this is a simple one.

Create a new ActionScript File, all we need is to write code, so no fla is needed for this purpose.

Main idea is to add new methods than can be called from our main FLA, or any other that you may create. Not all of this methods will be used in our example, but i think is good to share.

Here is it the code:

class Math2 extends Math { static function rndBetween(n1:Number, n2:Number) { return n1+random(n2-n1+1); } static function radToDeg(n:Number) { return n*180/Math.PI; } static function degToRad(n:Number) { return n/180*Math.PI; } static function degSin(n:Number) { return Math.sin(n/180*Math.PI); } static function degCos(n:Number) { return Math.cos(n/180*Math.PI); } static function degTan(n:Number) { return Math.tan(n/180*Math.PI); } static function angleBetweenRad(x1:Number, y1:Number, x2:Number, y2:Number) { return Math.atan2((y1-y2), (x1-x2)); } static function angleBetweenDeg(x1:Number, y1:Number, x2:Number, y2:Number) { return Math.atan2((y1-y2), (x1-x2))*180/Math.PI; } static function distanceBetween(x1:Number, y1:Number, x2:Number, y2:Number) { return Math.sqrt(Math.pow((y1-y2),2)+ Math.pow((x1-x2),2)); } }



The first line means that we are extending the Math class with this new one called Math2. Basically we have functions to calculate Radians-degrees convertions, distance between points, and also angles.

Save the file as Math2.as, use this name or it will refuse to work.

Finally copy the file in the same folder than your Fla development. In the next section we will start playing with balls.


Attaching Balls in the scene
The first thing you need is a Ball, so you can do it with a circle shape. Once you get the draw, select and press F8 to create a MoveClip symbol. Select "export for actionscript", and fill the identifier textfield with "circle". Finally press OK and remove your movie from the scene.

We are going to attach each ball from the library.

Since we created a new Math class, we are going to import it before we can do anything with that file, so in the first frame in the root, put this line of code:

import Math2;

Now, is time to add the balls. We are going to use a function for that, and it will be called "init". The code:

function init() { balls = 20; reductionMultiplier = 1; for (i=1; i<=balls; i++) { this.attachMovie("circle","mc"+i,_root.getNextHighestDepth(),{_x:30*i, _y:200}); } } init();

"balls" is the number that we are going to attach, you can use whatever you want if the performance is good for you. "reductionMultiplier" is just a multiplier to define how fast each movie will follow the other one. So, if you use a number higher than 1, it will become slowly and elastic.

After the variables, we have a simple loop for balls creation (attachMovie). Initial position is up to you.

Finally, we call the function by using the "init()" line.


How to make a movie to follow the mouse.
In the example the first ball is always following the mouse pointer. We need a function that can be used in the example and any other in the future. So here we go!

function followMouse(mc, lowerDistance) { orig = {x:mc._x, y:mc._y}; dest = {x:_root._xmouse, y:_root._ymouse}; myAngle = Math.atan2((orig.y-dest.y), (orig.x-dest.x)); myDistance = Math2.distanceBetween(orig.x, orig.y, dest.x, dest.y)-lowerDistance; if (myDistance+lowerDistance>=lowerDistance) { mc._x -= Math2.cos(myAngle)*(myDistance/reductionMultiplier); mc._y -= Math2.sin(myAngle)*(myDistance/reductionMultiplier); } }


This function needs two arguments:
1- A movieclip (mc), this is the movie that will follow the mouse.
2- How far the movie can be from the cursor? (lowerDistance), in pixels.

Then, we have two points with their coordenates (the movie x and y, and the mouse x and y).
The angle calculation is using Math.atan2 method that calculates the cuadrant for you. 
We also need to know the distance between both points, here is when we use the Math2 class, by calling the distanceBetween method.

Finally if the distance is higher than the lowerDistance, we need to move our movie along the angle we have calculated with our distance. In the X axis we use Math.cos, in the Y, Math.sin. Note, if you increase the reductionMultiplier, movement will be slowly.
 

Simple: how to make a movie to follow another movie
This is really easy if you understand the last function, cos basically is the same thing. We are just changin the mouse cursor with anoter movie position.
function follow(mc, myTarget, lowerDistance) {
 orig = {x:mc._x, y:mc._y};
 dest = {x:myTarget._x, y:myTarget._y};
 myAngle = Math.atan2((orig.y-dest.y), (orig.x-dest.x));
 myDistance = Math2.distanceBetween(orig.x, orig.y, dest.x, dest.y)-lowerDistance;
 if (myDistance+lowerDistance>=lowerDistance) {
  mc._x -= Math2.cos(myAngle)*(myDistance/reductionMultiplier);
  mc._y -= Math2.sin(myAngle)*(myDistance/reductionMultiplier);
 }
}


Now we have another argument (the target movie) instead of using the mouse position. That's all!

Next section will cover how to keep a distance between balls

Constraints, fun is comming!
Again, we need another function to be able to keep all the balls away one from each other. The function:
function keepDistance(obj1, obj2, dist) { orig = {x:obj1._x, y:obj1._y}; dest = {x:obj2._x, y:obj2._y}; myAngle = Math.atan2((orig.y-dest.y), (orig.x-dest.x)); myDistance = Math2.distanceBetween(orig.x, orig.y, dest.x, dest.y); if (myDistance<=dist-0.1) { obj1._x -= Math2.cos(myAngle)*((myDistance-dist)/2); obj1._y -= Math2.sin(myAngle)*((myDistance-dist)/2); obj2._x += Math2.cos(myAngle)*((myDistance-dist)/2); obj2._y += Math2.sin(myAngle)*((myDistance-dist)/2); } }


There are not a lot of changes here..basically we want to avoid one ball over another one. So, when the distance is lower than the distance that we want, we will have to move both movies along the angle, with the distance, in an opposite orientation. Since both balls are equal in size, we use the distance divided by 2.

Note that 0.1 is just a correction to avoid litle movements.

Next section will cover the integration of all the work we have so far.


All together
Finally, we are going to put all together, we just need to create some instructions for our functions usage.

The last code:

this.onEnterFrame = function() { followMouse(mc1,0); for (i=1; i<=balls; i++) { a = i+1; follow(_root["mc"+a],_root["mc"+i],(_root["mc"+a]._width/2)+(_root["mc"+i]._width/2)); for (j=1; j<=balls; j++) { if (j<>i) { keepDistance(_root["mc"+j],_root["mc"+i],(_root["mc"+i]._width/2)+(_root["mc"+j]._width/2)); } } } };



I've used the enterFrame, anyway you can build a function and use the setInterval to avoid FPS problems, and fine tuning.
We need to update our system constantly. First of all, our ball number 1 should follow the mouse with our followMouse function, first argument is our ball, second is the distance...in this case just 0.
Then, there is a Loop, main idea behind this is that we need each ball to follow the next one, in this way we have the chain. With our function follow we send 3 arguments: 1. ball to move, 2: ball to follow, 3. minimum distance between both, in this case the sum of both radius.
Finally, we have another loop, a nested one. Why this? Well, if we don't check each ball with all the others, is hard to see how all the balls overlaps. That's why we made a "keepDistance" function. This means, for each ball, we check distances for all the balls (not the same one). If there is an overlap, then move it.
That's all!
I hope you can enjoy this tutorial, and you can always get an explanation in spanish in my personal blog: www.alejandroquarto.com
Sorry about my english.
Source code in Flash 8 in the attachment at the bottom of the page.