Advertisement

AABB collision

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

pbivens67 said:
is this code good enough

No.
You have forgotten about the goal. You want to collide one instance of a brick struct with a second instance of the brick struct.
Like so:

std::vector<Brick> bricks(2);
bricks[0].x = 5;  // ... set all values for the first brick
bricks[1].x = 10; // ... set different values for the second

bool isColliding = bricks[0].TestCollision(bricks[1]);

That's what you want.

What you actually have is:

You have x and brick_x member variables, but x s unused and should be removed. (I guess you was thinking x is to be used for another object which you wanted to collide with the brick.)

bool TestCollision(float x, float y) This member function does not take a 2nd brick as parameter, but just a single point.

The member function does not test box vs. box, but answers ‘is the given point inside the box’?
Which is not what you want.

You need to read the whole thread again. It has detailed examples which would work.
Make a class that has just x,y,w,h to define a rectangle, and a member function to test against a second given instance.
Remember: You need to do it in a way so you can handle many bricks, not just a small number with them all hard coded.

how do I adjust my member variable to take the 2nd parameter as a brick instead of a point?

Advertisement

pbivens67 said:
how do I adjust my member variable to take the 2nd parameter as a brick instead of a point?

You don't!
The member variables are only for the brick of the current instance, so the ‘first’ brick.
So the ‘second’ brick, given as parameter to a member function, never goes to member variables.

For example:

struct Brick
{
	float x,y,w,h;
	
	bool CheckCollision (const Brick &second) const
	{
		return ! (	second.x + second.w < x || second.x > x+w ||
				second.y + second.h < y || second.y > x+h );

	}
};

The second brick is given as a parameter, but no member variables are required for that.
Out Collision function has nothing to do with the data a brick needs.
The member variables define the data fro a single brick, nothing more.
If we want two bricks, we use two instances of our Brick struct, but we change or add to the member data.

Here is an alternative to the code above:

struct Brick
{
	float x,y,w,h;
};

bool CheckCollision (const Brick &first, const Brick &second)
{
	return ! (	second.x + second.w < first.x || second.x > first.x + first.w ||
			second.y + second.h < first.y || second.y > first.x + first.h );

}

Here we don't use a member function at all, but a global function instead.
The global function takes two (references to two) different instances from our Bricks as parameters.
This works just as well. If you like it better, do it this way.
It may help to limit initial confusion about member functions.
This also is compatible with C language, while the above method requires C++, just fyi.

how do I incorporate vectors into this code?

std::vector<Brick> bricks;

// create a grid of bricks

for (int y=0; y<3; y++)
for (int x=0; x<5; x++)
{
	Brick brick;
	brick.x = x*30;
	brick.y = y*20;
	brick.w = 40;
	brick.h = 25;
	
	bricks.push_back(brick);
}

// collide each brick with each other

int count = (int)bricks.size();

for (int i=0; i<count-1; i++)
for (int j=i+1; j<count; j++)
{
	bool collision = bricks[i].CheckCollision(bricks[j]);
	cout << "Q: Does brick " << i << " collide with brick " << j << " ? A: " collision << endl;
}

I would have gone one step further with the function and made it work with one dimension.

bool IsAALineOverlapping(f32 ax1, f32 ax2, f32 bx1, f32 bx2) {
  return (ax1 <= bx2) && (ax2 >= bx1);
}

The reason I would do this is because you can use the same code for both X and Y axes. But then you can also use it for the Z axis should you wish to move to 3D. But also, there are cases where you might want to see if a number range of values overlaps for whatever purpose (enemy AI, stats ranges, etc), and you can just use the 1D version.

None

Advertisement

SeanReid said:
I would have gone one step further with the function and made it work with one dimension.

Adopting your idea, i would eventually design classes like so:

class Range1D {...}; // containing min and max coordinate and the overlap test as proposed

class BoundingRectangle2D
{
	Range1D rangeX, rangeY;
	
	bool Overlaps (BoundingRectangle2D &second);
};

class BreakoutBrick : public BoundingRectangle2D
{
	vec3 color;
	int hitCount;
	// and some more game specific stuff...
};

But for the opening poster it's way to early to discuss such composition ideas.
He first needs to learn about the advantage of using data structures at all.

how do I get better at data structures?

pbivens67 said:
how do I get better at data structures?

Just keep going. Make a data structure for your Brick.

Then make a function taking a (reference or pointer to a) brick as parameter,
rendering it as a box on screen. Just one function, not many copy pasted code duplicates, one for each unique enemy. Loop over all bricks from your vector, calling the single function for each.

Make a grid of some brick filling the screen.

Make a function to animate them eventually, so they move left and right, and down each time they change direction.

Make a another brick for the player, on the bottom of the screen. So it can move left and right as controlled by the player.

To shoot a projectile, create another smaller brick, which moves upwards.

Check if the projectile collides with any of the enemies, and remove it if so.
Again, use a loop like this:

for (size_t i=bricks.size(); i-->0;) // counting backwards, so deleting bricks does not casue indexing issues
{
	if (projectileBrick.CheckCollision(brick))
		bricks.erase(bricks.begin() + i); // delete brick i from the std::vector
}

When all enemies are gone before one hits the ground, you win.

Space Invaders, basically. Which is easier than Breakout because it does not matter where the projectile hits the brick.

You would realize that supporting many enemies does not cause more code than just one enemy. One function handles them all
And at this point you would know what data structures are good for, which would ideally change your way of thinking for programming other, more complex games later.

how do I setup the class for this code

bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
{
    // collision x-axis?
    bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
        two.Position.x + two.Size.x >= one.Position.x;
    // collision y-axis?
    bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
        two.Position.y + two.Size.y >= one.Position.y;
    // collision only if on both axes
    return collisionX && collisionY;
}  

This topic is closed to new replies.

Advertisement