In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: circle circle

Basic circle-circle (sum of radiuses) collision detection.

If it's a red dot, that means it is taking part in a collision; however, if it is a black dot, it isn't colliding with anything.

http://stackoverflow.com/users/500018/anthony-pace
Get Adobe Flash player
by auboux 22 Aug 2013
    Embed
// forked from NME's circle circle
//Basic circle-circle (sum of radiuses) collision detection.
//
//If it's a red dot, that means it is taking part in a collision; however, if it is a black dot, it isn't colliding with anything.
//
//http://stackoverflow.com/users/500018/anthony-pace

package {
    import flash.events.Event;
    import flash.display.Sprite;
    
    
    public class FlashTest extends Sprite {
        
        public var balls:Vector.<Ball>;
        public var ballMax:uint;
        
        public var radiusTotals:Vector.<Vector.<uint>>;
       
        public function FlashTest() {
           stage.frameRate = 60;
           stage.scaleMode = "noScale";
           init();
           
           
        }
        public function onEnterFrameEH(e:Event):void{
            updateAllBallsPos();
            checkForCollisions();//instead of drawing balls with the resultant information
            //in this case we call the function to draw the balls from the checkForCollisions function
        }
        public function updateAllBallsPos():void{
            //not using collision between object right now, and only considering stage boarders
            //also not taking into account that it shoud look like it's actually hitting the wall just yet
            var sw:uint = stage.stageWidth;
            var sh:uint = stage.stageHeight;
            var newX:int;
            var newY:int;
            var i:uint;
            var b:Ball;
            for (i=0;i!=ballMax;++i){
                b = balls[i];//
                newX = b.x+b.vX;
                newY = b.y+b.vY;
                //the following checks flip the driectiion the ball is moving in, if it hits a wall.
               if (newX>sw || newX<0){
                   ///since using an int, it should be the same as i *= -1;
                    b.vX = ~b.vX + 1;
                    newX = b.x+b.vX;
                }
                if (newY>sh || newY<0){
                    b.vY = ~b.vY + 1;//*=-1;// ~b.vY + 1;
                    newY = b.y+b.vY;
                }
                b.x = newX;
                b.y = newY;
                
            }
        }
        public function init():void{
            ballMax = 20;
            balls = new Vector.<Ball>(ballMax,true);
            var rr:uint;//random radius
            var rd:uint;//random radius * 2 = random diameter
            for(var i:uint = 0; i!=ballMax; ++i){
                rr = 15+uint(Math.random()*15);
                rd = rr<<1;
                balls[i]=new Ball(rr);
                balls[i].x = uint(rr+Math.random()*(stage.stageWidth-rd));
                balls[i].y = uint(rr+Math.random()*(stage.stageHeight-rd));
                balls[i].vX = -10+int(Math.random()*21);
                balls[i].vY = -10+int(Math.random()*21);
                
                
                //I could add them to stage here, but I personall want to wait until after I have checked  for collision
            }
            
            //make the lookup table for total distances from one circle to another
            //will only get rid of a bunch of + operations
            //var bmn1:uint = ballMax -1;
            radiusTotals = new Vector.<Vector.<uint>>(ballMax,true);
            
            for(i=0;i!=ballMax;i++){   //n*n loop
                radiusTotals[i] = new Vector.<uint>(ballMax,true);
             for (var j:uint = i+1; j<ballMax;j++){//
                 
                     radiusTotals[i][j]= (balls[i].radius +balls[j].radius)*(balls[i].radius +balls[j].radius)+1;
                 
             }
            }
            
            checkForCollisions();
            
            //now make all the balls visible by adding them to the stage
            for each(var b:Ball in balls){
                stage.addChild(b);
            }
            
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrameEH);
        }
        public function checkForCollisions():void{
            var collisionList:Vector.<Boolean> = new Vector.<Boolean>(ballMax,true);
            //I could do more with this, by making it so it collects the id and type of object it is colliding with to get certain reactions
            
            
            var blen:uint = ballMax;
            var blen_neg_1:uint = blen-1;
            var i:uint,j:uint; //used by the loops for itteration through the list
            var b1:Ball,b2:Ball;
            var r1_plus_r2_plus_1:uint;
            var w:int,h:int;
            //var hyp:uint;
            var hypSquared:uint;
            
            
            
            //not a great method for doing this; for, the more balls you have the more points need to be checked,
            //eg. if there are 10 points, ((n-1)*n)/2 = 45 checks
            // 100 points means 4950 checks...
            // 1000 points means 499500 checks... see what I mean?
            //like I said, not great, but it is a fundamental starting point.
            for(i = 0; i!=blen_neg_1; i++){
                
                b1 = balls[i];
                for(j=i+1;j!=blen;j++){
                    b2 = balls[j];
                    //instead of calculating this every time, if no sizez will be updated,
                    //you could just calculate it once and store it in a lookup table to speed things up
                    //
                    //but if things will change you can just use the line below
                    //r1_plus_r2_plus_1 = b1.radius + b2.radius+1;
                    
                    //now we get the distances using the pythagorean theorum a^2 + b^2 = c^2
                    h = b2.y-b1.y; //a the height of the triangle
                    w = b2.x-b1.x; //b the width of the triangle
                    //hyp = uint(Math.sqrt(w*w+h*h)+0.5);//helps round to the right amount, since it is automatically turned into a uint
                    hypSquared = h*h+w*w;
                    
                    if(hypSquared<radiusTotals[i][j]){
                    //if(hyp<radiusTotals[i][j]){//if(hyp<r1_plus_r2_plus_1){//if(hyp<r1_plus_r2_plus_1){
                        //we simply update it to be true, since we are trying to detect for collision
                        collisionList[i] = true;
                        collisionList[j] = true
                    }
                }
                //instead of drawing here, as I am doing below, you could return the boolean list 
                //(or another type of list that also include which items collided)
                //and use that information in another function to modify the positions; yet, that could over complicate things
                //so be careful ... for simplicity, I am just doing it here.
                b1.drawBall(collisionList[i]);
            }
            //putting this here avoids an additionals pass through the loop, which avoids checks
            //and still draws the last ball
            b1 = balls[i];
            b1.drawBall(collisionList[i]);

        }
    }
}
import flash.display.Graphics;
import flash.display.Sprite;
internal class Ball extends Sprite{
    public var vX:int,//the velocity for x
               vY:int,//the velocity for y
    
               radius:uint, //no need for a getter / setter so just make it public
               drawnStatus:int;// no great way to get a tri state flag in as3, so I might as well track status.
    
    private var _normalColour:uint,  //just keeping these private, but there is really no need
                _collisionColour:uint; 
                //I am just used to declaring things I don't intend on allowing a direct
                //update of private.  This allows you to enforce an action when something wants to get or set
                //you probably already know this if your reading the code, but I figured I would add the comment anyways
    
    public function Ball(radius:uint = 5, normalColour:uint = 0x000000, collisionColour:uint = 0xff0000):void{
        _normalColour = normalColour;
        _collisionColour = collisionColour;
        this.radius = radius;
        drawnStatus = 0;
    }
    //redrawing really sucks up resources, so I have added some checks to be sure we don't redraw if not needed
    //I could just store a list of assets to make it so it doesn't have to call the draw circle function again
    // and thus make it faster, but that will be for another tutorial.
    public function drawBall(collision:Boolean=false):void{
        var colour:uint;
        var g:Graphics = this.graphics;
         if(collision){//we are colliding, so...
            if(drawnStatus==1){ //if there is a collision, but we are already a red ball, there is no need to update what we look like
                return;
            }
            //state that the status of the ball, is that it is red, make the colour used for the ball red
            drawnStatus = 1;
            colour = _collisionColour;
            
        }else{//we are not colliding, so...
            if(drawnStatus==2){//if there we are not apart of a collision, but we are already a black ball, there is no need to update what we look like
                return;
            }
            //state that the status of the ball, is that it is black, make the colour used for the ball black
            drawnStatus = 2;
            colour = _normalColour;
            this.cacheAsBitmap = true;
          
        }
        
        g.clear(); // very important that you add this, or you will notice a memory leak, as you draw over the old object.
        //g.lineStyle(2,0x00cc00,1,false,'none'); //green line to show boarder and make following the ball easier
        g.beginFill(colour);
        g.drawCircle(0,0,radius);
        g.endFill();
    }
}