want to create an fps

Started by
16 comments, last by lightxbulb 9 years, 5 months ago

I think you'll do fine with simple collisions. You can implement something like this:

Sphere to sphere:

Let sphere1 have coordinates p0 = (p0x, p0y, p0z), a movement vector (basically by what vector you want to translate the sphere in this frame) v = (vx, vy, vz) and radius r1. Let sphere2 have coordinates p1 = (p1x,p1y,p1z), movement vector m = (mx, my, mz) and radius r2. We want to find at what point exactly they will collide. The linear functions p(t) = p0 + v*t and q(t) = q0 + m*t can be defined. What we want to actually find is a point where the distance between the centers of the two spheres = r1+r2. Here's what we get:


||p(t)-q(t)|| = r1+r2
((p0-q0) + (v-m)*t)^2 = (r1+r2)^2
 (v-m)^2*t^2 + 2*(p0-q0)*(v-m)*t + (p0-q0)^2 - (r1+r2)^2 = 0
//Thus:
A = (v-m)^2, B = (p0-q0)*(v-m), C = (p0-q0)^2 - (r1+r2)^2, D = B^2 - A*C
//If D<0 => No collision
//If D>=0 => 
t1 = (-B-sqrt(D))/A
t2 = (-B+sqrt(D))/A

If 0<= t1 <= 1 then there is a collision (else there is no collision, at least in this frame) and when it happens sphere1 and sphere2 will have coordinates:


p(t1) = p0 + v*t1
q(t1) = q0 + m*t1

Note that t2 should not be considered unless your sphere is inside the other one.

(If there's no collision you just move the spheres normally)

After you have calculated the collisions you need to resolve them, you have many options, but the most popular ones are:

1) Stop response: they just stop at the point of collision, in this case you just move the spheres to coordinates p(t1) and q(t1) respectively.

2) Slide response: the spheres slide depending on their velocity vectors - you basically alter the velocity vector to remove the contribution in the opposite direction of the normal of the geometry you collided against: v' = v + (-n*v)*n, you alter the velocity vector like so only after you've applied the stop response(so your spheres would be at the correct coordinates) - there are some minor details that have to do with time, but I'll explain them later. ( n is the normal vector at the point of collision - can be calculated by n = (p0-q0)/||p0-q0|| - that would be the n used in the calculations of v', the n used for m' is n' = -n )

3) Bounce response: the spheres bounce off one another - once again you alter the velocity vector after the stop response - you just reflect the vector(and you can optionally divide it's magnitude - basically diminish the speed because for example you lost some energy in the bounce). You alter the velocity vector like so: v' = 2*(-v*n)*n + v - you basically reflect it. If you want to have some control over the "elasticity" of the sphere you can do: v' = elasticity*(2*(-v*n)*n + v), where elasticity is a scalar such that: 0<= elasticity <= 1.0; 0 makes it so the object doesn't bounce, 1 makes it so it bounces without losing any magnitude off its velocity vector, 0.5 for example will make the speed of the sphere halved after the bounce. You can of course pick more than 1 for elasticity - but that will make the sphere gain speed every time it bounces.

Another thing to note is that a moving sphere vs static geometry is a lot easier to evaluate (just put m = 0 ). While many moving spheres are harder to evaluate (not impossible). Here's a brief summary of what you will do for many moving spheres:

0) evaluate all spheres for collisions and keep track of the various results (you can store them in a std::map<double,std::pair<Sphere*, Sphere*>> collisions<-> collisions.insert(std::make_pair(t1, std::make_pair(sphere1, sphere2))) you get for t1 if there's a collision(such that 0<t1<remaining_time: remaining_time = 1 the first time you do 0) in a new frame - otherwise dismiss the result). (you don't need to evaluate 2 spheres twice for the same collision as collision(sphere1,sphere2) = collision(sphere2, sphere1) )

1) if the map where you store the collisions is empty() => just move all your objects by v*remaining_time

if the map is not empty => proceed to 2)

2) pick the first element of the map(it will be with smallest time) - let's call it t1

3) translate every moving sphere by v*t1, where v is the velocity vector of the sphere

4) You would get at least one collision, resolve it (by stop/slide/bounce) => proceed back to 0), but make remaining_time = remaining_time - t1

At some point (depending on the magnitude of the velocity vectors, and the spatial distribution density of the spheres in the scene) you'll get 0 collisions at 0), or you would get such a low remaining_time that all the t1 results would get dismissed(because then t1>remaining_time) so you would basically get no collisions.

The theory is basically you find out the collision that will happen after the smallest time period - you resolve the collision and movement up to that period then you do the same for the remaining time period - you'd get really accurate collisions and resolutions this way (you can of course optimize it by making it less accurate - but I wanted to first show the ideal case). It may seem that you could get a lot of iterations just for one frame, but note that if your velocity vector has a small magnitude for this frame and the objects distribution doesn't have a too high density you probably won't get more than a few iterations. And it's an ideal approach - there are ways to alter it and make it plausible still.

For a sphere to axis aligned box and axis aligned box to box collisions, it's pretty much the same approach as sphere to sphere collisions though easier(if you have issues with it, I could still write the basic algorithm). Note that another popular decision is cylinder(used to depict players usually) to sphere/box/cylinder. With a cylinder you basically act for x,z as if it were a circle to circle collision(same as sphere to sphere but with 2d vectors) and for y as if it was an axis aligned box (basically your code for it should look like a hybrid between sphere/box collisions).

Note that you can do a sphere/box/cylinder to triangle collision if you're lazy to model your physics around your model's geometry.

A really simple example(for windows): http://www.mediafire.com/download/oouhkxydcw91d2w/ss_coll.7z

P.S. If you've got any questions feel free to ask.

Advertisement

so after some reading in the subject I decided to go with a rather simple strategy (I realized I definitely don't want to write a physics engine...):

Collisions should only make sure that the player (and the enemies) can't go into walls, and can't leave the map. So I created a rather simple collision mechanism, everything is represented by AABBs, and I try to keep the dynamic (player+enemies) AABBs out of the static ones.

here's the code I wrote for this purpose:


//if b collides w/ a, we set b's position, so that it's outside a
//an aabb is defined by it's position (center), it's half-extents, and min/max vertices
void collide_aabb_aabb( aabb a, aabb& b )
{
  if( a.intersects( &b ) && //there's an intersection, need collision response
      !b.is_inside( &a ) ) //but only if we're not inside
  {
    vec3 diff = b.pos - a.pos;
    vec3 abs_diff = abs( diff );

    vec3 res = vec3( 0 );

    //max search
    int idx = 0;
    for( int c = 1; c < 3; ++c )
    {
      if( abs_diff[idx] < abs_diff[c] )
        idx = c;
    }

    //final "collision response"
    res[idx] = ((a.extents[idx] + b.extents[idx]) - abs_diff[idx]) * sign(diff[idx]);

    b = aabb( b.pos + res, b.extents );
  }
}

However when I tried it on the Sponza test map, I'm facing the problem that for AABBs that completely enclose the player's AABB once I get outside of them, I can't get back inside, so this way there are some problematic AABBs (bigger meshes...). Also if I don't check if I'm inside the other AABB or not, I just get pushed outside immediately...

Obviously I could alleviate the problem by using 4 walls instead of a box, but then I should ignore some AABBs, rigtht? But is there a better solution to this?

here's a demo to show what I mean:

https://drive.google.com/file/d/0B33Sh832pOdOYlN6b0cyZjRDcGs/view?usp=sharing

@lightxbulb

thank you! I think I do understand the intersection math, and the basic physics stuff. What I think I don't understand is the practical considerations...

Solving analytically the collisions is more accurate and sidesteps a number of issues. Heck even engines from 2007 use it(http://www.blitzbasic.com/ - b3d went open source - you can check the collision code). Another thing is that you can deal with collisions of multiple moving bodies correctly if you implement the analytical solution. What the solution does is basically mathematically "predict" exactly where the collision occurs. So even if you deal with many moving objects you can still get a correct resolution of the collisions. On the other hand the "check if you're inside the object then resolve the collision" type of methods usually have the issue that you can pass through the object if you translate with a vector with a big enough magnitude (imagine having lag and needing to sync after that - you'll need to make bigger translations to make up - which will result in a disaster). Another thing is that you cannot say which collision happened first and which second,third etc. during this frame - so you'll have an issue if you're looking for even a bit of complexity in the collision handling. And I shouldn't mention that if you evaluate a collision between 2 or more moving objects the method you use works even worse. The method I showed you is harder, but I believe that the extra effort is worth it, as you'll avoid quite a few issues, while the collision system would be more flexible and easily extensible (+ it's easier to resolve collisions when you know exactly in what order and at what coordinates objects will collide - then you can go for various resolution models).

On a side note - it's not a good idea to have your object in the bounding box of another - the idea is that objects are solid even if in computer graphics you just draw the surface - so you'd better do the extra effort of making a bounding box for each of the sides if the object is supposed to be hollow - that would save you some real effort later on imo(I shouldn't even mention that usually the back polygons will be culled - so even your geometry would be changed if you want to model a hollow object).

P.S. Mind you in the 0) - 4) steps you need to mark your objects as collided if they collide so they don't get stuck to the other object before even having the chance to resolve the collision(lest you want response stop - but still it's good to have a collided flag). You can have various code implementations, but the basic idea is simple - check all the collisions that could occur (and btw I'd advise another collision routine for moving vs static objects, because moving to moving is harder as you see) - find the one that will occur after the least period of time - then update every moving object with this time period -> repeat (somewhere along the way in the next cycle of this algorithm you need to resolve the actually collided objects - not the predicted ones). It could be optimized if you butcher up accuracy.

P.P.S. Check this http://www.stencyl.com/help/view/continuous-collision-detection/

Solving analytically the collisions is more accurate and sidesteps a number of issues. Heck even engines from 2007 use it(http://www.blitzbasic.com/ - b3d went open source - you can check the collision code). Another thing is that you can deal with collisions of multiple moving bodies correctly if you implement the analytical solution. What the solution does is basically mathematically "predict" exactly where the collision occurs. So even if you deal with many moving objects you can still get a correct resolution of the collisions. On the other hand the "check if you're inside the object then resolve the collision" type of methods usually have the issue that you can pass through the object if you translate with a vector with a big enough magnitude (imagine having lag and needing to sync after that - you'll need to make bigger translations to make up - which will result in a disaster). Another thing is that you cannot say which collision happened first and which second,third etc. during this frame - so you'll have an issue if you're looking for even a bit of complexity in the collision handling. And I shouldn't mention that if you evaluate a collision between 2 or more moving objects the method you use works even worse. The method I showed you is harder, but I believe that the extra effort is worth it, as you'll avoid quite a few issues, while the collision system would be more flexible and easily extensible (+ it's easier to resolve collisions when you know exactly in what order and at what coordinates objects will collide - then you can go for various resolution models).

On a side note - it's not a good idea to have your object in the bounding box of another - the idea is that objects are solid even if in computer graphics you just draw the surface - so you'd better do the extra effort of making a bounding box for each of the sides if the object is supposed to be hollow - that would save you some real effort later on imo(I shouldn't even mention that usually the back polygons will be culled - so even your geometry would be changed if you want to model a hollow object).

P.S. Mind you in the 0) - 4) steps you need to mark your objects as collided if they collide so they don't get stuck to the other object before even having the chance to resolve the collision(lest you want response stop - but still it's good to have a collided flag). You can have various code implementations, but the basic idea is simple - check all the collisions that could occur (and btw I'd advise another collision routine for moving vs static objects, because moving to moving is harder as you see) - find the one that will occur after the least period of time - then update every moving object with this time period -> repeat (somewhere along the way in the next cycle of this algorithm you need to resolve the actually collided objects - not the predicted ones). It could be optimized if you butcher up accuracy.

P.P.S. Check this http://www.stencyl.com/help/view/continuous-collision-detection/

I know about these issues, I read the blog posts on http://www.wildbunny.co.uk

However the gameplay would be so simple that I don't want to do a complete collision system just one that's "good enough" that's why I didn't bother with all these things...

I decided that the dynamic objects can't collide with each other, and they will not move that fast that I'd need analytic collision handling (for bullets I'll use simple rays, not moving spheres).

You're right though, a proper system would involve these things. However I think I'll switch to Bullet as soon as things would get messy, as I really don't want to reinvent the wheel. It's just that for a game this simple, I think this should do fine.

I'll update the sponza scene so that there won't be big AABBs.

Well then just fix the bounding boxes, because the idea is that inside the bounding box it's a solid object, if you want to model something hollow - do it with a few bounding boxes - it's the easiest way, and will save you some headaches.

Well then just fix the bounding boxes, because the idea is that inside the bounding box it's a solid object, if you want to model something hollow - do it with a few bounding boxes - it's the easiest way, and will save you some headaches.

Allright, fixed it. And by that I mean that I wrote some code to ignore AABBs that enclose the player at start, plus I had to add some dummy geometry in blender so that there are replacement AABBs.

I also had to modify that collision code a bit...


//if b collides w/ a, we set b's position, so that it's outside a
//an aabb is defined by it's position (center), it's half-extents, and min/max vertices
void collide_aabb_aabb( aabb a, aabb& b )
{
  if( a.intersects( &b ) && //there's an intersection, need collision response
      !b.is_inside( &a ) ) //but only if we're not inside
  {
    vec3 diff = b.pos - a.pos;
    vec3 abs_diff = abs( diff );
    vec3 extent_diff = (a.extents + b.extents) - abs_diff;

    vec3 res = vec3( 0 );

    //min search
    int idx = 0;
    for( int c = 1; c < 3; ++c )
    {
      if( extent_diff[idx] > extent_diff[c] )
        idx = c;
    }

    //final "collision response"
    res[idx] = extent_diff[idx] * sign(diff[idx]);

    b = aabb( b.pos + res, b.extents );
  }
}

Next questions :)
So now I want to add enemies, I can already do gpu skinning, however I suppose that in order to be able to shoot certain body parts, I need to move the collision geometry with the parts. So I suppose I need to modify them using the same bones that a mesh has. Is that right? And how do I figure out which bones should affect for example the head's sphere?

Also how can I blend between animations for for example the weapon animations? (idle/reload etc.)

One solution is to just do a line pick against the geometry (before that you can check against the bounding volume to optimize - so you can exit early if you're not gonna hit a player), Otherwise you'd either have to pick a ready physics engine or write one (physics depending on animations is already advanced enough to warrant it imo). Even some commercial games use static bounding volumes while ignoring the animations though, so that's the easiest option - I can guess that mostly the arms/legs will move but you can approximate their area of movement even with a static(as in not affected by animations) bounding box.

If you go for the hard way though:

So I suppose I need to modify them using the same bones that a mesh has. Is that right? And how do I figure out which bones should affect for example the head's sphere?

Yes you need to bind them to the bones, the various part would be like simplified parts of your model. But then you'd need to think about non axis aligned bounding volumes - because if you use axis aligned bounding boxes for example - then you won't gain much accuracy over the static AABBs. So I would advise you either: get a ready physics engine, make a new one, or chose an easier option.

This topic is closed to new replies.

Advertisement