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
// 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();
}
}