## Improved collision detection and response简要分析与小的改进

2012-07-22 22:57  风恋残雪  阅读(1021)  评论(0编辑  收藏  举报

There are at least two ways to implement a character controller for a 3D game: Kinematic and Dynamic Character Controllers. In this post we discuss the difference and which solution is better.
A short reminder:
In this context a character controller is the piece of code that computes the movement of the player character in the game. It takes care that the user does not run through walls, can step up/down, jump etc. The character is often modeled as an upright capsule.
Related posts:  3D XNA Character Controller Example and Character Controller Requirements
Kinematic Character Controller:

The character controller is a dedicated piece of code that uses collision detection routines but no physics library. Physical behavior like gravity, non-penetration of solid objects is implemented explicitly in the character controller module. This type is also called Proxy Character Controller in Havok Physics. Bullet, PhysX and Havok have a Kinematic Character Controller implementation. Our 3D XNA Character Controller Example is another example of this category.
Dynamic Character Controller:

A physics library is used and the character is a special rigid body. The physics engine takes care of non-penetration, gravity etc. This type is also called Rigid Body Character Controller. Havok has a Dynamic Character Controller implementation.
Which solution is better?

Let's here why PhysX is against Dynamic Character Controllers:
(Lack Of) Continuous Collision Detection: Typical physics engines use discrete collision checks, leading to the famous tunneling effect that has plagued various commercial & non-commercial physics packages for years, which leads to three main problems:
The tunneling effect itself - if the character goes too fast, it might tunnel through a wall.
As a consequence, the maximum velocity of the character might be limited (hence also limiting the game-play possibilities).
Even without tunnel, the character might jitter when pushed forward in a corner. For example, the engine keeps moving it back and forth to slightly different positions.
No Direct Control: A rigid body is typically controlled with impulses or forces. It is nearly impossible to move it directly to its final position until you have converted the delta position vector to impulses or forces and applied them in hopes that the character will be where you wanted it to be as a result. Usually it doesn't work very well, in particular when the physics engine uses an imperfect linear solver.
Trouble with Friction: When the character is standing on a ramp, you don't want it to slide. Infinite friction is desired. When the character is moving forward on that same ramp, or sliding against a wall, you don't want it to slow down, thus a null friction is needed. These are usually defined with either 0 or infinite. However, the friction model might not be perfect, and what you actually get is very little friction, so you can still feel the character slowing down, or a high-but-not-infinite friction, so the character slides very slowly on that ramp no matter how artificially high the friction parameters are. The conflicting requirements for ramps mean that usually there is simply no way to perfectly model desired behavior.
Trouble with Restitution: Basically you don't want any restitution, ever. When the character moves fast and collides with a wall, you don't want it to bump against it. When the character falls from a height and lands on the ground, flexing his legs, you definitely don't want any bumps, which would visually look terrible. But once again, even when the restitution is exactly zero, you sometimes get a small bump nonetheless. This is not only related to the non-perfect nature of the linear solver, but also has to do with how typical penetration-depth-based engines recover from overlap situations, sometimes applying too high a force that repels objects more than desired.
Undesired Jumps: It is often important that a character stick to the ground, no matter what the physical behavior should be. For example, characters in action games tend to move fast, at unrealistic speeds. When they reach the top of a ramp, the physics engine often makes them jump a bit, in the same way a fast car would jump in the streets of San Francisco. But that is often not desired: the character should stick to the ground regardless of its current velocity. This is sometimes implemented using fixed joints, which is a terrible, terrible solution to a very simple problem that has been solved for years without requiring all the modern complexity of a physics engine.
Undesired Rotations: Finally, a character is always standing up and never rotating. However, a physics engine often has poor support for that kind of constraint, and a great deal of effort is put into just preventing a capsule around the character from falling (it should always stand up on its tip). This too is sometimes implemented using artificial joints, and the resulting system is neither very robust nor very fast.
(Excerpt from PhysX SDK manual v2.7.2 - please note that this is an older version of the PhysX SDK!)
Valid arguments - although nowadays most physics engines support continuous collision detection, so the first argument is partially valid today. Havok has a Dynamic Character Controller. Havok, what do you say in your defense?
The rigid body character controller has several advantages over the proxy character controller. These include:
Efficiency. The rigid body simulation is much more efficient than the phantom version. This is because of many internal optimizations in the rigid body simulation calculations.
Multithreading. On multithreaded platforms, the rigid body character controller multithreads naturally, as opposed to the proxy version which does not.
[...]
Character - rigid body and character - character interactions will be handled better by the rigid body character controller.
With these factors in mind we recommend using the rigid body character controller over using the proxy controller. However there are a number of caveats to be aware of in this choice. The behavior of the rigid body character controller may not be as smooth as the proxy controller, particularly for stair climbing.
(Excerpt from Havok Physics manual v5.5.0 - please note that this is an older version of Havok Physics!)
Points taken. Still, we prefer Kinematic Character Controllers for several reasons:
Many games or game engines do not use a physics engine, so the Dynamic Character Controller is not even an option. Integrating a physics engine only for the character controller does not sound reasonable.
Character Controller takes precedence over game physics. You can make a first person shooter without game physics, but you cannot make a first person shooter without a character controller. If game physics, e.g. a rolling barrel, behaves slightly odd, I do not care. But if the character controller behaves weird, the gameplay experience suffers. The character controller must be very precise, stable and tweakable.
Most games use custom character controller implementations. So the character controller code must be touched even if a standard physics engine is used. If a game developer has little physics experience, modifying a Kinematic Character Controller is easier. The kinematic controller deals with collisions, penetration depths and positions - whereas a dynamic controller deals with forces, impulses, friction, restitution, constraints etc.
If you already use a physics engine, it sounds tempting to use it for the character controller because many things come for free: non-penetration, gravity, pushing objects and being pushed. But: physics engines compute "rigid body dynamics". And a moving human does not behave like a rigid body!
Rigid body dynamics supports restitution, friction, inertia - things that have a different meaning for a character. And a character controller supports arbitrary slope limits, stepping up/down, etc. - things that have no natural counterpart in rigid body dynamics.
In the end, in a Dynamic Character Controller a lot of time is spent disabling features of the physics library and adding artificial non-physical behavior.
We feel Kinematic Character Controllers are the way to go. What do you think? If you have any arguments to add to the discussion, please let us know in the comments.



bool IntersectionDetection::intersectTriangle(const Vector& p1, const Vector& p2, const Vector& p3, const Vector& n, IntersectionPacket* packet) const
{
Vector closestPoint;
float u, v;
float dist2 = pointTriangleDistanceSquared(packet->m_position, p1, p2, p3, closestPoint, u, v);

return false;

packet->m_intersectionFound = true;

Vector delta = (packet->m_position - closestPoint);
float dist = sqrt(dist2);

if(dist > 0.0001f)
packet->m_position += delta * (packet->m_radius - dist) / dist;

return true;
}


// Set this to match application scale..
const float unitsPerMeter = 100.0f;
VECTOR CharacterEntity::collideWithWorld(const VECTOR& pos,
const VECTOR& vel)
{
// All hard-coded distances in this function is
// scaled to fit the setting above..
float unitScale = unitsPerMeter / 100.0f;
float veryCloseDistance = 0.005f * unitScale;
// do we need to worry?
if (collisionRecursionDepth>5)
return pos;
// Ok, we need to worry:
collisionPackage->velocity = vel;
45collisionPackage->normalizedVelocity = vel;
collisionPackage->normalizedVelocity.normalize();
collisionPackage->basePoint = pos;
collisionPackage->foundCollision = false;
// Check for collision (calls the collision routines)
// Application specific!!
world->checkCollision(collisionPackage);
// If no collision we just move along the velocity
if (collisionPackage->foundCollision == false) {
return pos + vel;
}
// *** Collision occured ***
// The original destination point
VECTOR destinationPoint = pos + vel;
VECTOR newBasePoint = pos;
// only update if we are not already very close
// and if so we only move very close to intersection..not
// to the exact spot.
if (collisionPackage->nearestDistance>=veryCloseDistance)
{
VECTOR V = vel;
V.SetLength(collisionPackage->nearestDistance);
newBasePoint = collisionPackage->basePoint + V;
// Adjust polygon intersection point (so sliding
// plane will be unaffected by the fact that we
// move slightly less than collision tells us)
V.normalize();
// veryCloseDistance 在我理解就是用来阻止物体之间相交的，即物理引擎中
// 习惯使用的空白（margin），所以此处的代码应该为
// collisionPackage->intersectionPoint -= min (collisionPackage->nearestDistance, veryCloseDistance) * V;
// 而不是下面的代码所示，如果分析得不正确还请指正。
collisionPackage->intersectionPoint -= veryCloseDistance * V;
}
// Determine the sliding plane
VECTOR slidePlaneOrigin = collisionPackage->intersectionPoint;
VECTOR slidePlaneNormal = newBasePoint-collisionPackage->intersectionPoint;
slidePlaneNormal.normalize();
PLANE slidingPlane(slidePlaneOrigin,slidePlaneNormal);
// Again, sorry about formatting.. but look carefully ;)
VECTOR newDestinationPoint = destinationPoint -
slidingPlane.signedDistanceTo(destinationPoint)*
slidePlaneNormal;
// Generate the slide vector, which will become our new
// velocity vector for the next iteration
VECTOR newVelocityVector = newDestinationPoint - collisionPackage->intersectionPoint;
// Recurse:
// dont recurse if the new velocity is very small
if (newVelocityVector.length() < veryCloseDistance)
{
return newBasePoint;
}
collisionRecursionDepth++;
return collideWithWorld(newBasePoint,newVelocityVector);
}