AABB to AABB - Separation

Last time we learned how to check for a collision between two axis aligned rectangles and that collision detection doesnt have to be scary.

First, an optomization:

Thanks to John Blackburne for pointing out a code optomization:

In your code, change any division by 2 to multiplication by .5 (multiplication is faster than division).



Helpful, but...

While having good collision detection is helpful, its not that useful. If you are making a game (especially a platformer), chances are that you need to do something after they collide. You know, something along the lines of separating them so that they DON'T overlap. A character going through the wall or ground isn't my idea of a good game. So lets do something about it!

How this is done:

You may say: "Well thats all fine and dandy, but how do you get to that point?


The good news is that we already have all the information we need with the last code, so we just need to change it a little. Instead of making a function that returns a boolean, we'll just make a function that DOES something. No returns nessecary.


This is what we have right now:


function 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;

(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;

}


This is what we will need to change:


function collide(r1:MovieClip, r2:MovieClip, custW1:Number = 0, custH1:Number = 0, custW2:Number = 0, custH2:Number = 0):void{

First, we change the function name. We arent just checking anymore. We're colliding. then we change the return type from Boolean to void. We wont need to return anything.


With that in mind, we'll skip to the end and just erase this line:

return overlap;

Now this is when the real change happens. This is the code that will separate the rectangles if they chould overlap. Change this:

(fAbs(xDist) <= maxDistX && fAbs(yDist) <= maxDistY) ? overlap = true : overlap = false;

To this:

if(r1 != r2){

if(xDist <= maxDistX && yDist <= maxDistY){
 
if(maxDistX - xDist < maxDistY - yDist){

(r1.x < r2.x) ? r1.x -= maxDistX - xDist : r1.x += maxDistX - xDist;

}

else if(maxDistY - yDist < maxDistX - xDist){

(r1.y  < r2.y) ? r1.y -= maxDistY - yDist : r1.y += maxDistY - yDist;

}

}

}

Explanation of changes

if(r1 != r2){

All this means is: If r1 is not the same object as r2. This prevents an object from trying to seperate from itself (it does nothing, so it wastes CPU).

if(xDist <= maxDistX && yDist <= maxDistY){

You already recognise this code, this just detects if they are overlapping.

if(maxDistX - xDist < maxDistY - yDist){

(r1.x < r2.x) ? r1.x -= maxDistX - xDist : r1.x += maxDistX - xDist;

}


This code checks to see if the distance the recatangles are overlapping is smaller on the x axis than the y axis. To seperate objects realisticlly, you need to use the smallest distance of overlap, and only seperate one axis at a time.

After it checks to see if the x axis overlap is smaller than the y axis overlap, then it checks to see which side r1 is on comparitivly to r2. It then pushes it out the exact distance nessecary (determined by taking the maxDistX and subtracting it from the distX. This is the exact amount of overlap).

else if(maxDistY - yDist < maxDistX - xDist){

(r1.y  < r2.y) ? r1.y -= maxDistY - yDist : r1.y += maxDistY - yDist;

}


This is the same thing, but it only activates if the y overlap is larger than the x overlap.

That was simple

This is further proof that you dont need to stress over basic collision. It is simple stuff. what we have here is pixel perfect collision detection and seperation for simple axis alligned rectangles. Feel free to use this code as you see fit, of course.


Suprisingly, the collision seperation is faster than the checking itself. Strange? Yeah, pretty much.


Here is what you end up with:
 


Comments

Warll

Sun, 13 Sep 2009 22:52:07

Wow, that is nice. Sadly now that i think about it my game is too simple to use this. I promise though I will try and use it, although bar that I have learned a fair bit from this tutorial.

 

Sidar

Mon, 21 Sep 2009 08:01:31

Hi there,

Im using both codes for box collision and checking the the separation.

Both are done in two separated functions.
The simple box collision works as it should, i even added a local to global boolean.

So thanks for that.
However the separation code isn't working as it should.
Im making this small engine for my artists so they can just drag and drop their library clips onto the stage so they can focus on level design.

So far it works, my engine is getting more efficient for them. However now i made a small collision box class,nothing more than a movieclip containing a box shape. The point is that they can drag this movieclip onto the stage to place the collision to their liking.
The widh and height are taken into count automatically.

But when i use your separation code it blocks the player only on the bottom and right side of the box and on those axis into infinity.

If i manage to get to the other side( by forcing it trough the box) it pushes the player way up into the top left corner, into infinity.

IS this due the fact im dragging and dropping the bounding box?
I Checked the code trillions of times...perhaps im just having a bad eyesight.

Any kind of help would be appreciated.

Also thanks for this simple solution to box collision.
I'm already pumped to try new stuff with it( if i could get it to work arghhh).

Thank you,
Sidar

 

Sidar

Tue, 22 Sep 2009 01:15:17

Hi there =).
Thanks for the email the other day.

It seemed like the missing part was indeed
the Math.Abs part that isn't typed out in this script.

var minDistX :Number = Math.abs(rectX - rectX2);
var minDistY :Number = Math.abs(rectY - rectY2);

This seem to do all the magic for some reason.
Although my character is also mouse controlled like in your example but it seems to ease in to the collision box.
At some point it does indeed stop but it still forces it self a bit over the edge.
Any thoughts on how this could be?
Or should i add the velocity to the height and width of my character?

But so far so good =)

Again thanks for the tutorial =D,
Sidar

 

Sidar

Tue, 22 Sep 2009 02:23:34

Ah ofcourse!
I was updating the collision before the player movement.
This caused the position to be updated to early.

It works now!

Thanks for this awesome tutorial.

Greetings,
Sidar

 

Tue, 22 Sep 2009 08:02:23

Thanks, glad I could help.

 

Sidar

Thu, 24 Sep 2009 01:36:32

Hi, its me again...
yerr...

I was wondering, and im pretty sure of it...could you explain how i can get a slope collision with these functions?
I mean the idea is pretty much the same right?
( i just cant figure it out >_<)

It would be nice to get a slope defined by height and rotation( 0,90,180,360 degrees of course, nothing in between ).

As i said before im using the Flash-IDE to drag and drop collision boxes onto the stage, so far the collision-boxes works, but i would like to have slopes and just rotate them on the stage.

The character seems to move frozen-like when its stuck behind a box(its mouse controlled and the distance between the character and mouse is basically the force of how fast it should move, so when it finally gets away from the box it shoots up to the mouse), a slope would be nice to have.

Any information on this would be awesome.

Greetings,
Sidar.


 

Makoto

Fri, 25 Sep 2009 16:29:00

Aloha,
It seems I am also having the same issue as Sidar was experiencing although I am not understanding the fix that was posted. Also, would you consider posting the source of your example?
Thank you.

 

Randall

Sat, 26 Sep 2009 18:51:44

You should provide the .fla for download so we can have all the code in one place.

 

Sat, 26 Sep 2009 21:09:14

Thanks for all the comments.

@Sidar: Sorry, I don't know how to use this on a slope. That requires knowledge of direction vectors, which I know nothing of. Google search 'N tutorial', and you will find an amazing (but highly advanced) tutorial for collision detection.

@Makoto: I will be posting the source soon, sorry about that. I totally forgot about it :\

@Randall: The .fla wouldnt help you, as I code in external .as files (as you should *insert stern look*). I will post the source code, but the .fla wouldn't actually have the code in it.

Thanks for all of the feedback, these are my first tutorials, there will be more to come!

 

Sidar

Fri, 09 Oct 2009 02:55:15

Ah, thats unfortunate.
Well in that case ill check those tutorials out.

TonyPa(google it) Seem to have a good tutorial too.

Anyway thanks again =)!

@ Makoto,
var xDist:Number = xr1 - xr2;
var yDist:Number = yr1 - yr2;

in my code i did:

var xDist:Number = Math.abs(xr1 - xr2);
var yDist:Number = Math.abs(yr1 - yr2);
(or use fAbs() instead if you are using that).

This seems to work for me though.

 

Your comment will be posted after it is approved.


Leave a Reply