PDA

View Full Version : Collision Handling


Nick
2004.08.16, 09:08 PM
When you are making collisions in your games, do you make each item handle its own collisions with other objects (a class function that handles collisions) or a global collision code that you use for all objects? I currently use a global collision code but I feel as I add more items to the world, it will become less and less useful unless I make class functions to specifically handle the collisions.

Wheatie
2004.08.16, 09:34 PM
While I haven't done a lot of collision handling, the way I have done it in the past is as a class method with a parameter of the other object (preferably defined as virtual in the super class of all objects) so that each object only has to deal with how it responds. The other object can take care of itself. Then, you can have a more "global" function that does the actual checking and sends the message to the specific objects. In the global function, this is where you can quickly eliminate checks that don't need to be made.

This is how I would do it, but I'd be interested in hearing how others would do it.

And if that description didn't make much sense as I'm rather tired right now, I can always try it again for you.

Skorche
2004.08.16, 09:35 PM
Lately I've been rethinking SPK. This was one of the same things I thought of, and heres what I came up with:

1. If you have different ways objects can collide, then you'd need to have a class method to collide the object with every other kind. This leads to redundant code.
2. What arbitrates the collisions? Does the object simply collide itself with all others? That would lead to redundant collisions.
3. Colliding objects both modify each other. In an OOP sense, it should be arbitrated from a higher level. Unless it's colliding with a part of something that the object owns.

Here's what I think I'm going to do:
1. Wrap the collisions up into "collision objects". These collision objects are initialized to collide certain objects or groups of objects of the same type with another group. This way you'd only need to make a collision routine once for each two kinds of objects you want to collide, not once in every class of object you'll have in your world.
2. Create an environment class that owns all of the objects and collision objects in the world, and let it manage their simulation and collision.

Nick
2004.08.16, 10:16 PM
If I used derived classes can the collision checking have the ability to not only take the classes but know which kind it is?

For example:
I have a class CObject.
Then derived classes CItem and CBuilding and CCharacter.

Can I create a function like:
void function(CObject newObject)
{
collision checking....
if type is _____
{}
if type is _____
{}
}

codemattic
2004.08.16, 10:34 PM
typeid() returns a type_info object, so you can:

if (typeid(CMyClass)==typeid(newObject)) {...

Nick
2004.08.16, 10:40 PM
oh cool. So, I would type something like:
CMyClass object1;

if (typeid(object1)==typeid(CMyClass))

Just making sure I know what to put in there.

Nick
2004.08.16, 11:30 PM
Nevermind. At a second look I realize that that is exactly what you posted.

Nick
2004.08.17, 01:40 AM
I'm having some trouble. These are pieces of my class CCharacter. If anybody can see why the code will compile warning and error free yet my character falls through the building. The variable aBuilding is a CBuilding defined as a private variable of the class CCharacter specifically and exclusively for this one line of code.


#include "Building.h"



CBuilding aBuilding;



void CCharacter::CheckCollision(CObject &object)
{
.......



if (typeid(object) == typeid(aBuilding))

Nick
2004.08.17, 03:45 AM
I found a better method using a dynamic_cast function. Collisions are working as perfect as I can code the inequalities.

NCarter
2004.08.17, 05:23 AM
If you can avoid using RTTI features (typeid and dynamic_cast) then do so, because they can have quite a lot of overhead, especially if you call dynamic_cast a lot. The presence of these features in your code usually means you've messed up the design of your class hierarchy.

A better solution is to simply include virtual methods for collision detection near the bottom of your class hierarchy, and have different derived classes override those methods in whatever way is most appropriate to them.

Nick
2004.08.17, 11:16 AM
The problem comes in that the class that is determining the collision must know what it is colliding with (what class) in order to properly respond.

Wheatie
2004.08.17, 04:52 PM
It isn't the greatest solution, but you could always have some sort of member variable that keeps track of the broad type of an object, i.e., a building, another player, etc. that you could check. It would eliminate use of the runtime features and still give you the same functionality, even if it took a little work in the constructors and hierarchy to get it right.

NCarter
2004.08.17, 05:07 PM
It's still better to generalise the collision interface by making base class virtual methods which are applicable for all kinds of objects, and overriding them to provide specific functionality. There's almost always some way of reorganising the design to accommodate this.

How about this: make an overall collision engine which detects possible collisions by looking for intersecting bounding spheres. When it detects a collision between a pair of objects, it calls the same method (void Object::Collision(Object *otherObject)) on both objects to tell each one about the other, and each object simply examines the other one to determine whether they really collided and how they should react to the collision.

Wheatie
2004.08.17, 09:23 PM
When it detects a collision between a pair of objects, it calls the same method (void Object::Collision(Object *otherObject)) on both objects to tell each one about the other, and each object simply examines the other one to determine whether they really collided and how they should react to the collision.

That's basically what I meant in my first suggestion for his collision handling code, but worded much better. :ninja:

Nick
2004.08.18, 02:39 AM
I changed from the dynamic cast code to simply putting a variable in the base class that sets the type using some enumerated constants. That much works rather well. The problem is my actual collision code. I used to only have collisions on the left, right, and top faces of the cubes. Now there are collisions on the front face as well. I'm not sure how to code this. I've tried a few different things but I get very undesireable results. Does anybody have some code for checking the collision between a) two cubes or b) a cube and a quad?

Nick
2004.08.18, 04:55 AM
Here's my attempt at cube to cube collision checking. It doesn't work at all right now. I know that my problem lies in this code because I have a backup of earlier code with everything but the actual checking (the code here) being the same. I'm not sure what the problem is. The code should be fairly easy to understand. The Get_() functions return a coordinate value (X,Y, or Z) or a dimension (Height,Width, and Depth). The variable object is a pointer. W is an integer value and LEFT, RIGHT, BOTTOM, TOP, FRONT, and BACK are all defined constants.

if ( ( GetX() + GetW() >= object->GetX() - object->GetW() ) &&
( GetX() - GetW() <= object->GetX() + object->GetW() ) &&
( GetY() + GetH() >= object->GetY() - object->GetH() ) &&
( GetY() - GetH() <= object->GetY() + object->GetH() ) &&
( GetZ() + GetD() >= object->GetZ() - object->GetD() ) &&
( GetZ() - GetD() <= object->GetZ() + object->GetD() ) )
{
if ( GetX() < object->GetX() ) { w += LEFT; }
if ( GetX() > object->GetX() ) { w += RIGHT; }
if ( GetY() < object->GetY() ) { w += BOTTOM; }
if ( GetY() > object->GetY() ) { w += TOP; }
if ( GetZ() < object->GetZ() ) { w += BACK; }
if ( GetZ() > object->GetZ() ) { w += FRONT; }
}

NCarter
2004.08.18, 08:29 PM
W is an integer value and LEFT, RIGHT, BOTTOM, TOP, FRONT, and BACK are all defined constants.
Er... what is W used for, and what are those directional constants defined as?

Perhaps you could explain what your collision detection is supposed to be doing. I'm having difficulty visualising how it works just from looking at the code. :wacko:

aaronsullivan
2004.08.18, 09:02 PM
Is width (height, depth) the WHOLE width of the cube... or the distance from the center out?

Nick
2004.08.18, 09:23 PM
Sorry about the vague description. W is called later from the individual handling functions to tell them where the collision is taking place so that it can respond properly. For example if it finds w to be defined as LEFT, it can move the character to the left side of the object. The directional constants are defined as RIGHT = 1, LEFT = 5, TOP = 10, BOTTOM = 20, FRONT = 40, BACK = 80. The numbers are doubling so that there will never be a possibility of adding 2 or three of them (if the collision hits the front right, W = FRONT+RIGHT. if the collision hits the front, top, and right, W = FRONT+RIGHT+TOP). You cannot find a combination that adds up to other values.

The dimensions are from the center of the cube, not the whole width of the cube.

The current basic collision response is for types of CBuilding. I purposely set the left and right collisions to also take over all of the other cases because it caused trouble otherwise.

I actually have the collisions working for the top, bottom, left and right. The problem comes from the front and back.

Here's the whole code:
void CCharacter::CollideBuilding(CActiveObject *object, int w)
{
switch (w)
{
case LEFT :;
case (LEFT+BOTTOM) :;
case (LEFT+FRONT) :;
case (LEFT+BACK) :;
case (LEFT+BOTTOM+FRONT) :;
case (LEFT+BOTTOM+BACK) :
{
SetX(object->GetX()-object->GetW()-GetW());
break;
}
case RIGHT :
case (RIGHT+BOTTOM) :;
case (RIGHT+FRONT) :;
case (RIGHT+BACK) :;
case (RIGHT+BOTTOM+FRONT) :;
case (RIGHT+BOTTOM+BACK) :
{
SetX(object->GetX()+object->GetW()+GetW());
break;
}
case TOP :;
case (TOP+LEFT) :;
case (TOP+RIGHT) :
{
SetDescent(0);
SetY(object->GetY()+object->GetH()+GetH());
break;
}
case BOTTOM :;
{
SetY(object->GetY()-object->GetH()-GetH());
break;
}
case FRONT :;
case (FRONT+TOP) :;
case (FRONT+BOTTOM) :
{
SetZ(object->GetZ()+object->GetD()+GetD());
break;
}
case BACK :;
case (BACK+TOP) :;
case (BACK+BOTTOM) :
{
SetZ(object->GetZ()-object->GetD()-GetD());
}
}
}

void CCharacter::CheckCollision(CActiveObject *object)
{

int w = 0;

if ( ( ( GetZ() + GetD() ) >= ( object->GetZ() - object->GetD() ) ) &&
( ( GetZ() - GetD() ) <= ( object->GetZ() + object->GetD() ) ) &&
( ( GetY() - GetH() ) >= ( object->GetY() - object->GetH() ) ) &&
( ( GetY() - GetH() ) <= ( object->GetY() + object->GetH() ) ) )

{
if ( ( ( GetX() + GetW() ) >= ( object->GetX() - object->GetW() ) ) &&
( ( GetX() - GetW() ) <= ( object->GetX() - object->GetW() ) ) ) { w += LEFT; }

if ( ( ( GetX() - GetW() ) <= ( object->GetX() + object->GetW() ) ) &&
( ( GetX() + GetW() ) >= ( object->GetX() + object->GetW() ) ) ) { w += RIGHT; }
}

if ( ( ( GetZ() + GetD() ) >= ( object->GetZ() - object->GetD() ) ) &&
( ( GetZ() - GetD() ) <= ( object->GetZ() + object->GetD() ) ) &&
( ( GetX() + GetW() ) >= ( object->GetX() - object->GetW() ) ) &&
( ( GetX() - GetW() ) <= ( object->GetX() + object->GetW() ) ) )
{
if ( ( ( GetY() - GetH() ) <= ( object->GetY() + object->GetH() ) ) &&
( ( GetY() + GetH() ) >= ( object->GetY() + object->GetH() ) ) ) { w += TOP; }

if ( ( ( GetY() + GetH() ) >= ( object->GetY() - object->GetH() ) ) &&
( ( GetY() - GetH() ) <= ( object->GetY() - object->GetH() ) ) ) { w += BOTTOM; }
}

if ( ( ( GetX() - GetW() ) >= ( object->GetX() - object->GetW() ) ) &&
( ( GetX() + GetW() ) <= ( object->GetX() + object->GetW() ) ) &&
( ( GetY() - GetH() ) >= ( object->GetY() - object->GetH() ) ) &&
( ( GetY() + GetH() ) <= ( object->GetY() + object->GetH() ) ) )
{
if ( ( ( GetZ() - GetD() ) <= ( object->GetZ() + object->GetD() ) ) &&
( ( GetZ() + GetD() ) >= ( object->GetZ() + object->GetD() ) ) ) { w += FRONT; }

if ( ( ( GetZ() + GetD() ) <= ( object->GetZ() - object->GetD() ) ) &&
( ( GetZ() - GetD() ) >= ( object->GetZ() - object->GetD() ) ) ) { w += BACK; }
}

if ( w != 0 )
{
if ( object->GetType() == BUILDING_TYPE ) { CollideBuilding(object,w); }
}
}

I apologize about the spacing. The if() statements all lined up nicely in Xcode making it easier to read.

Hope I cleared things up for my sake so I can get some help. Again, the only sides not working are the front and back. The are the last couple sets of if() statements.