Sign in to follow this  
os3330

A few problems concerning collision and frame rate.

Recommended Posts

I'll list them in order of importance. First Problem: I have a life and a health variable in my game. Every time I check a collision, I store the enemy's damage value (if there is a collision). Then through a different function, I subtract the health from the damage amount and check if it's needs to be deleted. If not, it moves, then the animation is performed. Finally, the object shows up on the screen. Trouble is, the program virtually ignores each variable and everybody is killed in one blow (with the exception of the Player, which I took the collision out). Second Problem: The background picture does not want to render. If my computer is running slow, once the program starts, it shows for a moment and then disappears. I have no idea if it lies in the rendering order (in Environment class) or something else. Third Problem: For a game to run at 60 FPS, moving one pixel per key event is considered slow, right? I'm not really familiar with frame rate capping. For one, is it better to set a frame rate for the whole shell (game, score counter, logo, etc.) or do it individually within each class that outputs to the screen? Fourth Problem: This may tie in close connection with the others. I reworked my code so it does a set amount of things before showing (collision, setting state, moving, and animation). In the set state and moving functions (the Bullet is a special exception, its state is determined at collision), a flag may be set to delete the object from the screen. For example, if a Bullet and Enemy collide, the Bullet disappears instantly while it takes nearly a second for the Enemy to vanish. For only three objects (Player, Bullet, Enemy), this is pretty unacceptable. Here's the project folder (external libraries included, amounts to 6.5 MB compressed): http://rapidshare.com/files/101147941/p_sunrise_6.rar.html Thanks.

Share this post


Link to post
Share on other sites
Downloading a RAR file with everything is a bit annoying for people who want to help you btw - preferable to post the relevant source in some tags here.

I didn't really have much time to look at it, and my C++ isn't the best, but it does look like your collision detection routines need some simplifying.

At the moment you have:

Environment:
for each Object A:
for each Object B:
checkCollisionAndSetStatus..

But your routines are in different places - your environment calls on your objects which each get the other objects from environment and asks the environment to check the collisions between each of them.

This is because you want to handle collision detection differently depending on the object, but you really don't have many cases to handle and you could simplify things a lot by handling them in one place, or by using a different design pattern.

Note that in your current algorithm, you run collision-detection code twice for every collision (once from each colliding object). But it's only the results of the collision that differ between each object - you should be asking each colliding object to handle a single known collision (worked out elsewhere). Also if an enemy was hit by two bullets in a single frame, they will only be affected by one here.

Your bullets only do 1 damage, so presumably enemies are dying in 1 hit because you don't clear enemyDamage each frame. Alternatively bullets that don't get removed might just collide each frame since your collision code doesn't explicitly check for the removal state.

For the Fourth Problem, maybe slow down your framerate to get a quick review of why your enemies aren't getting removed quickly. You should get some logging code in there btw, so you can check the time between when the enemy was set to be removed and its actual removal. I suspect that it is taking a while because its health has to go down to zero first.

Share this post


Link to post
Share on other sites
Quote:
Original post by Argus2
Downloading a RAR file with everything is a bit annoying for people who want to help you btw - preferable to post the relevant source in some tags here.

I didn't really have much time to look at it, and my C++ isn't the best, but it does look like your collision detection routines need some simplifying.

At the moment you have:

Environment:
for each Object A:
for each Object B:
checkCollisionAndSetStatus..

But your routines are in different places - your environment calls on your objects which each get the other objects from environment and asks the environment to check the collisions between each of them.

This is because you want to handle collision detection differently depending on the object, but you really don't have many cases to handle and you could simplify things a lot by handling them in one place, or by using a different design pattern.

Note that in your current algorithm, you run collision-detection code twice for every collision (once from each colliding object). But it's only the results of the collision that differ between each object - you should be asking each colliding object to handle a single known collision (worked out elsewhere). Also if an enemy was hit by two bullets in a single frame, they will only be affected by one here.

Your bullets only do 1 damage, so presumably enemies are dying in 1 hit because you don't clear enemyDamage each frame. Alternatively bullets that don't get removed might just collide each frame since your collision code doesn't explicitly check for the removal state.

For the Fourth Problem, maybe slow down your framerate to get a quick review of why your enemies aren't getting removed quickly. You should get some logging code in there btw, so you can check the time between when the enemy was set to be removed and its actual removal. I suspect that it is taking a while because its health has to go down to zero first.
The collision is handled at the character level because I'm planning to evolve it as the game development progresses (for bosses and such).

Zeroing the enemyDamage variable (do'h!) did the trick. The enemy disappears when the health reaches zero in the appropriate amount of hits. Moving the state checking code into the rendering function removed the delay of destroying objects.

However, I'm still having a problem with the frame rate. Still, at 60 FPS, the game runs like its on 30 FPS or lower. I have a Timer that starts when the main loop executes and restarts when it gets back up there. Here's the actual code that does the capping (placed right before I update the screen).

// Cap the frame rate.
if ( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ) {
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
Ideally, where should I cap the frame rate in the main loop to get the results I want? Or should I cap the frame rate in parts, like the Environment gets its own frame rate, the window gets its own, etc.?

Share this post


Link to post
Share on other sites
The collision function is being called twice but on same frame, I believe. Correct me if I'm wrong on that.

How can I move the handle_collision() function into one place (the Environment class in this case)? What I'm thinking is that I move the handle_collision() into the Environment class. In the set_status() function of each class, there it checks for collision. But, that reverts back to the first problem, the collision function calling twice on the same colliding objects.

Can someone give me a few nudges in the right direction concerning this?

Share this post


Link to post
Share on other sites
Quote:
How can I move the handle_collision() function into one place (the Environment class in this case)? What I'm thinking is that I move the handle_collision() into the Environment class. In the set_status() function of each class, there it checks for collision. But, that reverts back to the first problem, the collision function calling twice on the same colliding objects.

If you wanted to handle collisions all in one class, then you can just do your comparisons there like (pseudocode):

for each object A:
for each unchecked_object B:
if(A == B) continue
if(check_collision(A, B))
A.collided_with(B)
B.collided_with(A)
remove A from unchecked_objects

You can keep the same check_collision method, and you just have a virtual collided_with method on your sprites to handle the effects of a known collision. i.e. bullets don't do anything if it's another bullet, and characters subtract damage.

For a small-scale game, that collision detection should be sufficient, but larger scale lots of people use additional methods to reduce the number of required tests, and also to handle the situation where an object is travelling so fast that it moves through another object in a single frame.

Stuff like FPS timing I know very little about - I suggest you have a look around gamedev's resources/articles sections, or do a search on the web for the best way to handle this. It may be that your drawing routines end up too slowly to run it at the required rate. Or the timer resolution on your OS may not be enough to run it at the FPS you're requesting.

Share this post


Link to post
Share on other sites
regarding collision check redundancy:


foreach object A
foreach object B

if &A>=&B break
else
checkcollision(A,B) , etc



saves you the trouble of having to track each object as being checked or not-checked this frame

Share this post


Link to post
Share on other sites
Using for-each, is the object a particular instance (Enemy, Bullet) or it can be any object? Because all my objects follow a hierarchy; they all inherit from the Sprite class and there is no instanceof equivalent in C++ (that actually facilitates good design).

And assuming that Object A can be any type of object, let's say there is no collision with A. So is it safe to go on to B, C, etc. and the object checking below will change as well?

Share this post


Link to post
Share on other sites
I wrote it, but it's pretty damn messy.
// Handles any collision within the environment.
void Environment::handle_collision()
{
// Gets of copy of the objects to check for collision.
std::vector<Sprite*> copy = get_objects();

// Sets up two iterators to check for objects one at a time.
int indexOne = 0;
std::vector<Sprite*>::iterator iter = copy.begin();
std::vector<Sprite*>::iterator iterUnchecked = copy.begin();

while ( iter != copy.end() ) {
// Going through the array a second time.
int indexTwo = 0;

while ( iterUnchecked != copy.end() ) {
// If they are the same checking object, skip it.
if ( copy.at( indexTwo ) == copy.at( indexOne ) ) {
continue;
}

// Don't have bullets kill off each other.
else if ( copy.at( indexTwo )->get_health() == -1 && copy.at( indexOne )->get_health() == -1 ) {
continue;
}

// Actually checks for collision.
else if ( check_collision( copy.at( indexTwo ), copy.at( indexOne ) ) == true ) {
copy.at( indexTwo )->set_status();
copy.at( indexOne )->set_status();
}

// Removes the object from the vector.
copy.erase( copy.begin() + indexOne );

// Iterates to the next unchecked object.
++iterUnchecked;
}

// Iterates to the next object.
++iter;
++indexOne;
indexTwo = 0;
}
}
Is it possible to clean that up a little? Perhaps more importantly, will it perform a collision check for every colliding object once?

Share this post


Link to post
Share on other sites
you should not have fixed frame rates or frame rate capping. Base your movements off time between frames and don't assume that it is fixed.

Share this post


Link to post
Share on other sites
I know it's been awhile since I posted in this thread, but I still have this problem (I put it off to handle more serious issues).

I transferred all my collision handling to the Environment class. Currently, I have two functions that handle collsion (finding out which one is better):

// Handles any collision within the environment.
void Environment::handle_collision()
{
// Iterates through both objects, checking each for collision.
std::vector<Sprite*>::const_iterator allyIter = allyObjects.begin();

while ( allyIter != allyObjects.end() ) {
std::vector<Sprite*>::const_iterator enemyIter = enemyObjects.begin();

while ( enemyIter != enemyObjects.end() ) {
if ( check_collision( *allyIter, *enemyIter ) ) {
// Sets the new status for the collided objects.
( *allyIter )->set_status( ( *enemyIter )->damage_value() );
( *enemyIter )->set_status( ( *allyIter )->damage_value() );
}

// Iterates to the next enemy object.
++enemyIter;
}

// Iterates to the next allied object.
++allyIter;
}
}

// Handles collision for a object.
void Environment::handle_collision( Sprite* obj )
{
// Finds out what vector the object's in.
if ( indexOf( obj, allyObjects ) == -1 && indexOf( obj, enemyObjects ) == -1 ) {
// Not in either container.
return;
}

else if ( indexOf( obj, allyObjects ) != -1 ) {
// In ally container.
std::vector<Sprite*>::const_iterator enemyIter = enemyObjects.begin();

while ( enemyIter != enemyObjects.end() ) {
if ( check_collision( obj, *enemyIter ) ) {
// Sets the new status of the object.
obj->set_status( ( *enemyIter )->damage_value() );
}

// Iterates to the next object.
++enemyIter;
}
}

else if ( indexOf( obj, enemyObjects ) != -1 ) {
// In enemy container.
std::vector<Sprite*>::const_iterator allyIter = allyObjects.begin();

while ( allyIter != allyObjects.end() ) {
if ( check_collision( obj, *allyIter ) ) {
// Sets the new status of the object.
obj->set_status( ( *allyIter )->damage_value() );
}

// Iterates to the next object.
++allyIter;
}
}
}

However, both segments of code check for collision twice (I thought the second collision handling method would solve it but it didn't). I'm stumped on figuring out a solution to this problem. Can anyone help?

Share this post


Link to post
Share on other sites
It checks collision twice because it first checks collisions on A against all B, and then collisions on B against all A, right? Maybe set a flag on your objects that specifies whether they've been checked already, and skip them as needed?

EDIT: No... That's not it... I misread your code.

Share this post


Link to post
Share on other sites

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