Gamasutra: The Art & Business of Making Gamesspacer
Book Excerpt: Game Engine Architecture
View All     RSS
September 20, 2017
arrowPress Releases
September 20, 2017
Games Press
View All     RSS






If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

Book Excerpt: Game Engine Architecture


November 25, 2009 Article Start Previous Page 3 of 6 Next
 

14.6.3. Object and Subsystem Interdependencies

Even if we didn't care about performance, a simplistic per-object updating approach breaks down when game objects depend on one another. For example, a human character might be holding a cat in her arms. In order to calculate the world-space pose of the cat's skeleton, we first need to calculate the world-space pose of the human. This implies that the order in which objects are updated is important to the proper functioning of the game.

Another related problem arises when engine subsystems depend on one another. For example, a rag doll physics simulation must be updated in concert with the animation engine. Typically, the animation system produces an intermediate, local-space skeletal pose. These joint transforms are converted to world space and applied to a system of connected rigid bodies that approximate the skeleton within the physics system. The rigid bodies are simulated forward in time by the physics system, and then the final resting places of the joints are applied back to their corresponding joints in the skeleton. Finally, the animation system calculates the final world-space pose and skinning matrix palette. So once again, the updating of the animation and physics systems must occur in a particular order in order to produce correct results. These kinds of inter-subsystem dependencies are commonplace in game engine design.

14.6.3.1. Phased Updates

To account for inter-subsystem dependencies, we can explicitly code our engine subsystem updates in the proper order within the main game loop. For example, to handle the interplay between the animation system and rag doll physics, we might write something like this:

while (true) // main game loop
{
// ...

g_animationEngine.CalculateIntermediatePoses(dt);

g_ragdollSystem.ApplySkeletonsToRagDolls();

g_physicsEngine.Simulate(dt); // runs ragdolls too

g_collisionEngine.DetectAndResolveCollisions(dt);

g_ragdollSystem.ApplyRagDollsToSkeletons();

g_animationEngine.FinalizePoseAndMatrixPalette();

// ...
}

We must be careful to update the states of our game objects at the right time during the game loop. This is oft en not as simple as calling a single Update() function per game object per frame. Game objects may depend upon the intermediate results of calculations performed by various engine subsystems. For example, a game object might request that animations be played prior to the animation system running its update. However, that same object may also want to procedurally adjust the intermediate pose generated by the animation system prior to that pose being used by the rag doll physics system and/or the final pose and matrix palette being generated. This implies that the object must be updated twice, once before the animation calculates its intermediate poses and once afterward.

Many game engines allow game objects to update at multiple points during the frame. For example, an engine might update game objects three times -- once before animation blending, once after animation blending but prior to final pose generation, and once after final pose generation. This can be accomplished by providing each game object class with three virtual functions that act as "hooks." In such a system, the game loop ends up looking something like this:

while (true) // main game loop
{
// ...

for (each gameObject)
{
gameObject.PreAnimUpdate(dt);
}

g_animationEngine.CalculateIntermediatePoses(dt);

for (each gameObject)
{
gameObject.PostAnimUpdate(dt);
}

g_ragdollSystem.ApplySkeletonsToRagDolls();

g_physicsEngine.Simulate(dt); // runs ragdolls too

g_collisionEngine.DetectAndResolveCollisions(dt);

g_ragdollSystem.ApplyRagDollsToSkeletons();

g_animationEngine.FinalizePoseAndMatrixPalette();

for (each gameObject)
{
gameObject.FinalUpdate(dt);
}

// ...
}

We can provide our game objects with as many update phases as we see fit. But we must be careful, because iterating over all game objects and calling a virtual function on each one can be expensive. Also, not all game objects require all update phases -- iterating over objects that don't require a particular phase is a pure waste of CPU bandwidth. One way to minimize the cost of iteration is to maintain multiple linked lists of game objects -- one for each update phase. If a particular object wants to be included in one of the update phases, it adds itself to the corresponding linked list. This avoids having to iterate over objects that are not interested in a particular update phase.


Article Start Previous Page 3 of 6 Next

Related Jobs

Konami Gaming, Inc.
Konami Gaming, Inc. — Las Vegas, Nevada, United States
[09.19.17]

Software Engineer III
Nintendo of America Inc.
Nintendo of America Inc. — Redmond, Washington, United States
[09.19.17]

Software Engineer, Developer Support (NTD)
DigitalFish, Inc.
DigitalFish, Inc. — San Mateo, California, United States
[09.18.17]

Software Engineer - Mechanical Learning
DigitalFish, Inc.
DigitalFish, Inc. — Mountain View, California, United States
[09.18.17]

Software Engineer - AR/VR





Loading Comments

loader image