Many games use physics engines to drive the way things move and react. Using a physics engine can add immersion, eye candy, and, best of all, emergent gameplay, but can also, if used incorrectly, lead to unrealistic results or game-breaking problems. In this post, I'll explain how to identify and fix common problems seen in games of today.
We will examine and solve these problems as seen in Unity, focusing on the built-in 3D physics engine Nvidia PhysX, but the core principles can be applied to any other platform and physics engine.
This demo shows most mistakes mentioned in this article in both a incorrect or broken state, and in a fixed state:
This is a simple scene with a ball that hits into a stack of barrels. The alternate version Incorrect Scale (10x) shows how the scene changes at 10x scale (see the scale markings on the floor of the demo). Notice how it appears to be in slow-motion, but this effect is simply caused by the scale of the scene.
This shows a simple side-scroller game working as intended. The "bad" alternative; Rigidbody & Character Controller Together shows the same game, but with a Rigidbody component attached to the character. Notice how the Rigidbody breaks the behavior of the Character Controller.
Objects With Bounciness
Barrels are shot from the side of the scene and are hurled into the air. As they come crashing down, they bounce off the ground and move in simple but majestic ways. The broken version shows the same scene without bounciness, and it looks so much more boring in comparison.
Not Directly Modifying a Rigidbody's Transform
In this scenario, a heavy ball is pushed up a ramp using
Rigidbody.AddForce(). In the second scenario, instead of using
Rigidbody.AddForce() to move the ball up the ramp,
Transform.position is used. The result of using
Transform.position is that the ball rolls back down, due to the fact that the rigidbody isn't properly taking into account the rigidbody's velocity change, but then the position modification causes the ball to jitter up and down the ramp.
In most games, players would assume that the scale of the world is relatable to Earth's scale. They would expect, for example, an enemy falling from a watch tower to fall at the same rate you would perceive on Earth. If the enemy falls too slowly or too quickly, it can detract from the immersion—particularly if the enemy is human-sized!
Nvidia PhysX in Unity is set up to use one unit per meter. You can use this trait to check whether the scale of your objects are correct by simply adding in a Unity primitive cube. The primitive cube is exactly one cubic meter. If you notice, for example, that a oil barrel in your scene is 2x bigger than the the cube, that means your oil barrel is two meters tall (6.56 feet)!
Fixing the scale is as easy as scaling every object in the scene. Just select all the objects in your scene and use the Scale tool to make them bigger or smaller. If you notice your objects are moving too quickly, make the objects larger. If you notice the opposite—that the objects move too slowly—you should scale the objects down.
You can scale your objects with more precision by grouping them into one null object, and scaling that one object. For example, setting the scale of the parent object to
1.2 on each axis will increase the size of every object within the object by 20%. You can also scale the objects in increments using the scale tool by holding down Ctrl-LMB (Windows) or Cmd-LMB (OS X).
Using a Rigidbody and a Character Controller Together
I've seen this happen a few times, and it actually makes sense how often this occurs. The developer assumes that a Character Controller is necessary to control their avatar, but they want the avatar to be affected by gravity and other objects in the environment.
The problem is that a Character Controller is designed for more classical controls, like those typically found in a platformer or first person shooter. A Rigidbody is simply a non-deformable object that is affected by gravity and other physical forces (such as other objects colliding with it). These are two very separate components, with different intended uses.
Choose only a Character Controller when you want to complete control over how the player moves. On the other hand, if you want your character to be driven by the physics engine, use a Rigidbody. In adding a Rigidbody to a character, you will probably want to constrain rotation so that the player doesn't topple over.
Directly Modifying a Rigidbody's Transform
Unlike a Character Controller, it's not good practice to set the position or rotation of a rigidbody, or scale a rigidbody object constantly (for player control and such). Instead, you should use the AddForce() and AddTorque() methods found in the Rigidbody class. It is okay to directly set the position and rotation of a Rigidbody if, for example, you're spawning in the object for the first time, or resetting the scene. In that situation it will be fine, as long as the Rigidbody is not intersecting other objects.
This matters because, when a rigidbody is moved to an exact position or rotational state, it could pass through an object. The physics engine then has to correct this issue, and most of the time the physics engine does not run at the same time Unity's
Update() message does. The end result is jittery behaviour whenever there's an intersection, and it's possible that the rigidbody may pass through objects entirely.
Another bad side effect that can occur when, say, moving a rigidbody along an axis for player movement is that internally the rigidbody is simulated and then it applies it's position. Updating the position then moves the rigidbody without taking into account the velocity change and such. If the rigidbody is rolling back down a slope, it will be moving the rigidbody backwards while your position altering code is moving the rigidbody back up the slope.
Objects Rolling Forever
Let's say you're developing a golf game. There is a problem with how the golf ball does not stop rolling, and somehow manages to continue rolling on forever as long as there isn't any kind of hole or ditch in its path. The reason why this happens is because in real life, the ball would be slowed down by the grass it runs over (among other things), since it has to push the tiny grass blades down, the grass essentially is like a constant ramp. This is called rolling resistance. Unity cannot simulate this behaviour accurately, so instead artificial stopping forces must be used.
In Unity the best force to use to stop an object from rolling forever is "angular drag". Changing the angular drag on the golf ball is the way to fix this issue. The exact value really depends on the behaviour you're looking for, however you may notice that a value of 1.0 angular drag may not even be enough in some cases.
Objects Without Bounciness
Almost every object in the world bounces after an impact. Unity's internal, default physics material has no bounce at all. Meaning every object will not bounce unless your override the default physics material or apply a physics material to the objects in your scene with a bounciness value higher than 0.
One of the best ways on fixing this issue is by creating a creating your own default physics material and assigning it in the Physics Manager found by clicking Edit > Project Settings > Physics.
Rigidbodies Partially Sinking Into Geometry
Most physics engines have some kind of parameter dictating how much two objects can be interpenetrating or intersecting until they are pushed away from one another. This parameter is called Min Penetration For Penalty in Unity. By default this value is 0.01 (meters), meaning that, by default, objects can be intersecting up to 1 centimeter (almost 0.4 inches) before being pushed apart.
You should set Min Penetration For Penalty to a value where it is barely noticeable that objects are intersecting. Setting the value to something tiny, such as
0.0001, may result in jittery rigidbodies.
How to Prevent Mistakes
Writing Code for Rigidbodies (for Programmers)
If you aren't a programmer, you don't have to worry about the following scenario. When writing code that moves, rotates, or scales rigidbodies, it is important to keep this in the FixedUpdate loop. Writing this code in the
Update loop will potentially lead to unstable results, since the
Update function may be called at 1000 Hz, while the physics engine and the
FixedUpdate function are each called at 50 Hz by default.
You can change the frequency on physics steps by changing the parameter Fixed Timestep, found in Edit > Project Settings > Time. The value determines how much time is waited, in seconds, between each physics update or step. You can work out the frequency in Hertz by dividing 1 by the value (for instance, a 0.01 second wait means 1 / 0.01 = 100 Hz). The more frequent the steps, the more accurate and stable the simulation will be. However, setting the frequency higher than the CPU can handle will result in a very unstable simulation. Try to keep the Fixed Update frequency between 30 Hz and 100 Hz.
While working on a destructible brick wall, I ran into an issue caused by Instantiating bricks after a chunk of the wall had been destroyed. I fixed this issue by placing the problematic code into a Coroutine, and placing the following line before Destroying the object:
// Wait a frame yield return null; // C# yield; // UnityScript
In waiting for a frame, it would guarantee that the logic was synced up in Update time, rather than FixedUpdate time. This seems to mean that the Destroy function is executed in sync with the Update loop.
Bonus Unity Tip: Don't Use The Standard Assets Physics Materials!
The Physics Materials package, which comes as part of the Unity Standard Assets, is actually almost completely useless. There are five physics materials contained in the package, and all of them are unrealistic in some way.
Each material has identical static and dynamic friction. In the real world, objects that are standing still have slightly more friction then when they are moving. The Rubber material's coefficient of friction is
1.0, which is not similar to any rubber found in the real world. And if that doesn't sound silly enough, every material has 0 "bounciness" (excluding the "Bouncy" material). This all means that the materials not even a close representation of their real life counterpart.
It is best to create your own physics materials when necessary. There are a lot of websites around that share physical properties of materials—the important ones being dynamic friction, static friction, and restitution or bounciness.
Very few physics related problems are actually that difficult to fix. If there is any kind of physics-related bug that seems difficult to track down, try slowing down time to see what's going on. If you notice that the issue starts around a certain line of code, you can use Debug.Break to pause the editor and inspect what's going on. Feel free to comment here if you have any questions or need help.