Advertisement

AABB collision

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

I have worked on my code my only problem is that the “one” variable must be of class type.

#include <iostream>
#include <vector>

using namespace std;

class GameObject
{
public:
	float x;
	float y;
	float Position;
	float Size;
	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;
	}
};




int main()
{
	return 0;
}

@pbivens67 The CheckCollision function is a member function of the class, so you can omit the “one” parameter and use the class's variables.

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

class GameObject
{
public:
  float x;
  float y;
  float w;
  float h;
  bool CheckCollision(const GameObject& other) // AABB - AABB collision
  {
    if(IsAALineOverlapping(x, x + w, other.x, other.x + other.w))
      return IsAALineOverlapping(y, y + h, other.y, other.y + other.h);
    
    return false;
  }
};

None

Advertisement

this looks like it should work

#include <iostream>
#include <vector>

using namespace std;

class one
{
public:
	float one;
};

class GameObject
{
public:
	float x;
	float y;
	float Position;
	float Size;
	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;
	}
};




int main()
{
	return 0;
}

pbivens67 said:
class GameObject { public: float x; float y; float Position; float Size;

What is a GameObject?
Something supposed to be anything?
Why does it have x and y, but also a one dimensional position? And a one dimensional size?

Why not just x, y, width and height, as you were using all the time before?
That's 2D position and 2D size then, which is what you need.

But as said before, the ideal data for a (axis aligned) rectangle is to define two corners:
topLeftX, topLeftY, and bottomRightX, bottomRightY.
It's better because then you don't need additions for an overlap test.
And instead those clumsy names we should just call it:
minX, minY, maxX, maxY.
That's shorter to type and is clear about math and meaning. It does not need a convention about left = decreasing coordinate and right = increasing coordinate, or vice versa, adding the same confusion about top and bottom.

So if you are willing to change your habits about rectangles, use min/max corners.
But not that nonsense from the quote.

pbivens67 said:
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision

This would make sense for a global function, as said some posts before.

But currently you have it as a member function, so you can use the current instance for the ‘first’ box, and you need to give only the ‘second’ box as parameter.

Your function works technically, but since you don't use the current instance of the GameObject, it's just bad code.
To fix it, we have several options:

class GameObject
{
public:
	vec2 Position;
	vec2 Size;
	bool CheckCollision(GameObject &two) // AABB - AABB collision
	{
		bool collisionX = Position.x + Size.x >= two.Position.x &&
			two.Position.x + two.Size.x >= Position.x;
		bool collisionY = Position.y + Size.y >= two.Position.y &&
			two.Position.y + two.Size.y >= Position.y;
		return collisionX && collisionY;
	}
};

I have removed x and y, and made pos and size 2D vectors.
But you don't have a 2D vector class. So i wonder why you seek for related code examples, pasting them in, although we gave examples avoiding a need for a 2D vector class.

Using such vec2 class would be a good idea, as said before, but i assume you do not yet and just want to make some Breakout Bricks or Space Invaders enemies first, which is fine.
But that's another topic. Let's focus on the ‘current instance’ and ‘global function vs. member function’ confusion for now.

Maybe it helps if we use the ‘this pointer', which is a C++ language feature:

class GameObject
{
public:
	vec2 Position;
	vec2 Size;
	bool CheckCollision(GameObject &two) // AABB - AABB collision
	{
		bool collisionX = this->Position.x + this->Size.x >= two.Position.x &&
			two.Position.x + two.Size.x >= this->Position.x;
		bool collisionY = this->Position.y + this->Size.y >= two.Position.y &&
			two.Position.y + two.Size.y >= this->Position.y;
		return collisionX && collisionY;
	}
};

‘this’ always gives the pointer to the current class instance in member methods.
So instead using ‘one’, i have now replaced that with 'this', and we access the data of the current object.
The result is the exact same as with the code snippet above. We do not need to write ‘this→’ each time to access member variables. But we can, if we like.
Though, the real reason why 'this' exists is something else:

class foo
{
	float value;
	
	
void SetValue (float value)
	{
		this->value = value;
	}

};

In this example the parameter of the member function has the same name as the m,ember variable.
Here we must use ‘this→’ to tell the compiler we mean the member variable, not the given function parameter.

Nothing of this is important, but maybe it helps.

Another option to fix your function would be this, in case you really want to give both boxes as parameter, but you don't want it to be a global function out of the class:

class GameObject
{
public:
	float Position;
	float Size;
	static 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;
	}
};

I made it a static function.
This way the function has no access to member variables, and does not refer to any instance of some GameObject at all.
We can call it like that:

GameObject enemy, projectile;
bool overlap = GameObject::CheckCollision(enemy, projectile);

It's the same as a global function, just we have it inside some class, eventually to keep things organized.

Finally, also the example of a global function again, just to be complete:

class GameObject
{
public:
	float Position;
	float Size;
};
	
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;
};

To use it, we no longer need to add ‘GameObject::’ to access the function, since it's now global:

GameObject enemy, projectile;
bool overlap = CheckCollision(enemy, projectile);

Do you understand all this?

No?

Fine. Because it does not matter at all for now. Nobody cares.
Just make something work using some rectangles!
Try to do something with those structs or classes. And if it does not work, post code and we'll help.
We gave examples about collision tests and box classes weeks ago already in another thread.
Now the same here, again.
Stick at something instead constantly starting from scratch!

I am still getting the “one” error.

#include <iostream>
#include <vector>

using namespace std;

class GameObject
{
public:
	float Position;
	float Size;
};

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




int main()
{
	GameObject enemy, projectile;
	bool overlap = CheckCollision(enemy, projectile);
	cout << overlap << endl;

	return 0;
}

pbivens67 said:
how do I setup the class for this code

To use this code, you would need a 2D vector class/struct for Position and Size.
I gave an example for this in your older thread which i can't find.
But i do not recommend it anyway at this point. More new things, more confusion.

There is an example of a Rect struct here in this thread, page 2.
It does not need vec2 and has collision test. So that's all you need. Use that.
You posted code which would work yourself, the problem then was just to use it, e.g. with many Rects in a std::vector.
But it should work otherwise. So why do you start from scratch, doing the same thing in a ‘GameObject’, requiring a 2D vector class which you do not yet have?

Begin on page one, stick with the Rect, copy paste code, compile it, fix error messages, compile it again, use it.

So you don't use 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;
} 

You use this, from page 2:

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

// you forgot to copy the intersection function:
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[])
{

	//Rect r[3 * 6];
	// you want to use std::vector instead
	std::vector<Rect> r(3 * 6);

	for (int y = 0; y < 6; y++)
	{
		for (int x = 0; x < 3; x++)
		{
			int index = y * 3 + x;
			r[index].x = x * 40 + 260;
			r[index].y = y * 80;
			r[index].w = 40;
			r[index].h = 80;
		}
	}

	return 0;
}

On page 4 i gave an example using a member function instead a global function for the test:

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

	}
};

Pick what you prefer. Ofc. ‘Intersection, Overlap, or Collision’ always means the same thing. Rename as you like.

'Rect, Brick, Axis Aligned Bounding Box' always means the same thing as well. Pick what you prefer, but then stick at it.

Here was another snippet making a grid of overlapping bricks testing their collisions, page 4:

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

The older thread had similar, working examples.

So we turn in circles. And we can not really repeat this anymore. All has been said multiple times.

But instead taking the code and using it,
You write new code from scratch, asking us about how it looks, without even compiling it so you would see obvious bugs yourself. < :(

That's not how it works.
Programming is:

Write some code implementing a solution for the current problem.
COMPILE THE CODE.
Fix error messages.
RUN IT.
Debug it.
Repeat until it works.

Advertisement

do I need to add anything to my code?

#include <iostream>
#include <vector>

using namespace std;

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

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

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

// you forgot to copy the intersection function:
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[])
{

	vector<Rect> r(3 * 6);

	for (int y = 0; y < 6; y++)
	{
		for (int x = 0; x < 3; x++)
		{
			int index = y * 3 + x;
			r[index].x = x * 40 + 260;
			r[index].y = y * 80;
			r[index].w = 40;
			r[index].h = 80;
		}
	}

	return 0;
}

Oh man, you drive me crazy…

There should be no class Rect1 or class Rect2. Just class Rect.

The intersection function should be:

bool intersectRect(const Rect& rect1, const Rect& rect2)

Use one single Rect class, but create as many instances as you need:

int main ()
{
	Rect rect1;
	Rect rect2:
	
	Rect rects[10];
	
	std::vector<evenMoreRects> (100);
	
	intersectRect (rect1, rects[5]);
	intersectRect (evenMoreRects[90], rects[5]);
		
};

All this works with just one Rect class. You only need to write it once.
That's the realization we try to arrive at…

pbivens67 said:
do I need to add anything to my code?

You need to setup test data for the intersection function, to see if it works.

You had a project where you have rendered multiple rectangles.
I would copy / include the current Rect code to this project, so you can do this better than just with numbers:
Move a player Rect over a grid of static brick Rects, and color those which intersect the player differently.
That's a good test, and it extends to a game after that.

I had to adjust the intersectRect function.

This topic is closed to new replies.

Advertisement