Sign in to follow this  

How best to implement collision detection and other features object 'orientatedly'?

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello all, First time poster so be kind. Hope this is in the right place. I'm a newbish game programmer who's done a bit of programming in the past, mainly at university (with C). However, I'm rather new to object oriented programming having chosen C# and XNA for my game programming needs. I've completed Riemer's 2D tutorial, which is a basic Worms sort of game (stationary user controlled cannons, change trajectory and power of cannon to destroy other cannons). The tutorial did everything in the one final, which does make it hard to expand on the code, so now that I'm done with it, I've made a SpriteManager class, terrain generator class as well as a rocket and cannon class to break it down. Now I'm up to collision detection and I'm not sure how to go about. I've got all the methods required, so I can implement but what's the best/neatest way to do so? One option is creating a class/file with all (4) the collision detection methods in it, however as I'm using a ton of variables from my sprite manager/rocket/cannon/terrain classes the obvious drawback is that any change in my other files will mean I'll have to update my collision detection file. Another approach is to have a method in my terrain and cannon classes that check to see if there's a collision with the rocket (passing in the rocket object), while having any coll dect methods that both classes use either in my sprite manager or separate collision detection file. So, all up I'm not sure which of the above implementations to employ, or a different method altogether? Also, I'd imagine there's be some books/tutorials which tackle these type of issues - can anybody give some examples of such? Sorry for the overly verbose post but any help will be greatly appreciated.

Share this post


Link to post
Share on other sites
I don't quite understand why you say you will need to modify the collision class if you make changes to your sprite class.

Here's the way I would do collision detection in a simple 2D game.

Construt a Collision class with static methods to test for rectangle-rectangle, rectangle-circle and circle-circle collisons. These methods would take in a Rectangle or Circle object that is used to represent the bounds of your game objects - don't pass in the entire game object, just the bounding rect/circle object.

Sorry if I failed to understand your question.

Share this post


Link to post
Share on other sites
Thanks for the reply but the thing as there is collision with the terrain and the terrain is deformable, the game uses per pixel collision detection and not bounding boxes.

For example, one of the methods:


private Vector2 CheckPlayersCollision()
{
Matrix rocketMat = Matrix.CreateTranslation(-42, -240, 0)
* Matrix.CreateRotationZ(rocket.angle)
* Matrix.CreateScale(rocket.scaleFactor)
* Matrix.CreateTranslation(rocket.position.X, rocket.position.Y, 0);

for (int i = 0; i < spriteManager.numberOfPlayers; i++)
{
Sprite player = spriteManager.players[i];
if (player.isAlive)
{
if (i != spriteManager.currentPlayer)
{
int xPos = (int)player.carriagePosition.X;
int yPos = (int)player.carriagePosition.Y;

Matrix carriageMat = Matrix.CreateTranslation(0, -spriteManager.carriageTexture.Height, 0) *
Matrix.CreateScale(player.scaleFactor) * Matrix.CreateTranslation(xPos, yPos, 0);

Vector2 terrainCollisionPoint = TexturesCollide(spriteManager.rocketColorArray, rocketMat,
spriteManager.carriageColorArray, carriageMat);

if (terrainCollisionPoint.X > -1)
{
spriteManager.players[i].isAlive = false;
return terrainCollisionPoint;
}

Matrix cannonMat = Matrix.CreateTranslation(-11, -50, 0) * Matrix.CreateRotationZ(player.angle)
* Matrix.CreateScale(player.scaleFactor) * Matrix.CreateTranslation(xPos + 20, yPos - 10, 0);
Vector2 cannonCollisionPoint = TexturesCollide(spriteManager.cannonColorArray, cannonMat,
spriteManager.rocketColorArray, rocketMat);
if (cannonCollisionPoint.X > -1)
{
spriteManager.players[i].isAlive = false;
return cannonCollisionPoint;
}
}
}
}




Notice all the spriteManager.____, rocket.____ and player.____, which as me worried I'm going about it the wrong way.

[Use source tags. Grrr. -Zahlman]

[Edited by - Zahlman on February 28, 2010 11:38:14 AM]

Share this post


Link to post
Share on other sites
In my limited experience, I've found that there is no easy/clean/appealing way to handle collision detection with lots of different types of entities without either having some giant class that uses RTTI and defines a function for every possible type of collision between two objects (ugly), or requiring every collidable class to be aware of every other collidable class and implementing a collision function for each (double ugly).

You may want to look up whats known as "Double Dispatch", which is I think a typical solution to this kind of collision problem, although when I was looking into it I thought that it looked like even more of a headache than the other two options.

Share this post


Link to post
Share on other sites
You could also expand your CD classes like I do:
In every bounding box, you store a Texture2D [which of course requires you to use your own bounding box class]. In this Texture2D you store a black and white picture of the exact pixels that are solid [=collidable]. Now whenever two or more bounding boxes collide, you check the Texture2Ds for their content, and if the solid pixels of the bounding boxes collide. If they do, raise a collision event or do whatever you want. If not, no collision occured.
Since the [totally awesome!] tutorial by Riemer doesn't use animations, it's fairly simpel for the 2D tutorial. I'm still worried about performance when using per-pixel-CD, but that's not the question here.

Hope I could help :)

Share this post


Link to post
Share on other sites
I'd also be interested in more discussion about this.

In my current project I have an Entity class which is intended to be the base class of every interactive sprite.

Would it be a reasonably good implementation to have something like Entity::collision( Entity& e1, Entity& e2 ), where it's a static function that returns whether or not 2 Entities are colliding?

Ya, I'll probably go with that and see how it works. What does everybody else usually use?

Share this post


Link to post
Share on other sites
Let's try some basic refactoring first.

0) Consistent indentation is always a good thing.

1) Simplify the interface to matrices. Instead of having to create a separate matrix and multiply for each transformation, let's give the matrix functions that do that work: provide the parameters for the transformation matrix, and it creates the other one, does the multiplication and returns the result (without modifying itself). We can then chain those calls together just like with the multiplication, but we aren't constantly writing 'Matrix.Create'. For that matter, adding the word 'Create' seems a little verbose to me. What's wrong with just Matrix.Translation()? The noun form makes it obvious that we're creating something, in the same way that a constructor call does.

2) We can reduce indentation in the loops by reversing some of the logic. Instead of saying "only do the rest of the loop if this is true", we say "skip the rest of the loop if this is false". Then we only have to indent the skipping part instead of the whole "rest of the loop".

3) How about a convenience function to create a translation matrix from a position? (I'm assuming your rocket position is a Vector2 here.)

4) Let's skip the magic numbers - I assume they're to account for the rocket/cannon size (distance from corner to center)? Then they should be part of those classes (maybe static data).

5) Any particular reason to round the carriage position off by casting to int? :/


// I don't know whether you're using C++ or Java or C# or just what, so adapt
// this as necessary.
Matrix Matrix::Zrotated(float angle) {
return this * Matrix.ZRotation(angle);
}

Matrix Matrix::scaled(float factor) {
return this * Matrix.Scaling(factor);
}

Matrix Matrix::translated(float x, float y, float z) {
return this * Matrix.Translation(x, y, z);
}

Matrix Matrix::translated(Vector2 v) {
return this * Matrix.Translation(v);
}

Matrix Matrix::Translation(Vector2 v) { // static
return Matrix::Translation(v.x, v.y, 0);
}

Matrix Matrix::YTranslation(float y) { // static
return Matrix::Translation(0, y, 0);
}

private Vector2 CheckPlayersCollision() {
Matrix rocketMat = Matrix.Translation(Rocket::offset)
.Zrotated(rocket.angle)
.scaled(rocket.scaleFactor)
.translated(rocket.position);

for (int i = 0; i < spriteManager.numberOfPlayers; i++) {
Sprite player = spriteManager.players[i];
if (!player.isAlive) { continue; }
if (i == spriteManager.currentPlayer) { continue; }

Matrix carriageMat = Matrix.YTranslation(-spriteManager.carriageTexture.Height)
.scaled(player.scaleFactor)
.translated(player.carriagePosition);

Vector2 terrainCollisionPoint = TexturesCollide(
spriteManager.rocketColorArray, rocketMat,
spriteManager.carriageColorArray, carriageMat
);

if (terrainCollisionPoint.X > -1) {
spriteManager.players[i].isAlive = false;
return terrainCollisionPoint;
}

// Didn't you mean to rotate/scale the cannon with the cannon's
// angle/factor rather than the player's? Or is the cannon always
// "attached" to the player in a specific way, or just what?
Matrix cannonMat = Matrix.Translation(Cannon::size)
.Zrotated(player.angle)
.scaled(player.scaleFactor)
.translated(player.carriagePosition)
.translated(20, -10, 0);

Vector2 cannonCollisionPoint = TexturesCollide(
spriteManager.cannonColorArray, cannonMat,
spriteManager.rocketColorArray, rocketMat
);

if (cannonCollisionPoint.X > -1) {
spriteManager.players[i].isAlive = false;
return cannonCollisionPoint;
}
}
}



By the way, what if more than one collision happens on the same frame? Returning out of the function might not be the best idea. Perhaps you need to return a vector of collision points?

Quote:

Notice all the spriteManager.____, rocket.____ and player.____, which as me worried I'm going about it the wrong way.


The next logical step is to move the collision-matrix calculation into those objects, so that you can just call rocket.collisionMatrix(). Also, instead of storing "color arrays" in the spriteManager, maybe they should be instance or static data for the actual colliding objects, as well.

Share this post


Link to post
Share on other sites
Thanks for all your help, much appreciated.

Firstly, I'm using C# and XNA and the method names are not my own. Didn't think of creating my own matrix methods to simplify it but I'm confused by your ___.___.___ notation.

Matrix rocketMat = Matrix.Translation(Rocket::offset)
.Zrotated(rocket.angle)
.scaled(rocket.scaleFactor)
.translated(rocket.position);

I can guess what it's doing but I don't think it's allowed in C#.

The way my game works is that there can only be one collision and all the cannons are player controlled (think Worms/Gunbound). A player aims and shoots, then another player aims and shoots and so on.

I cast to int because C# doesn't allow implicit casting when loss of data occurs.

I agree with the magic numbers - the base file wasn't my own but from a tutorial and I haven't gotten around yet to taking them out.

What I did was to make the collision method as a method of each class (terrain and player), with 'general' methods stored separately. Works fine and as I moved my color arrays to the appropriate classes, no longer have to call spritemanager anywhere.

Really like your idea to lessen the indentation in my code. Makes the code much more readable without all the indentation.

Share this post


Link to post
Share on other sites
There's no particularly special notation there. It's just method chaining, and it's perfectly legal in C#. (I did sort of guess after the fact that you were using C#.)

Quote:
I cast to int because C# doesn't allow implicit casting when loss of data occurs.


The point is, the only reason "loss of data occurs" is because you assign to variables of type int. I don't see a reason why you need to assign to ints. The only reason you would be doing that is if you intend to lose the data when you don't have to.

Share this post


Link to post
Share on other sites
Hmmm...now that you mention it I'm not sure why. The screen position has to be an int when drawing to the screen but there's no reason for it to be cast as one when doing computations.

Gonna have to ask Riemer on that one.

Share this post


Link to post
Share on other sites

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

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this