Advertisement

AABB collision

Started by March 29, 2024 09:20 PM
50 comments, last by JoeJ 6 months ago

Is this code good for AABB collision detection?

#include <iostream>

using namespace std;

class Rect
{
public:
	float x = 5;
	float y = 10;
	float w = 5;
	float h = 10;
};

int main(int argc, char* args[])
{
	Rect rect1;
	Rect rect2;
	if (rect1.x < rect2.x + rect2.w &&
		rect1.x + rect1.w > rect2.x &&
		rect1.y < rect2.y + rect2.h &&
		rect1.y + rect1.h > rect2.y)
	{
		cout << "Collision" << endl;
	}

	return 0;
}

It seems correct, but you should wrap it in a function to make it more reusable:

bool intersectRect( const Rect1& rect1, const Rect2& rect2 )
{
    return rect1.x < rect2.x + rect2.w && rect1.x + rect1.w > rect2.x &&
           rect1.y < rect2.y + rect2.h && rect1.y + rect1.h > rect2.y;
}

Generally it's also better to store the minimum and maximum coordinates of an AABB, instead of the minimum + size. Then your function becomes simpler:

bool intersectRect( const Rect1& rect1, const Rect2& rect2 )
{
    return rect1.min.x < rect2.max.x && rect1.max.x > rect2.min.x &&
           rect1.min.y < rect2.max.y && rect1.max.y > rect2.min.y;
}
Advertisement

How do I access intersectRect in the main function of the program? here is my code so far.

#include <iostream>

using namespace std;

class Rect1
{
public:
	float min = 5;
	float max = 10;
};

class Rect2
{
public:
	float min = 15;
	float max = 20;
};

bool intersectRect(const Rect1& rect1, const Rect2& rect2)
{
	return rect1.min < rect2.max && rect1.max > rect2.min &&
		rect1.min < rect2.max && rect1.max > rect2.min;
}
int main(int argc, char* args[])
{

	return 0;
}

Sorry I forgot the .x and .y in the intersectRect() function (now fixed).

this code works but,l how do I use intersectRect in the main function using the bool function?

#include <iostream>

using namespace std;

class Rect1
{
public:
	float x = 5;
	float y = 10;
	float w = 10;
	float h = 10;
};

class Rect2
{
public:
	float x = 15;
	float y = 20;
	float w = 10;
	float h = 10;
};

bool intersectRect(const Rect1& rect1, const Rect2& rect2)
{
	return rect1.x < rect2.x + rect2.w && rect1.x + rect1.w > rect2.x &&
		rect1.y < rect2.y + rect2.h && rect1.y + rect1.h > rect2.y;
}

int main(int argc, char* args[])
{

	return 0;
}

pbivens67 said:
Is this code good for AABB collision detection?

Simply beautiful. Bravo, Phil! \:D/

I agree min and max is the best convention to define a box.
That's because intersection tests are usually the most used operation, and min/max is faster than corner/width or center/width conventions.
But you don't have to do this, ofc.

Btw, Aressera forgot x and y in his code example. It should be:

bool intersectRect( const Rect1& rect1, const Rect2& rect2 )
{
    return rect1.minX < rect2.maxX && rect1.maxX > rect2.minX &&
           rect1.minY < rect2.maxY && rect1.maxY > rect2.minY;
}

probably.

Besides, i would make it a member function of the class, so:

class Rect
{
public:
	float minX, maxX, minY, maxY;
	
	bool Intersects( const Rect1& rect2 ) const
	{
    	return 	minX < rect2.maxX && maxX > rect2.minX &&
           		minY < rect2.maxY && maxY > rect2.minY;
	}
};

This has the advantage the code is more organized.
And you don't need to remember the function name.
You just type myRect. and the code editor should list all member functions.

The const stuff also is not necessary. It's a good habit but causes some confusion initially, so you can ignore that for now.

However, i would not initialize to magic numbers such as 5 or 10.
Personally i would not initialize at all for performance reasons (but that's considered bad practice these days, since the random values can cause bugs if we forget to initialize).

It makes sense to initialize to a ‘impossible, inverted’ range, like so:

float minX = FLT_MAX, maxX = -FLT_MAX, minY = FLT_MAX, maxY = -FLT_MAX;

This may feel wrong, but it's the proper initial state if we want to calculate a bounding box from multiple points or boxes.
It also helps to spot bugs if we forget to initialize.

Otherwise, if that sounds confusing, just initialize everything to zero.

But keep in mind you have to initialize to what you actually need.
It's a data structure now, and will have many instances.
So you can no longer set your desired magic numbers just once, like you're used to from evil global variables.

Usually people use constructors to make this convenient.
I'm too lazy to type this, but you should add it.
So the usage example / testing code would be:

int main(int argc, char* args[])
{
	float x1 = 5;
	float y1 = 10;
	
	float w = 5;
	float h = 10;
	

	float x2 = 7;
	float y2 = 12;

	Rect rect1;
	rect1.minX = x1; rect1.maxX = x1+w;
	rect1.minY = y1; rect1.maxY = y1+h;
	
	Rect rect2;
	rect2.minX = x2; rect2.maxX = x2+w;
	rect2.minY = y2; rect2.maxY = y2+h;
	
	bool interscts = rect1.Intersects(rect2);	
	if (intersects) cout << "Collision" << endl;
	

	if (rect2.Intersects(rect1) != interscts ) cout << "Paranoia justified" << endl;

	return 0;
}

In your example both rects had the same values, in case you did not notice (!)

But yeah, seems your on track to handle many objects very soon.

Rect invaders[40]; would just work. :D

Advertisement

how is this code

#include <iostream>

using namespace std;

class Rect1
{
public:
	float x = 5;
	float y = 5;
	float w = 0;
	float h = 0;
};

class Rect2
{
public:
	float x = 25;
	float y = 25;
	float w = 0;
	float h = 0;
};

bool intersectRect(const Rect1& rect1, const Rect2& rect2)
{
	return rect1.x < rect2.x + rect2.w && rect1.x + rect1.w > rect2.x &&
		rect1.y < rect2.y + rect2.h && rect1.y + rect1.h > rect2.y;
}

int main(int argc, char* args[])
{
	if (intersectRect)
	{
		cout << "Collision" << endl;
	};
	return 0;
}

It's bad.

Functionally it's the same, but what would you do if you needed 100 rects?

Since both of your rects store the same data, processed by the same intersection test, obviously you only want to write it once. One functionality, one code. It's not about how many instances of rectangles you need. It's about the functionality, which you only need once.

bool intersectRect(const Rect1& rect1, const Rect2& rect2)

Using this interface, you could not swap retc1 and rect2, like i did for my paranoid test.

Now imagine we have indeed 100 rects. How many intersection functions would we need to intersect each rect with each other? It's 100 x 100 copies of that same function. Ofc. we don't want that.

Think at it at a very low technical level maybe:

We have the intersection function, which takes two rectangles.
Both rectangles exist somewhere in memory.
So we could give two memory addresses.
The function makes some assumptions, e.g. that we have minX,maxX,minY,maxY in that order at the given address.
If we used a different order in memory, e.g. minX,minY,maxX,maxY, the function would calculate wrong results.

So to guarantee those assumptions are fulfilled, we use structs or classes, defining the layout of data in memory. It's in the same order we declare the member variables.

Once we have this, it does no longer matter where in memory the data is, since we can give any two addresses to the memory. At any of these addresses, there are the 4 values in defined order.

Thus, we can have 100 rects in memory, and intersect each with each other, simply by giving the two addresses we currently want, and we only need one function to do all our tests.
We also need only one struct or class to define memory layout.
But we can make as many instances of such struct as we want, either by using:
Rect r1, r2, r3;
Or by using:
Rect r[3];

Sleep over that. And then ask further questions.

you liked my original code. how do I instantiate the rect classes in the main function. here is my original code

#include <iostream>

using namespace std;

class Rect
{
public:
	float x = 5;
	float y = 10;
	float w = 5;
	float h = 10;
};

int main(int argc, char* args[])
{
	Rect rect1;
	Rect rect2;
	if (rect1.x < rect2.x + rect2.w &&
		rect1.x + rect1.w > rect2.x &&
		rect1.y < rect2.y + rect2.h &&
		rect1.y + rect1.h > rect2.y)
	{
		cout << "Collision" << endl;
	}

	return 0;
}

pbivens67 said:
how do I instantiate the rect classes in the main function.

You did it here:

	Rect rect1;
	Rect rect2;

This topic is closed to new replies.

Advertisement