Help with 2D particle/projectile collision detection and framerate issues.

Started by
15 comments, last by between3and26characters 6 years, 5 months ago

I'm currently working on remaking a game I made a few years ago, and it didn't occur to me until recently that the new code is very framerate dependent, in that the speeds of various game entities vary depending on what framerate you're getting. I'm making the new version of the game using Slick2D.

I modified the calculations that "move" the projectile to take deltaTime into account, and this has got me some better results with various frame rates, but now I'm running into another issue. Specifically, the accuracy of my projectile collision detection varies depending on the frame rate. The faster the frame rate, the more accurate it is. I can understand why this is, as with a slower frame rate, the projectiles will be moving further along their path, meaning they could skip right past the enemy collision radius; so I'm trying to think of how I could modify the existing system to give me better results that would be consistent regardless of frame rate.

I've thought about using raycasting to decide whether the projectile hits. I was about to start writing the code so that the raycasting would be done when the weapon fires, so it would determine then if there is a collision, and then just fire the projectile really fast so it gets there before the enemy can move away from it.

I realize that this is not a good approach, because this means the enemy will take damage before the projectile actually arrives, which would result in some.... interesting visuals. So would it be feasible to have the projectile simply raycast along its own trajectory and if it's within the enemy's collision radius, then it hits?

Currently, the collision detection is:

Quote

projectile moves along its path
calculate distance between projectile and enemy
if distance is less than or equal to enemy collision distance
    particle hits, enemy takes damage, particle disappears

But as mentioned before, the varying frame rate can cause particles to completely miss just because of lag. So my new idea is...

Quote

particle moves along its trajectory
transform particle position along trajectory
if this position is less than or equal to enemy collision distance from enemy
    particle hits, enemy takes damage, etc

I feel like I'm confusing myself every time I try to reason this out. Can anyone offer advice?

My current code base is available at: Generic Zombie Shooter: Redux

Relevant Files:

Quote

src/com/gzsr/objects/weapons/Pistol.java - example of weapon; look at update() and fire() methods.
src/com/gzsr/gfx/particles/Particle.java - the particle class that handles particle movement
src/com/gzsr/entities/enemies/EnemyController.java - line 105, update() method shows how enemies check projectiles for collisions
src/com/gzsr/entities/enemies/Zumby.java - line 41, checkCollision() method for checking if projectile is in range

I'm also open to any suggestions about the rest of my code, specifically any design patterns I could use to make things easier to modify or anything that I could do differently.

Advertisement

What about doing a raycast check between previous -> current frame position?

Like, testing bounding spheres against the line section between prev->cur position etc :)

.:vinterberg:.

Fix your timestep.

You may still end up wanting to do swept checks for certain fast moving things, but fixing your timestep is fundamental to getting any kind of repeatable behaviour.

Ok, that seems better. I went with a fixed timestep (target frame rate of 60), and things seem consistent now (will have to confirm this by testing more). However, specifically, my Flamethrower weapon generates a lot of little particles, and most of them seem to pass right through the collision radius for the enemy, and I see them die off a little bit after they pass the zombie.

I've attached a screenshot in case it helps. Can anyone tell why it might be failing for that particular weapon, but not the basic weapons (Pistol, Assault Rifle, Shotgun)?

In the screenshot, you can see the flame particles mostly missing the zombie (the collision radius is noted by the red circle).

gzsr_gameplay_10.png

I had a (very) quick look at the source. One thing I notice is you are still propagating your 'time' through the update. You shouldn't need to to do this, as with a fixed timestep everything is based on a fixed 'tick' unit of time. Personally in the more rare circumstances I need to access gametime (for timers etc) I access them through a global mechanism (beware there are lots of gotchas with globals, and some people refuse to use them, but imo there are a few situations where they are a good solution imo).

You can pass a fraction through your render function though, for a fraction through the tick, store the previous and current positions / rotations etc then interpolate on the render.

To debug stuff like this, first call is to decrease your tick rate, turn off your interpolation and then you may be able to literally 'see' what is going on. If you down the tick rate to say 2 ticks a second, with no interpolation, the game will move slowly, and you can see whether particles are entering the collision radius and not colliding.

Bear in mind also there may also be 'ordering' effects with the collisions, from looking at your code. E.g. are you moving the projectiles and colliding checking them before moving the monster on that tick? So what you see may not be exactly what is going on under the hood, unless you move both, *then* do the collision check (i.e. both the projectile *and* the monster may be moving).

You may be able to simply solve this situation by giving the projectile volume and 'pushing' out the collision radius of the monster. But personally I'd probably have a lower tick rate than 60 per second .. often 10-20 ticks is enough with interpolation, and put in some kind of hierarchical collision system with a line segment / circle check as vinterberg suggests, instead of just point in circle.

As an aside, another option with fixed timestep is you can run multiple schemes at once : game logic at say 10 ticks per sec, physics at 60 ticks per second, which can be a good option in games which demand fast physics.

I have no control over the update and render methods. BasicGameState is a Slick2D class. My only option is to do it through the overridden method. Do you know of any good articles that could explain the particular type of collision you're referring to?

10 hours ago, PaCkEtPiRaTe said:

I have no control over the update and render methods. BasicGameState is a Slick2D class. My only option is to do it through the overridden method. Do you know of any good articles that could explain the particular type of collision you're referring to?

I don't really know that much java, or Slick2D, but it looks like you are calling the update and render functions yourself for players / monsters etc in GameState.java (rather than them being automagically called). In which case it would seem you can call pretty much whatever function you want, e.g. MyUpdate(), MyRender(float f), unless there's some particular reason it has to be the exact overridden method they suggest.

Sorry, nothing off top of my head for the collision, but google will find lots of articles, just search for things like 'line segment, circle, collision'.

3 hours ago, lawnjelly said:

I don't really know that much java, or Slick2D, but it looks like you are calling the update and render functions yourself for players / monsters etc in GameState.java (rather than them being automagically called). In which case it would seem you can call pretty much whatever function you want, e.g. MyUpdate(), MyRender(float f), unless there's some particular reason it has to be the exact overridden method they suggest.

Sorry, nothing off top of my head for the collision, but google will find lots of articles, just search for things like 'line segment, circle, collision'.

Wrong update method. I was referring to the overridden update method of the BasicGameState class, which is what my GameState class uses as its main update method. I then call the update methods of my various game objects from there. I have no control over this particular update method, so I have no control over what information the delta variable gives me.

Edit .. Just had closer look, it seems your loop is doing roughly the right kind of thing. I think we are at cross purposes, that's all, I was referring to the update / render in your game objects (player monsters etc) not to the function that gets called by Slick2D. :)

55 minutes ago, lawnjelly said:

Edit .. Just had closer look, it seems your loop is doing roughly the right kind of thing. I think we are at cross purposes, that's all, I was referring to the update / render in your game objects (player monsters etc) not to the function that gets called by Slick2D. :)

Well thank you for taking the time to look at the code. Now I just need to find a decent article explaining the collision that vinterberg describes.

This topic is closed to new replies.

Advertisement