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.
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
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)); } }
Finally copy the file in the same folder than your Fla development. In the next section we will start playing with balls.
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.
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); } }
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);
}
}
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.
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)); } } } };