|
Resource
Guide

Outsourcing
Reality: Integrating a Commercial Physics Engine
Applying Forces
There are
three ways to give an object motion in a physics world: you can apply
a force to the object, you can apply an impulse, and you can set its velocity
directly. Each has different trade-offs.
To be effective,
a force has to be applied over a specific amount of time. In many sims,
applying a force means "apply this force over the next simulation
step." This is usually not what you want, as applying a force for
1/60th of a second won't push it very far unless it's a huge force. What
you do want is a way to say, as simply as possible, "apply this amount
of force for this amount of time." There are three ways to do this.
The first approach is to continually reapply the force each substep until
you've reached your target time. For each force you wish to apply, keep
track of how long it needs to be applied, and apply it one substep at
a time. The problem with this approach is its complexity; you need to
keep track of each force that you're applying, how long it's been applied
for, and how much longer it's going to be applied. There's also the minor
problem that you must apply forces over an integer number of substeps,
which limits how finely you can tune your use of forces.
The second approach is to use impulses. An impulse is a force premultiplied
by a time and which takes effect instantaneously. If you want to apply
a force of 10 newtons continuously over 1/10th of a second, a 1-newton
impulse will do the trick. The limitation to using impulses is that the
force is not in fact applied for the entire time; all the energy is delivered
instantly, and your object reaches its target velocity instantaneously
rather that being gradually accelerated. For quick forces, such as a jump
or a bullet, the simplicity of impulses makes them preferable to actual
forces. If you want to lift something slowly, though, forces are the way
to go.
The third approach -- velocities -- is both limiting and particularly
useful for situations where you need very tight control. We'll discuss
it in detail later in the "Player Control Strategies" section.
Spatial Queries
Physics
engines by their nature incorporate high-performance spatial data structures.
These are handy for a lot of query types:
- Trigger
volumes (switch to camera B when the user enters this region).
- Line-of-sight
(can I see the power tower from here?).
- Ray
casts for AI environment probing (can Watson see me?).
- Proximity
queries for AI (start talking when the player is within five feet).
- Evaluating
theoretical object placement (can this door close without crushing
anything?).
- Ray
casts for picking (let the user click on the lever).
- Volume
queries for motion planning (can I walk all the way to the hatch?).
Spatial
queries can affect many types of game logic. A good query interface will
save you time every day; it's an area of integration that will reward
careful planning. While it can be very game specific, there are a few
design parameters for your query interface that apply to almost all games:
Cascading. One query can significantly narrow the field for multiple,
more complex queries: a 20-foot sphere around your avatar can gather all
potentially interesting objects for subsequent query by line-of-sight.
Triggers. Some queries are set up once and report only when their
state changes. For example, a region might notify you when the player
enters, rather than you having to ask all regions each frame. This will
typically be delivered as an event from the collision system.
Explicit queries. Some queries are only relevant at a particular
moment and must be resolved instantaneously, for example, "Is that
door in my way?"
Query partitioning. Some questions are only asked about specific
types of objects; a camera region may only ever care if an avatar enters
it, not a creature or rolling boulder. If your physics engine has an "early
out" callback, you can use such application-specific type information
to partition the query space, eliminating expensive detailed testing for
pairs of objects you know will never interact.
Integrating
Keyframed Motion
If you're not using physics for a racing game or flight simulation, you're
probably looking for interesting gameplay - big complicated machines,
moving platforms, and the like. It's likely that many of these will be
lovingly hand-animated by your talented artists. Unfortunately, hand animation
is not obligated to obey the laws of physics. How do we integrate keyframed
motion into a physically based simulation?
The approach I'll discuss here is particular to the Havok API; it happens
to be what we're using, and a proper discussion of these details requires
a bit of specificity. It should be illuminating regardless of your choice
in API, however, as it demonstrates how time, movement, and frame rate
can all affect your simulation.
There are two primary issues involved with "physicalizing" keyframed
animation:
- Translate
motion from the hierarchical scene graph into the flat physics world.
- Give
the physics engine enough information about the moving object to allow
it to interact realistically with other, non-keyframed objects.We've
adopted a few simplifying assumptions for keyframed motion, which greatly
simplify implementation while still capturing the essential functionality.
First, we
consider keyframed motion to be nonnegotiable. A keyframed sliding wall
can push a character, but a character cannot push a keyframed wall.
Our second assumption is that we do not ask the physics engine to resolve
interaction between two keyframed systems. Because these systems are hand-animated
and initiated by script, avoiding interdependencies is the level author's
domain.
When considering the integration of physics and keyframed animation, we
first need to gather the local-to-world transforms of all the keyframed
objects, as we'll need them to feed positions and velocities into the
simulation. Because physics has no sense of hierarchy, you'll need all
your kinetic information in world space. One way to do this is to cache
matrices as you traverse your scene graph in preparation for rendering.
This process gives you the matrix that you need to match the flat transform
structure of physics. Because of the no-negotiating rule for keyframed
objects, you can go ahead and submit the keyframed objects to your rendering
pipeline as you traverse, as physics will not change those transforms.
This helps parallelism, since all static and keyframed geometry can be
transmitted to the graphics card before physics even starts.
Keyframed objects participate only partially in the simulation; they are
not moved by gravity, and other objects hitting them do not impart forces.
They are moved only by keyframe data. For this reason, it is necessary
to "freeze" the keyframed objects during the simulation phase
in which such forces are calculated and applied.
Keyframed objects are further marked at setup time as zero-order-integration
objects. This advises physics that these objects are explicitly positioned
and instructs the engine to call back during each integration substep.
In this callback, you are responsible for updating the position, orientation,
linear velocity, and angular velocity for the keyframed object. This information
is critical for determining what happens when, say, your avatar is standing
on top of that keyframed elevator. Since the physics engine has no knowledge
of the forces at work, it's relying on you to help it fake the results.
To illustrate the importance of getting the velocity right, think about
the difference between standing on an elevator that's moving down and
one that's moving up. In the down case, a collision between you and the
elevator should be resolved by you moving down. In the up case, the exact
opposite is desired. The only difference here is velocity, and an incorrect
result will embed your player up to the knees in the elevator floor -
undesirable by most standards.
The process of calculating velocities is a simple matter of interpolating
position and orientation from the animated transforms that you stashed
away a few paragraphs back. As an alternate, higher-quality-but-higher-cost
approach, you can ask your animation system at each physics substep to
interpolate a fresh position for you. This extra bit of work can be expensive,
because you have to reinterpolate the motion channel not only for the
object in question but also for any parent transforms.
What this gains for you is a greater degree of frame rate independence
for keyframed physical objects. To illustrate the problem of frame rate
dependence, take a look at Figure 4.
Figure 4
shows an elevator reaching the bottom of its descent and moving back up.
At frames 1 and 2, it's in the same position but moving in two different
directions. If you're sampling position only at frame boundaries, you'll
conclude that the elevator is stationary. If you add a sample in the middle,
you'll have a more accurate simulation, at a cost of reaccumulating all
transform dependencies. This is a fairly dramatic case; in many other
cases, you'll see the object calculate different velocities at different
frame rates. How much this matters to your players depends in large degree
on your game's animation speed, object velocities, and tolerance for error
in motion. In a surprising number of cases, this winds up not mattering,
but it's an accuracy trade-off of which you should be well aware.
The approach I just outlined is not the only one to handling keyframed
motion. The Karma engine provides a different facility in which the keyframe
data is used as a constraint to the object's position but does not control
it directly. The end result is that the object is attached to the animation
in a springy fashion; if there are a lot of people in your keyframed elevator,
it will lag behind, springing ahead again as folks jump off. You can adjust
the strength of the spring and the speed with which it acts. This is a
neat gameplay effect and can be excellent for the right application.
|