Collision Series:AABB to AABB - Detection 09/12/2009
AABB to AABB - DetectionHave you ever played a game with bad collision? It's not a very good experience. Bad collision in a game snowballs into other problems. It can interfere with the controls, interfere with the gameplay, and basically anything else that relies on objects overlapping (which is typically a lot). If you are just starting Actionscript, then you are probably using hitTestPoint() or hitTestObject(), flash's built in collision detection. HitTestPoint() tests any given point pixel-perfectly against a graphical object. hitTestObject() uses two graphical objects' bounding boxes to collide.There are pro's and cons with both: Pros: hitTestPoint() - Easy to use, and it tests one point pixel perfectly against a graphical object. This can be extremely useful, as it is pixel perfect(meaning it returns true is it is touching any pixel that the object is comprised of). hitTestObject() - Easy to use is honestly the only thing I can think of. It's pretty crappy otherwise. Cons: hitTestPoint - A point is a very small area (obviously). What if you need a bigger area? Using a lot of hitTestPoints will result in a slower program to get the same results as with other methods. hitTestObject() - You dont get any flexibility with hitTestObject. The bounding boxes are being tested (the boxes who's dimensions are the objects width and height). That means if you dont need that entire area tested, or need more tested, you are out of luck. Not to mention that its a very slow method. Though theres not really a suitable cure for hitTestPoint() that I can give you, I can give you the cure for hitTestObject! Now after that very long intro, I can finally say: Welcome to the first of my collision series tutorials: AABB to AABB! The CureEssentially, what I'm going to give you is a hitTestObject with flexibility and speed. The collision series tutorials are based on math (everyones favorite subject), so if you have some kind of phobia, please leave (dont leave). This type of collision detection is called AABB to AABB (AABB is an axis aligned rectangle [no rotation]). Example: How it worksIt is actually quite simple. We just have to calculate the distance between 2 of its axes. Then you check to see if they are touching by comparing the distance to the maximum distance they could be while still touching. Confused? Dont be, I will show you. In order to be overlapping another rectange, it has to be overlapping on both axes, like so: How we do that stuffTesting this in Actionscript is simpler even than it looks. We need a little bit of information first though. We need the width of both of the objects, the height of both of the objects, and the x,y positions of both of the objects. Luckily, flash makes this easily avaliable to us. We will work with two rectangles (lets call them r1 and r2): var wr1:Number = r1.width; var hr1:Number = r1.height; var wr2:Number = r2.width; var hr2:Number = r2.height; var xr1:Number = r1.x; var yr1:Number = r1.y; var xr2:Number = r2.x; var yr2:Number = r2.y; That was easy. And thats all the raw information we need to solve this collision problem. From now on. We will simply be manipulating this information from now on. The next thing we will need to do is find the max distance between them before they stop overlapping. If you think about it, it is just half of their width added together. Then half of their height added together for the other axis. Since we wont be needing the full width or height data of the rectangles, lets go back and halve our variables: var wr1:Number = r1.width/2; var hr1:Number = r1.height/2; var wr2:Number = r2.width/2; var hr2:Number = r2.height/2; Awesome. Let's calculate the max distances now: var maxDistX:Number = wr1 + wr2; var maxDistY:Number = hr1 + hr2; Now were getting somewhere. Of course, this is just the begginning. We havent even tested the overlap yet. So close, yet so far... We can easily check the distance between the rectangles on both axes by subtracting the x and y distances of the 2 rectangles. Guess what that mean? More variables!!! var xDist:Number = xr1 - xr2; var yDist:Number = yr1 - yr2; We are now finally ready to check the overlap!!! All this variable preparation has lead us up to this: var overlap:Boolean; if(xDist <= maxDistX && yDist <= maxDistY){ overlap = true; } else { overlap = false; } If you test the code ouy on a couple of rectangles, you may notice that it doesn't work. Why could that be?! Well, your xDist or YDist variables arent always positive. A negative reading will skew the equation, because there isn't negative distance. Flash gives us the Math class to handle such things, specifically Math.abs(). Lets make it into a variable for speeds sake (pulling a static class variable from a local position is kind of slow): var fAbs:Function = Math.abs; Now we can redo that nasty code: if(fAbs(xDist) <= maxDistX && fAbs(yDist) <= maxDistY){ overlap = true; } else { overlap = false; } Eh. That code is working perfectly fine, but if/else statements arent the fastest way to go. If only we had a really codey looking operator that could make the code faster and impress our friends out of confusion... Enter the conditional operator. The conditional operator looks something like this: (booleanValue) ? ifTrue : ifFalse; Thats not so bad right? Lets add our if/else statement in there: (fAbs(xDist) <= maxDistX && fAbs(yDist) <= maxDistY) ? overlap = true : overlap = false; Congratulations!!! You've just... well... replicated hitTestObject(). Dang!!! We can improve it. We have the technology.That sounded a lot cooler in my head. Anyways, we now have a faster version of hitTestObject(), which still doesnt help us in the flexibility department. I do still have a cure though, so dont run away yet. The good thing about math based detection is that it's based on numbers. So why don't we just change the numbers! This would be a good use of a function with parameters. This is the anatomy of the function: function someFunction( param1:Type, param2:Type, etc:Type):ReturnType{ //do stuff here }; The function can use the parameters you give it as variables inside the function. This is not only super cool, but amazingly useful. lets write our function and its parameters now: function checkAABB(r1:MovieClip, r2:MovieClip, custW1:Number = 0, custH1:Number = 0, custW2:Number = 0, custH2:Number = 0):Boolean{ //function stuff here } we are shoving lots of info into our function here, so I will explain it all: r1 & r2 are our faithful rectangles custW1 & custH1 are the numbers we want to set as our 'width' and 'height' for r1 custW2 & custH2 are the same thing, but for r2 You may notice that the custom widths and heights are set equivelant to something already. What this allows us to do is skip over those if we want, as they already have a pre-set value. That means if we just type... checkAABB(rectangle1, rectangle1); ... it will set the testing widths and heights to 0. Wait... we dont want that. Lets make some conditionals to help set it straight and make it easy on ourselves. If we dont want to type in a custom test width or height, just set it to the real width or height: function checkAABB(r1:MovieClip, r2:MovieClip, custW1:Number = 0, custH1:Number = 0, custW2:Number = 0, custH2:Number = 0):Boolean{ var wr1:Number; var hr1:Number; var wr2:Number; var hr2:Number; (custW1 == 0) ? wr1 = r1.width/2 : wr1 = custW1/2; (custH1 == 0) ? hr1 = r1.height/2 : hr1 = custH1/2; (custW2 == 0) ? wr2 = r2.width/2 : wr2 = custW2/2; (custH2 == 0) ? hr2 = r2.height/2 : hr2 = custH2/2; } Ok, all set. Now if we dont enter a custon value for them, they automatically become the true width or height. Now just add the rest of your code and return overlap (since the function requires you to return a value with the type Boolean). It'll look something like this: Final Codefunction checkAABB(r1:MovieClip, r2:MovieClip, custW1:Number = 0, custH1:Number = 0, custW2:Number = 0, custH2:Number = 0):Boolean{ var fAbs:Function = Math.abs; var overlap:Boolean; var xr1:Number = r1.x; var yr1:Number = r1.y; var xr2:Number = r2.x; var yr2:Number = r2.y; var wr1:Number; var hr1:Number; var wr2:Number; var hr2:Number; (custW1 == 0) ? wr1 = r1.width*.5 : wr1 = custW1*.5; (custH1 == 0) ? hr1 = r1.height*.5 : hr1 = custH1*.5; (custW2 == 0) ? wr2 = r2.width*.5 : wr2 = custW2*.5; (custH2 == 0) ? hr2 = r2.height*.5 : hr2 = custH2*.5; var maxDistX:Number = wr1 + wr2; var maxDistY:Number = hr1 + hr2; var xDist:Number = xr1 - xr2; var yDist:Number = yr1 - yr2; (fAbs(xDist) <= maxDistX && fAbs(yDist) <= maxDistY) ? overlap = true : overlap = false; return overlap; } OverviewI really drug this tutorial on and on. Thats not very much code, but its flexible on rectangle size and faster than hitTestObject! Please look for the next tutorial, where I show you how to separate the rectangles when they overlap! *Edit* Next tutorial is here: AABB to AABB - Separation CommentsSat, 12 Sep 2009 13:30:47 Alright! I'll have to add this to my game, thank you for making this. Lengths great since it goes into explaining it. PS: Maybe you should show your working swf(The one you shoulded us in the chat room) in the end of the tutorial. Your comment will be posted after it is approved. Leave a Reply |


RSS Feed


