Collision testing and response, for all active objects, in one function.

This topic is 3005 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

So, I want my Update function in my Physics class to be pretty powerful, perhaps I'm asking too much, but I want it to loop through a box of objects that are currently in play (for instance, a pong ball and two paddles to start). I want it to perform collision checks between all relevant objects, keeping in mind ultimately I plan on having more than just a pong ball and two paddles. It seems overkill for pong, yes this I know, but I'm looking at the future. Here is what I have so far: It's a rough start, a lot of holes, and very incomplete.
void Physics::Update(std::vector<Renderable_Object*> & Game_Objects)
{
for (int i = 0; i++ i < Game_Objects.end())
{
for (int j = 0; j++; j < Game_Objects.end())
{
if (i == j){ j++; }
if (Game_Objects->AABB)
{
if (Game_Objects[j]->AABB)
{
if(collision2d(Game_Objects->getMinimum(), Game_Objects->getMaximum(),
Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()))
{
reverse_x_velocity(Game_Objects->getVelocity());
}
}
}
}
}
}

Here is my renderable object abstraction: Also pretty basic, probably not pretty, but functional at this point.
class Renderable_Object
{
public:

Renderable_Object(float minX, float minY, float maxX, float maxY, float posX, float posY, bool visible, bool physical)
{
d_min.x = minX;
d_min.y = minY;
d_max.x = maxX;
d_max.y = maxY;
position.x = posX;
position.y = posY;
isVisible = visible;
isPhysical = physical;
velocity.x = 0;
velocity.y = 0;
}
Vector2 & getVelocity()
{
return velocity;
}
void setVelocity(float x, float y)
{
velocity.x = x; velocity.y = y;
}
Vector2 & getLocation()
{
return position;
}
Vector2 & getMinimum()
{
return d_min;
}
Vector2 & getMaximum()
{
return d_max;
}

private:
Vector2 position;
Vector2 velocity;
Vector2 d_min;
Vector2 d_max;
bool isPhysical; // does this object use collision at all?
bool isVisible; // render this object?
bool AABB; //compatible with AABB collision detection
};

Does this look like a right step in the direction I'm trying to go? Any tips or hints?

Share on other sites
As I start to enter in all the variances and possibilities this function is getting more and more complex:

void Physics::Update(std::vector<Renderable_Object*> & Game_Objects){			for (int i = 0; i++ i < Game_Objects.end())	{		if (Game_Objects->isPhysical)		{			for (int j = 0; j++; j < Game_Objects.end())			{				if (i == j)				{					j++; 				}				else if (Game_Objects->AABB)				{					if (Game_Objects[j]->isPhysical)					{						if (Game_Objects[j]->AABB)						{							if(collision2d(Game_Objects->getMinimum(), Game_Objects->getMaximum(), 										Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()))							{									//do stuff							}						}					}					else					{						j++;					}				}			}		}		else		{			i++;		}	}}

Is this a fruitless attempt?

Share on other sites
Hmmm, I think this is the most I can write before field testing this thing.

I would love if anyone with half an idea of what I'm trying to do could point out any obvious logical errors that I'm missing!
void Physics::Update(std::vector<Renderable_Object*> & Game_Objects){			for (int i = 0; i++ i < Game_Objects.end())	{		if (Game_Objects->isPhysical)		{			for (int j = 0; j++; j < Game_Objects.end())			{				if (i == j)				{					j++; 				}				else if (Game_Objects->AABB)				{					if (Game_Objects[j]->isPhysical)					{						if (Game_Objects[j]->AABB)						{							if(collision2d(Game_Objects->getMinimum(), Game_Objects->getMaximum(), 										Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()))							{									//do stuff							}							else							{								j++;							}						}						else						{							if(planeIntersection2d(Game_Objects[j], Game_Objects->getLocation()))							{								//do other stuff							}							else							{								j++;							}						}					}					else					{						j++;					}				}			}		}		else		{			i++;		}	}}

Share on other sites
tbh that looks a bit of a mess for a collision loop.

void Physics::Update(std::vector<Renderable_Object*> & Game_Objects){			for (int i = 0; i < Game_Objects.size(); i++)	{		if (!Game_Objects->AABB) continue;		if (!Game_Objects->isPhysical) continue;			for (int j = i+1; j < Game_Objects.size(); j++)		{			if (!Game_Objects[j]->AABB) continue;			if (!Game_Objects[j]->isPhysical) continue;						if(collision2d(	Game_Objects->getMinimum(), Game_Objects->getMaximum(), 							Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()))			{					//do stuff			}			else if(planeIntersection2d(Game_Objects[j], Game_Objects->getLocation()))			{				//do other stuff			}		}	}}

Share on other sites
I would say that at first, make a specific collision for your specific problem.
You have plenty of time to make general solutions.
And as the number of objects grow, and so as your experience, you surely will come up with better methods.

For example you will have to implement optimizations (for example quad trees).

and for the code:
Why don't you use fewer if statement with more conditions?
if( i != j && Game_Objects->AABB && Game_Objects[j]->AABB &&    collision2d(Game_Objects->getMinimum(), Game_Objects->getMaximum(),                Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()) ){...}

Share on other sites
Quote:
 Original post by oliiitbh that looks a bit of a mess for a collision loop.How about this?*** Source Snippet Removed ***

I like what you've done, but what about when i == 2, and I want to check it against j 0, and 1?

You start the j loop off by making it i+1, this leaves out anything before i if i = anything but 0, or am I wrong?

Share on other sites
Quote:
Original post by JWColeman
Quote:
 Original post by oliiitbh that looks a bit of a mess for a collision loop.How about this?*** Source Snippet Removed ***

I like what you've done, but what about when i == 2, and I want to check it against j 0, and 1?

You start the j loop off by making it i+1, this leaves out anything before i if i = anything but 0, or am I wrong?
Because you already checked 0-2, so if you would check 2-0, that would be double check, so it would be wrong/nothing happen/objects explode/program crash.

Share on other sites
Oh, I know why I did that, because of my planeIntersection2d function.

It's funny and only wants to accept a plane as it's first argument, and the second argument can be any physical object with a position, probably an AABB object.

I can probably circumvent this somehow though.

Currently the only object that uses collision of any kind that isn't AABB is a plane, but I can test an AABB object against a plane.

Here's the code for that function:
bool Physics::planeIntersection2d(Plane & boundingPlane, Vector2 & objectPosition){	Ray line_between_objects(objectPosition,boundingPlane.position);	double DotProduct=line_between_objects.direction.dot(boundingPlane.normal);	double l2;	//determine if ray parallel to plane	if ((DotProduct<0)&&(DotProduct>-0)) 		return false;	l2=(boundingPlane.normal.dot(boundingPlane.position-objectPosition))/DotProduct;	if (l2<-0) 		return false;	return true;}

I suppose a plane is a renderable object, considering I will be drawing the borders of my pong game as planes?

The trick is making sure that the plane, whichever index it may fall under in Game_Objects, gets passed as the first argument to the function.

Share on other sites
something like this?

void Physics::Update(std::vector<Renderable_Object*> & Game_Objects){			for (int i = 0; i < Game_Objects.size(); i++)	{		if (!Game_Objects->isPhysical) continue;			for (int j = i+1; j < Game_Objects.size(); j++)		{			if (!Game_Objects[j]->isPhysical) continue;						if(	Game_Objects->AABB && 				Game_Objects[j]->AABB && 				collision2d(	Game_Objects->getMinimum(), Game_Objects->getMaximum(), 								Game_Objects[j]->getMinimum(), Game_Objects[j]->getMaximum()))			{				//do stuff			}			else if(Game_Objects->Plane && 					Game_Objects[j]->AABB && 					planeIntersection2d(Game_Objects, Game_Objects[j]->getLocation())			{				// do stuff			}			else if(Game_Objects[j]->Plane && 					Game_Objects->AABB && 					planeIntersection2d(Game_Objects[j], Game_Objects->getLocation())			{				//do other stuff			}		}	}}

Share on other sites
ultimately, if you have many types of primitives (plane, box, obb, spheres) that needs to be tested, consider a collision table.

Share on other sites
AH YES! The obvious answer is sometimes the hardest to pick out when you've been staring at your code for hours!

I'll simply add another boolean value to the renderable_object abstraction to determine whether or not the object I'm working with is a plane! That works perfectly!

Share on other sites
oliii, when will you collect all your collision threads-replies and write an article about it? I think it would be very valuable. (if you've already done this, then SORRY!)

Share on other sites
I have one more question, it's regarding the collision response.

Right now I'm using this function in a pong game, how do I make sure I apply the right collision response to the pong ball, and make sure I only apply that specific collision response to the pong ball, or any specific response to any object for that matter?

I have a seemingly complex idea in mind.

Instead of housing the game_objects in a vector, use a map, connected with some kind of other variable, and then I can just update the map, so that some other class, such as logic or whatnot, can determine what to do with the collision event? Or is there some kind of simpler method?

Share on other sites
Quote:
 Original post by szecsoliii, when will you collect all your collision threads-replies and write an article about it? I think it would be very valuable. (if you've already done this, then SORRY!)

About the collision response, that can be a bit tricky. You can have a callback that passes the collision information to each objects (that's usually what is done for sound effects and graphical effects, and game logic like 'destroy ball on impact'), or like the collision table, have a collision response table that take the collision information and produce the appropriate response.

Or use a generic solution (pure rigid body dynamics, consider your paddle and walls with infinite mass, and the sphere with a finite mass, that way all the energy from the collision response will be applied to the ball), with coefficients of friction and restitution.

Unless you write a physics engine or something a bit complex, you shouldn't need this. Just use dirty logic (switch / case statements, ect), could be simple enough.

[Edited by - oliii on December 2, 2009 9:16:05 AM]

Share on other sites
Hmmm, I'm considering that callback method. This would take some oop magic as far as my renderable_object goes.

A virtual collision response function, something like that anyways.

Basically the update function in physics will tell an object if it collides, then that object will know it has collided with something, and it can tell physics how to deal with that collision, sounds like I'm on the right track.

Share on other sites
I think this is the direction I want to go in:
void collisionResponse(int responseType);

In physics I can enumerate possible collision responses (would this be a collision response table?), the object could hold the information on what kind of collision response it would have, and if a collision is true I run this function for the two colliding objects.

Share on other sites
Can do. If you want the ball to stick to the paddle, or just deflect. What if your two objects have different collision response type? Do you run both responses one after the other?

Share on other sites
Well, I haven't written the implementation yet, but I will make sure I make that possible.

It's going to take two ints. And then the collisionResponse function will switch based on the enumeration I just fleshed out:

	enum responseType{Bounce, Do_Nothing, Return_To_Previous_Location}

So say the ball and the paddle collide, collisionResponse will get passed a 0 from the ball and a 1 from the paddle.

In another case, say the paddle intersects the top bounding plane, the paddle will pass a 2, and return to the location it was at before it intersected the plane, and the plane will pass a 1 of course, and not move.

In another case, the ball will strike one of the bounding planes, in which case the ball will of course Bounce, and pass a 0 to the function, and the plane will do what it normally does, nothing.

Okay that sounds great in theory, the trouble is when I pass this information, this 0, 1, or 2, where it's coming from. I can't make the paddle always respond with a 1, or a 2. It's dependent on the other object in collision. Hmmm, let me think on this, unless you have a quick answer to that issue.

Ideally I'd like to accomplish this without writing something special for each kind of renderable object, so maybe this isn't the way to go?

Share on other sites
Hmmmm...
enum responseType{Bounce, Do_Nothing, Return_To_Previous_Location}

0+1=1

As in bounce + do nothing = do nothing.

1+1=2

As in do nothing + do nothing = return to previous location.

The paddle is the one that would default as a 1, but in the case it is paired up with another 1, it's response would be 2?

Is this a viable strategy?

HMMMM, actually this makes perfect sense to me.

Two objects with no response to a collision would naturally just kinda stand there, I'm thinking of running into walls in a 3d shooter or any other game where you run into something unmoveable, you just stop, you don't go through the wall, and the only response that I have defined here that coincides with that is return to previous location before the collision occurred.

Share on other sites
Here is a pseudo-code version of what I'm working on, it's pretty simple actually, only one conditional.

void Physics::collisionResponse(int response1, int response2){	if (response1 == 1 && response2 == 1)	{		// change response to #2 for object that isn't mobile (return to previous location)		// respond normally for object that is immobile (do nothing)	}	else	{		// perform default responses	}}

Share on other sites
I think this about covers it...

Any tips on doing this a way better way or optimizing what I have? Making it neater?

void Physics::collisionResponse(Renderable_Object * collidedObjectA , Renderable_Object * collidedObjectB){	if (collidedObjectA->defaultCollisionResponse == 1 && collidedObjectB->defaultCollisionResponse == 1)	{		if (collidedObjectA->Plane)		{			collidedObjectB->getLocation().x = collidedObjectA->getLocation().x;		}		if (collidedObjectB->Plane)		{			collidedObjectA->getLocation().x = collidedObjectB->getLocation().x;		}	}	if (collidedObjectA->defaultCollisionResponse == 0)	{		if (collidedObjectA->getVelocity().x != 0)		{			reverse_x_velocity(collidedObjectA->getVelocity());		}		if (collidedObjectA->getVelocity().y != 0)		{			reverse_y_velocity(collidedObjectA->getVelocity());		}	}	else if (collidedObjectB->defaultCollisionResponse == 0)	{		if (collidedObjectB->getVelocity().x != 0)		{			reverse_x_velocity(collidedObjectB->getVelocity());		}		if (collidedObjectB->getVelocity().y != 0)		{			reverse_y_velocity(collidedObjectB->getVelocity());		}	}}