18.5PublishorPerish 317
you never publish (or receive) the actual properties used to render location and
orientation. If you do, the remote actors will get an update and render the last
known values instead of the results of dead reckoning. It’s an easy thing to over-
look and results in a massive one-frame discontinuity (a.k.a. blip). Instead, create
publishable properties for the last known values (i.e., location, velocity, accelera-
tion, orientation, and angular velocity) that are distinct from the real values.
The second consideration is partial actor updates, messages that only contain
a few actor properties. To obtain believable dead reckoning, the values in the
kinematic state need to be published frequently. However, the rest of the actor’s
properties usually don’t change that much, so the publishing code needs a way to
swap between a partial and full update. Most of the time, we just send the kine-
matic properties. Then, as needed, we send other properties that have changed
and periodically (e.g., every ten seconds) send out a heartbeat that contains eve-
rything. The heartbeat can help keep servers and clients in sync.
MythBusting—AccelerationIsNotAlwaysYourFriend
In the quest to create believable dead reckoning, acceleration can be a huge
advantage, but be warned that some physics engines give inconsistent (a.k.a.
spiky) readings for linear acceleration, especially when looked at in a single
frame as an instantaneous value. Because acceleration is difficult to predict
and is based on the square of time, it can sometimes make things worse by in-
troducing noticeable under- and overcompensations. For example, this can be
a problem with highly jointed vehicles for which the forces are competing on
a frame-by-frame basis or with actors that intentionally bounce or vibrate.
With this in mind, the third consideration is determining what the last known
values should be. The last known location and orientation come directly from the
actor’s current render values. However, if the velocity and acceleration values
from the physics engine are giving bad results, try calculating an instantaneous
velocity and acceleration instead. In extreme cases, try blending the velocity over
two or three frames to average out some of the sharp instantaneous changes.
PublishingTips
Below are some final tips for publishing:
Published values can be quantized or compressed to reduce bandwidth
[Sayood 2006].
318 18.BelievableDeadReckoningforNetworkedGames
If an actor isn’t stable at speeds near zero due to physics, consider publishing
a zero velocity and/or acceleration instead. The projective velocity blend will
resolve the small translation change anyway.
If publishing regular heartbeats, be sure to sync them with the partial updates
to keep the updates regular. Also, try staggering the heartbeat time by a ran-
dom amount to prevent clumps of full updates caused by map loading.
Some types of actors don’t really move (e.g., a building or static light). Im-
prove performance by using a static mode that simply teleports actors.
In some games, the orientation might matter more than the location, or vice
versa. Consider publishing them separately and at different rates.
To reduce the bandwidth using
ShouldForceUpdate(), you need to dead
reckon the local actors in order to check against the threshold values.
Evaluate the order of operations in the game loop to ensure published values
are computed correctly. An example order might include: handle user input,
tick local (process incoming messages and actor behaviors), tick remote (per-
form dead reckoning), publish dead reckoning, start physics (background for
next frame), update cameras, render, finish physics. A bad order will cause
all sorts of hard-to-debug dead reckoning anomalies.
There is an optional damping technique that can help reduce oscillations
when the acceleration is changing rapidly (e.g., zigzagging). Take the current
and previous acceleration vectors and normalize them. Then, compute the dot
product between them and treat it as a scalar to reduce the acceleration before
publishing (shown in the
ComputeCurrentVelocity() function in List-
ing 18.2).
Acceleration in the up/down direction can sometimes cause floating or sink-
ing. Consider publishing a zero instead.
TheWholeStory
When all the pieces are put together, the code looks roughly like Listing 18.2.
void OnTickRemote(const TickMessage& tickMessage)
{
// This is for local actors, but happens during Tick Remote.
double elapsedTime = tickMessage.GetDeltaSimTime();
bool forceUpdate = false, fullUpdate = false;
Vec3 rot = GetRotation();
Vec3 pos = GetTranslation();
18.5PublishorPerish 319
mSecsSinceLastUpdateSent += elapsedTime;
mTimeUntilHeartBeat -= elapsedTime;
// Have to update instant velocity even if we don't publish.
ComputeCurrentVelocity(elapsedTime, pos, rot);
if ((mTimeUntilHeartBeat <= 0.0F) || (IsFullUpdateNeeded()))
{
fullUpdate = true;
forceUpdate = true;
}
else
{
forceUpdate = ShouldForceUpdate(pos, rot);
fullUpdate = (mTimeUntilHeartBeat < HEARTBEAT_TIME * 0.1F);
}
if (forceUpdate)
{
SetLastKnownValuesBeforePublish(pos, rot);
if (fullUpdate)
{
mTimeUntilHeartBeat = HEARTBEAT_TIME; // +/- random offset
NotifyFullActorUpdate();
}
else
{
NotifyPartialActorUpdate();
}
mSecsSinceLastUpdateSent = 0.0F;
}
}
void SetLastKnownValuesBeforePublish(const Vec3& pos, const Vec3& rot)
{
SetLastKnownTranslation(pos);
SetLastKnownRotation(rot);
SetLastKnownVelocity(ClampTinyValues(GetCurrentVel()));
SetLastKnownAngularVel(ClampTinyValues(GetCurrentAngularVel()));
320 18.BelievableDeadReckoningforNetworkedGames
// (OPTIONAL!) ACCELERATION dampen to prevent wild swings.
// Normalize current accel. Dot with accel from last update. Use
// the product to scale our current Acceleration.
Vec3 curAccel = GetCurrentAccel();
curAccel.normalize();
float accelScale = curAccel * mAccelOfLastPublish;
mAccelOfLastPublish = curAccel; // (pre-normalized)
SetLastKnownAccel(GetCurrentAccel() * Max(0.0F, accelScale));
}
void ComputeCurrentVelocity(float deltaTime, const Vec3& pos,
const Vec3& rot)
{
if ((mPrevFrameTime > 0.0F) && (mLastPos.length2() > 0.0F))
{
Vec3 prevComputedLinearVel = mComputedLinearVel;
Vec3 distanceMoved = pos - mLastPos;
mComputedLinearVel = distanceMoved / mPrevFrameTime;
ClampTinyValues(mComputedLinearVel);
// accel = the instantaneous differential of the velocity.
Vec3 deltaVel = mComputedLinearVel - prevComputedLinearVel;
Vec3 computedAccel = deltaVel / mPrevDeltaFrameTime;
computedAccel.z() = 0.0F; // up/down accel isn't always helpful.
SetCurrentAcceleration(computedAccel);
SetCurrentVelocity(mComputedLinearVel);
}
mLastPos = pos;
mPrevFrameTime = deltaTime;
}
Listing 18.2. Publish—the whole story.
18.6GroundClamping
No matter how awesome your dead reckoning algorithm becomes, at some point,
the problem of ground clamping is going to come up. The easiest way to visual-
ize the problem is to drop a vehicle off of a ledge. When it impacts the ground,
18.6GroundClamping 321
the velocity is going to project the dead-reckoned position under the ground. Few
things are as disconcerting as watching a tank disappear halfway into the dirt. As
an example, the demo on the website allows mines to fall under ground.
CanWeFixIt?
As with many dead reckoning problems, there isn’t one perfect solution. Howev-
er, some simple ground clamping can make a big difference, especially for far
away actors. Ground clamping is adjusting an actor’s vertical position and orien-
tation to make it follow the ground. The most important thing to remember about
ground clamping is that it happens after the rest of the dead reckoning. Do every-
thing else first.
The following is one example of a ground clamping technique. Using the
final dead reckoned position and orientation, pick three points on the bounding
surface of the actor. Perform a ray cast starting above those points and directed
downward. Then, for each point, check for hits and clamp the final point if ap-
propriate. Compute the average height H of the final points
0
Q
,
1
Q
, and
2
Q
, and
compute the normal
N of the triangle through those points as follows:
012
3
z
z
z
H
QQQ
10 20

N
QQ Q Q.
Use H as the final clamped ground height for the actor and use the normal to de-
termine the final orientation. While not appropriate for all cases, this technique is
fast and easy to implement, making it ideal for distant objects.
OtherConsiderations
Another possible solution for this problem is to use the physics engine to
prevent interpenetration. This has the benefit of avoiding surface penetration
in all directions, but it can impact performance. It can also create new prob-
lems, such as warping the position, the need for additional blends, and sharp
discontinuities.
Another way to minimize ground penetration is to have local actors project
their velocities and accelerations into the future before publishing. Then,
damp the values as needed so that penetration will not occur on remote actors
(a method known as predictive prevention). This simple trick can improve
behavior in all directions and may eliminate the need to check for interpene-
tration.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.61.81