
Let's assume you're trying to simulate a car driving on an uneven terrain (perhaps some sort of heightmap). Let's assume you know how to measure the distance from the car's chassis to the heightmap ground at any position (typically, measure the height of the heightmap, and subtract the height of the car at that point in the XZ plane). The car has four wheels at four different positions on the chassis; these wheels are generally in contact with the ground.
Each contact is a form of physical spring, which affects the car with a force depending on how far the contact is from the chassis. At some length, the force is 0; the wheel lets go of the ground. At some point, the force is 1/4 the weight of the car times gravity. This is the equilibrium point.
Each spring can be treated as attached to the corners of a box. Each spring, each simulation step, applies force to a simulated rigid body. The trick is that the force is off-center, so it applies rotational torque to the body.
In general, you can represent a rigid body as:
public class RigidBody { public Vector3 Position; public Quaternion Orientation; public Vector3 LinearVelocity; public Vector3 AngularVelocity; public Vector3 Force; public Vector3 Torque; public float Mass; public void Integrate(float dt) { LinearVelocity += Force / Mass * dt; Force = Vector3.Zero; AngularVelocity += Torque / Mass * dt; Torque = Vector3.Zero; Position += LinearVelocity * dt; Orientation += new Quaternion((AngularVelocity * dt), 0) * Orientation; Orientation.Normalize(); } }
This assumes that the mass is evenly distributed (like a sphere) in world space; else you'll have to build an inertia tensor for your mass distribution and apply that to the places that now just divide by Mass.
Now, when you apply an impulse at a (world-space) position to a body, it will apply both force and torque. If I remember the math correctly, the function looks like:
public void ApplyForce(Vector3 forcePosition, Vector3 directionMagnitude) { float lengthSquared = directionMagnitude.LengthSquared(); if (lengthSquared < 1e-8f) return; // or use your own favorite epsilon Force += directionMagnitude; Vector3 distance = forcePosition - Position; Torque += Vector3.Cross(directionMagnitude, distance); }
So, each step, you will run the raycast to detect distance to ground, turn that into a spring force, apply those forces at the positions of the wheels to the body, and then call Integrate() to update position/orientation of the body. Note that tuning physical simulation systems (spring strength, masses, etc) is notoriously tricky and will probably take many tries. You also usually want to dampen the springs; in general by adding a counter-force based on the velocity of each of the points in world space. Velocity of a given point is, again if I remember my math right, something like the following:
public Vector3 PointVelocity(Vector3 worldPoint) { Vector3 distance = worldPoint - Position; Vector3 vel = Vector3.Cross(AngularVelocity, distance) + LinearVelocity; return vel; }
The actual rigid body simulation is pretty simple: each update step, add forces/impulses based on the wheel springs (and any collision detection constraints, etc), then integrate the body for the duration of the time step (typically 1/60th of a second).
However, for a real physics system, you will start doing collision detection, so that the car can't run into walls, or a very hard landing can't push the car underground, etc. At that point, you'll need collision proxies for the shape of the car (typically, a box), and for the ground (a heightmap or mesh). You'll also formulate the collision constraints using some kind of joint, or contact constraint. When you start doing that to a number of movable bodies (boxes, traffic cones, and whatnot) you will come to situations where a number of objects are in contact with each other, and integrating each body in isolation from the others won't be very realistic. At that point, you need a system that solves all the constraints at the same time. Typically, this is formulated as a linear constraint problem solver, also known as a "big matrix" solver.
Collision detection, and constraint solution, are actually the hard parts of physical simulation. Thus, I recommend you get a good reference book if you want to go down that path. Or just download an existing engine, and use that; someone else has already done the work of writing and debugging all that gnarly code :-)
| Attachment | Size |
|---|---|
| CSharpRigidBody.zip | 1.55 KB |
Comments
Epsilon
Just a tip - you can use "float.Epsilon" instead of 1e-8f.
Thanks for the comment!
Thanks for the comment! That's certainly one possible value for the epsilon.
Choosing an appropriate Epsilon is a black art! The value of float.Epsilon is the smallest representable value (I believe denormalized!), which may not actually be the value that you want for robustness in the simulation. Specifically, when you compare values with magnitude greater than 0, you are guaranteed that any representable difference between them is greater than float.epsilon, thus adding only float.epsilon will be the same as a true equals test.
Where you add an epsilon, how big it is, and which order you do your math and testing actually matters to the behavior of the simulation, and doing a full analysis of errors, margins and cancellations can find all kinds of surprising corner cases.
Possible error?
I think there's something wrong with your math (or rather, physics?) in ApplyForce(Vector3, Vector3).
The directional force is added to the force acting on the body, as a whole. Another part of that force is then used to add torque to the body. Which means you've just created energy out of nothing.
You'll have to break down the directional force into two separate vectors. The first vector points from the source of the directional force to the body's center of mass. The magnitude of that vector is the cosine of the angle between the directional force and that vector. This force is used to translate the body (added to this.Force). The second vector is perpendicular to the first vector and uses the sinus of the angle as magnitude. This force is used to rotate the body (added to this.Torque). The sum of both forces is the same as the initial force (Law of Conservation).
A physical system is actually
A physical system is actually a 6-dimensional space. The angular momentum lives orthogonally to the linear momentum, and does not interact in the absense of contact constraints. Thus, you don't "create something from nothing" when applying the impulse to the center of mass for linear momentum, and calculate the angular momentum applied torque as radius times force times sin-angle times time.
Note that the "sin-angle" part comes out automatically from the cross product formulation used to derive the torque to apply. You can check out the appropriate formulas here:
http://en.wikipedia.org/wiki/Elementary_physics_formulae
Another way to think of it: If you hit a north/south facing iron bar at the north end, perpendicular to the bar, in the eastward direction, the north part of the bar will start rotating eastward -- but the south part of the bar starts rotating westward! These linear momentums (for each discrete "particle" of the bar, if you will) cancel each other out, leaving the full linear momentum as the integral of all the particles around the center.
Hope this helps!
Best Introduction.
I'm not new to game physics, and was just passing by and read your tutorial, and think its really good. I hope you do a follow up one, maybe expanding onto different integration techniques, impulses, solvers etc, as you have a really good way of putting across the principle.
Thanks and keep up the good work.
Terry
Clear and to the point.
A nice tutorial on the workings of rigidbodies - not to simple and cuts through to the real core of whats happening.
Good Stuff :)
Btw: don't forget to apply
Btw: don't forget to apply gravity each step, or your rigid body will float in the air, and go sailing off into infinity as soon as some force acts on it.