Physics performance optimizations

Now that we have an understanding of the most significant features of the Unity Physics Engine, we can cover some optimization techniques to improve our game's physics performance.

Scene setup

Firstly, there are a number of best practices we can apply to our Scenes to improve consistency of the physics simulation. Note that these techniques will not necessarily improve CPU or memory usage, but they will result in a reduced likelihood of instability from the Physics Engine.

Scaling

We should try to keep all physics object scales in the world as close to 1:1:1 as we possibly can. This means that for the default gravity value of -9.81, the unit scale of the world is implied to be 1 meter per unit, since the force of gravity at the surface of the Earth is 9.81 m/s² (most games are trying to simulate this situation). Our object sizes should reflect our implied world scale, since scaling them too large will cause gravity to appear to move the objects much more slowly than we would expect. The converse is also true; scaling objects too small will make them appear to fall too quickly and will not seem realistic.

We can tweak the world's implied scale by modifying the strength of gravity under Edit | Project Settings | Physics (or Physics2D) | Gravity. However, be aware that any floating-point arithmetic will be more accurate with values closer to 0, so if we have some objects that have scale values far above (1,1,1), even if they match the implied world-scale, then we could still observe erratic physics behavior. So, early in the project we should import and scale our most common physics objects around a scale value of (1,1,1), and then adjust the value of gravity to match. This will give us a reference point to work with as we introduce new objects.

Tip

Be warned that Unity 4 also has a value for Speed of Sound in its Audio settings, which is used during any Doppler-based audio effects. The default value is 343 to match the speed of sound in air of 343 ms-1. Changing the implied world scale via gravity will require this value to be adjusted to maintain consistency. Unity 5 calculates the Doppler Effect differently and so this variable was removed, no longer making this issue a concern.

Positioning

Similarly, keeping all objects close to (0,0,0) in position will result in better floating-point accuracy, improving the consistency of the simulation. Space Simulator and Free-Running games try to simulate incredibly large spaces, and they typically use this technique as much as possible by secretly teleporting, (or simply keeping) the player character centered in the world. At this point, either everything else is moved to simulate travel, or volumes of space are compartmentalized so that physics calculations are always calculated with values close to zero. This ensures that all objects remain close to (0,0,0) in order to avoid floating-point inaccuracies as the player travels great distances.

Other types of games should not risk introducing floating-point inaccuracy, so unless we're already deep into our project (such that changing and re-testing everything at a late stage would be too much hassle), we should try to keep all of our physics objects close to (0,0,0). Plus, this is simply good practice for our project workflow as it makes it much faster to add and position objects in our game world.

Mass

The Unity documentation recommends that object mass values stay around 0.1, with no values exceeding 10, due to the instability it generates: http://docs.unity3d.com/ScriptReference/Rigidbody-mass.html.

This means we should not think of mass in terms of measurements such as pounds or kilograms, but rather a relative value between objects. We should try to maintain consistent and reasonable ratios of mass between colliding objects. Having objects colliding with a mass ratio of greater than 1,000 will most certainly result in erratic behavior due to the large momentum difference and eventual loss of floating point precision. We should try to ensure inter-object collisions occur with objects with similar values of their mass property, and object pairs that have a significant scale difference should be culled with the Collision Matrix (more on this shortly).

Tip

Improper mass ratios are the most common cause for physics instability and erratic behavior.

Note that the force of gravity at the centre of the Earth affects all objects equally, regardless of their mass, so it does not matter if we consider a mass property value of 1 to be the mass of a rubber ball or the mass of a warship. There's no need to adjust the force of gravity to compensate for any assumptions we're making with these relative mass property values. What does matter, however, is the amount of air resistance the given object undergoes while falling (this is why a feather falls slower than a solid object of identical mass). So, to maintain realistic behavior, we may need to customize the drag property for such objects or customize the force of gravity on a per-object basis (such as disabling the Use Gravity checkbox and applying a custom gravitational force via Script code).

Use Static Colliders appropriately

As previously mentioned, the Physics System automatically generates a data structure from the data of all Static Colliders (colliders without Rigidbody objects) separately from the structure that manages Dynamic Colliders (colliders with Rigidbody objects). Unfortunately, if new objects are introduced into the data structure at runtime, then it must be regenerated. This is likely to cause a significant CPU spike. This makes it vital that we avoid instantiating new Static Colliders during gameplay.

In addition, merely moving, rotating, or scaling Static Colliders also triggers this regeneration process and should be avoided. If we have colliders that we wish to move around without reacting to other objects colliding with them in a physical way, then we should attach a Rigidbody to make it a Dynamic Collider and set the Kinematic flag to true. This flag prevents the object from reacting to external impulses from inter-object collisions. This allows it to behave similar to a Static Collider, but it is now in a data structure that properly supports moving objects, so we can move it around from Script code (during Fixed Updates!), and it will apply impulses to other objects.

Tip

It's for this reason that the Kinematic flag is often used on objects controlled by players; they can push other objects around but shouldn't be pushed around themselves.

Optimize the Collision Matrix

As we know, the Physics System's Collision Matrix defines which objects assigned to certain layers are allowed to collide with objects assigned to other layers. Or to put it more succinctly, which object pairs are even considered by the Physics System. Every other object-layer-pair is simply ignored by the Physics Engine, which makes this an important avenue for minimizing Physics Engine workload since it reduces the number of bounding-volume checks that must be performed at each and every timestep.

Tip

Reminder: the Collision Matrix can be accessed through Edit | Project Settings | Physics (or Physics2D) | Layer Collision Matrix.

The following screenshot shows a common Collision Matrix for an arcade shooter game:

Optimize the Collision Matrix

In this example, we have minimized the number of possible inter-object collisions for the Physics System to check. Since powerups can only be picked up by the player, then there is no need to compare collisions between powerups and objects from any other layers. Meanwhile, we don't wish for projectiles to collide with the object that fired it, which excludes Enemy Projectiles from colliding with enemies, and Player Projectiles from colliding with the player. We want everything to collide with the game world (walls and other surfaces), and perhaps we don't want projectiles colliding with other projectiles (although some games might want this!).

We should perform logical sanity checks like this for all of the Layer combinations in the Collision Matrix to see whether we're wasting precious time checking for inter-object collisions between object-pairs that aren't necessary.

Prefer discrete collision detection

We should use the Discrete option by default for the majority of objects. Teleporting objects once and performing a single overlap check between nearby object-pairs is a fairly trivial amount of work. However, the amount of calculation it takes to interpolate the objects between their starting and ending positions, and simultaneously verify any slight bounding-volume overlaps between these points over time, is significantly greater.

Consequently, the Continuous collision detection option is an order of magnitude more expensive than the Discrete detection method, and the ContinuousDynamic collision detection setting is an order of magnitude more expensive than Continuous! Having too many objects of the continuous types will cause serious performance degradation in complex Scenes. In either case, the costs are multiplied by the number of objects that need to be compared during any given frame and whether or not the comparison collider is Static or Dynamic.

Ergo, the continuous detection settings should only be used in extreme circumstances. The Continuous setting should be used when important collisions are frequently missed with the static world, for instance, if we expect certain objects to move quickly, and we wish to be certain they never fall through the game world or teleport through walls. Finally, the ContinuousDynamic setting should only be used if the same situation applies but we wish to catch collisions between pairs of very-fast moving Dynamic Colliders. Unless we have good reason to use them, all other situations should favor the Discrete setting.

But, perhaps, the Discrete setting isn't working well-enough on a large scale. Perhaps our entire game revolves around a lot of small physics objects and discrete collision detection simply isn't catching enough collisions to maintain product quality. Well, we're in luck, because we can customize the physics timestep to give the Discrete collision option a better chance of catching such collisions by modifying how frequently the engine checks for Fixed Updates.

Modify the FixedUpdate frequency

As mentioned previously, Fixed Updates and physics timestep processing are strongly-coupled, so by modifying the frequency of Fixed Update checks we not only change the frequency that the Physics System will calculate and resolve the next callback, but we will also change how frequently FixedUpdate() callbacks are being invoked. Consequently, changing this value can be risky if we're deep into our project and have a lot of behavior that depends on these callbacks.

Altering the FixedUpdate frequency can be accomplished through the Edit | Project Settings | Time | Fixed Timestep property in the Editor, or through the Time.fixedDeltaTime property in script code.

Modify the FixedUpdate frequency

Reducing this value (increasing the frequency) will force the Physics System to process more frequently, giving it a better chance of catching collisions between our Dynamic, Discrete objects. Naturally, this comes with a CPU cost since we're invoking more FixedUpdate() callbacks, as well as asking the Physics Engine to update more frequently, having it move objects and verify collisions more often.

Conversely, increasing this value (decreasing the frequency) provides more time for the CPU to complete other tasks before it must handle physics processing again, or looking at it from another perspective, giving the Physics System more time to process the last timestep before it begins processing the next one. But, lowering the FixedUpdate frequency would essentially lower the maximum velocity at which objects can be moving before the Physics System can no longer capture collisions between discrete objects (depending on the objects' sizes).

This makes it absolutely vital to perform a significant amount of testing each time the Fixed Timestep value is changed. Even with a complete understanding of how this value works, it is difficult to predict what the overall outcome will look like during gameplay, and whether the result is passable for quality purposes. Hence, changes to this value should be made early in the project's lifecycle and then made infrequently in order to get a sufficient amount of testing against as many physics situations as possible.

It might help to create a test Scene that flings some of our high-velocity objects at one another to see if the results are acceptable, and run through this Scene whenever Fixed Timestep changes are made. But actual gameplay tends to be rather complex, with many background tasks and unanticipated player behavior that causes additional work for the Physics System, or gives it less time to process the current iteration. Actual gameplay conditions are difficult to duplicate in a vacuum, and there's no substitute for the real thing, so the more testing we can accomplish against the current value of Fixed Timestep, the more confident we can be that the changes meet acceptable quality standards.

We always have the continuous collision detection options as a last resort to offset some of the resulting instability we're observing. But unfortunately, even if the changes are targeted, it is more likely that this will cause further performance issues than we started with due to the overhead costs of continuous collision detection. It would be wise to profile our Scene before and after enabling continuous collision detection to verify that the benefits are outweighing the costs.

Adjust the Maximum Allowed Timestep

If the Maximum Allowed Timestep value gets hit regularly, then it will result in some pretty bizarre-looking physics behavior. Rigidbody objects will appear to slow down or freeze in space, since the Physics Engine needs to keep exiting early before it has fully resolved its entire time quota. In this case, it is a clear sign that we need to optimize our physics from other angles. But at the very least we can be confident the threshold will prevent the game from completely locking up during a physics processing spike.

This setting can be accessed through Edit | Project Settings | Time | Maximum Allowed Timestep. The default setting is to consume a maximum of 0.333 seconds, which would manifest itself as a very noticeable drop in frame rate (a mere 3 FPS) if it were breached. If we ever feel the need to change this setting, then we obviously have some big problems with our physics workload, so it is recommended to only tweak this value if we have exhausted all other approaches.

Minimize cast and bounding-volume checks

All of the raycasting methods are incredibly useful, but they are relatively expensive (particularly CapsuleCast() and SphereCast()), requiring us to minimize how often they are called. We should avoid calling these methods regularly within Update callbacks or Coroutines, saving them only for key events in our script code.

If we absolutely must rely on persistent line, ray, or area-of-effect collision areas in our Scene (examples include security lasers, or continuously burning fires), then they would be better simulated using a simple Trigger Collider, rather than performing continuous raycasting or overlap checks.

If such replacements are not possible, and we truly need persistent casting checks using these methods (such as a red-dot laser sight), then we can minimize the amount of processing each call makes by exploiting the LayerMask objects.

For example, a lazy raycast would look like so:

[SerializeField] float _maxRaycastDistance;

void PerformRaycast() {
    RaycastHit hitInfo = new RaycastHit();
      if (Physics.Raycast(new Ray(transform.position, transform.forward), out hit, _maxRaycastDistance)) {
        // handle raycast result here
      }
}

But this overload of Physics.Raycast() will cause the ray to collide with the first object of any layer in its path. The Physics.Raycast method has multiple overloads that accept a LayerMask object for an argument. We can use this to customize which objects should be checked by the raycast, simplifying the workload for the Physics Engine:

[SerializeField] float _maxRaycastDistance; 
[SerializeField] LayerMask _layerMask;

void PerformRaycast() {
    RaycastHit hitInfo = new RaycastHit();
      if (Physics.Raycast(new Ray(transform.position, transform.forward), out hit, _maxRaycastDistance, _layerMask)) {
        // handle raycast result here
      }
}

The LayerMask object can then be configured through the object's Inspector view:

Minimize cast and bounding-volume checks

Tip

Note that, because the RaycastHit and Ray classes are managed by the native memory space of the Unity Engine, they don't result in memory allocations that draw the attention of the Garbage Collector. We will learn more about such activity in Chapter 7, Masterful Memory Management.

Avoid complex Mesh Colliders

In order of efficiency, the various colliders are: Spheres, Capsules, Cylinders, Boxes, Convex Mesh Colliders, and finally Concave Mesh Colliders. However, the four main primitives are an order of magnitude more efficient than either of the Mesh Colliders, as the mathematical calculations for detecting and resolving collisions between them are fairly succinct and optimized. Performing comparisons between a convex shape and another collider can be a costly process, while comparing between a concave shape and anything else is even more expensive.

Note

Performing runtime bounding-volume checks between pairs of concave shapes is perhaps the closest thing to "Mathematical Armageddon" that we might find in a real-time physics simulation (at least for a few more years!). To protect us from our own stupidity, Unity effectively bans us from performing concave-to-concave Mesh Collider bounding volume checks.

A great irony between physics and graphics in 3D applications is how difficult it is to handle spherical and box objects between the two of them. The perfect spherical mesh would require an infinite number of polygons to generate, but how a sphere is represented in a Physics Engine is relatively trivial to resolve for contact points and collisions. Conversely, a simple box takes a miniscule number of polygons and effort to produce graphically, and yet takes significantly more mathematics and processing power to find contact points and resolve collisions for. This implies that getting the most out of both graphics and physics would be to populate our world with low-polygon graphical objects, represented as spheres within the Physics System. However, this would make absolutely no sense to a human observer as they witness sharp, pointy objects rolling around like balls.

It is always important to remember when working with Physics Engines that the physical representation of an object does not necessarily need to match its graphical representation. This is beneficial as a graphical mesh can often be condensed down into a much simpler shape, generating a very similar physics behavior and sparing us the need to use an overly-complex Mesh Collider.

This separation of representations between graphics and physics allows us to optimize the performance of one system without (necessarily) negatively affecting the other. If there would be no noticeable repercussions on gameplay, then we are free to represent complex graphical objects with the simplest physics shapes behind-the-scenes. If the player never notices, then no harm is done!

So, we can solve this problem in one of two ways: either by approximating the physics behavior of the complex shape using one (or more) of the standard primitives, or a using a much simpler Mesh Collider.

Use simpler primitives

Most shapes can be approximated using one of the four primitive colliders (in order of efficiency): Spheres, Capsules, Cylinders, or Boxes. In fact, we do not need to represent the object using only a single collider; we are free to use several colliders if it serves our needs for creating a complex collision shape by attaching additional child GameObjects with their own colliders. This is almost always less costly than using a single Mesh Collider and should be preferred over more complex solutions.

The following image shows a handful of complex graphical objects, represented by one or more simpler primitives in the Physics System:

Use simpler primitives

Using a Mesh Collider for any one of these objects would be significantly more costly than the primitive colliders shown here. It is worth exploring any and all opportunities to simplify our objects down using these primitives as much as we can, as they can provide significant performance gains.

For example, concave meshes are unique in that they can feature gaps, or holes, that allow other meshes to "fall" into, or even through them, which introduces opportunities for the objects to fall through the world if concave shapes are used for world collision areas. It is often better to place Box Colliders in strategic locations for this purpose.

Use simpler Mesh Colliders

The mesh assigned to the Mesh Collider does not necessarily need to match the graphical representation of the same object (Unity simply picks it as the default). This gives us an opportunity to replace the Mesh Collider's mesh property with a different, simpler mesh for an object's collider from the one we use for its graphical representation.

The following image shows an example of a complex graphical mesh that has been given a simplified mesh for its Mesh Collider:

Use simpler Mesh Colliders

Simplifying the rendered mesh into convex shapes with lower polygon counts in this way will greatly reduce the overhead needed to determine bounding-volume overlaps with other colliders. Depending on how well the original object is estimated, there may be few to no noticeable gameplay differences, especially in the case of this axe, which we expect to be moving quickly as creatures swing it during attacks, making it unlikely that players will notice the difference between the two meshes as colliders.

Avoid complex physics components

Certain special physics Collider Components, such as Terrain, Cloth, and Wheel Colliders, are orders of magnitude more costly than all primitive colliders, and even Mesh Colliders in some cases. We should simply not include such Components in our Scenes unless they are absolutely necessary. For instance, if we have Terrain objects in the distance that the player will never approach, then there's little reason to include an attached Terrain Collider.

Games featuring Cloth Components should consider instantiating different objects without them when running in lower-quality settings, or simply animating cloth behavior (although it is totally understandable if the team has grown attached and fallen in love with how the stuff moves around).

Games using Wheel Colliders should simply try to use fewer wheel colliders! Vehicles with more than four wheels may be able to use only four wheels to generate the correct physics behavior, while faking the graphical representation of additional wheels.

Let physics objects sleep

The Physics Engine's sleep feature can pose several problems for our game.

Note

Reminder: the sleep threshold can be modified under Edit | Project Settings | Physics | Sleep Threshold.

Firstly, some developers don't realize that many of their Rigidbody objects are sleeping during most of the lifetime of their application. This tends to lead developers to assume that they can get away with (for example) doubling the number of Rigidbody objects in their game, and the costs will simply double to match it. This is unlikely. The frequency of collisions and total accumulated time of active objects is more likely to increase in an exponential fashion, rather than a linear one. This leads to unexpected performance costs when new physics objects are introduced into the simulation. We should keep this in mind when we decide to increase the physics complexity of our Scenes.

Secondly, there is the danger of "islands" of sleeping physics objects being generated. Islands are created when a large number of Rigidbody objects are touching one another, and have gradually come to rest—imagine a pile of boxes that have been spawned and formed a large pile. Eventually, all of the Rigidbody objects will fall asleep once enough energy in the system is lost and they all come to rest. However, because they're all still touching one another, as soon as one of these objects is awoken, it will start a chain reaction, awakening all other nearby Rigidbody objects. Suddenly we have a large spike in CPU usage because dozens of objects have now re-entered the simulation, and there are suddenly many more collisions to resolve until the objects fall asleep again.

If we could find a way to detect that islands are forming, we could strategically destroy/despawn some of them to prevent too many large islands from being generated. But workarounds such as these will be dependent upon the game itself, as performing regular, global checks, and distance comparisons between all of our Rigidbody objects is not a cheap task to accomplish. For example, a game that requires the player to move lots of physics objects into an area (for example, a game that involves herding sheep into a pen) could choose to despawn the Dynamic Collider object as soon as the player moves it into position, locking the object to its final destination, and easing the workload on the Physics Engine.

Thirdly, changing any of a Rigidbody's properties at runtime, such as mass, drag, Use Gravity, and so on, will also reawaken the object. If we're regularly changing these values (such as a game where object sizes and masses change over time), then they will remain active for longer periods of time than usual. This is also the case for applying forces, so if we're using a custom gravity solution (such as suggested back in the section entitled Mass), we should try to avoid applying the gravitational force every Fixed Update, otherwise the object will be unable to fall asleep.

Sleeping objects can be a blessing and a curse. They can save us a lot of processing power, but if too many of them reawaken at the same time or our simulation is too busy to allow enough of them to fall asleep, then we could be incurring some unfortunate performance costs during gameplay. We should strive to limit these situations as much as possible by letting our objects enter the sleeping state as much as possible, and avoid grouping them together in large clusters.

Modify Solver Iteration Count

Joints, Springs, and other connected Rigidbody objects are not trivial to simulate in the world of Physics Engines. Because of the co-dependent interactivity (internally represented as movement constraints) that occurs with joining two objects together, the system must often make several attempts at solving the necessary mathematical equations. This multi-iteration approach is required to calculate an accurate result whenever there is a change in velocity to any piece of the object-chain.

It therefore becomes a balancing act of limiting the maximum number of attempts the "Solver" makes to resolve a particular situation, versus how accurate a result we can get away with. We don't want the Solver to spend too much time on a single collision, because there is a lot of other work the Physics Engine has to do within the same iteration. But, we also don't want to reduce the maximum number of iterations too far as it will only approximate what the final solution would have been if it had been given more time to calculate the result.

Note

The same Solver also gets involved when resolving inter-object collisions and contacts. It can almost always determine the correct result with a single iteration, with the exception of some very rare and complex collision situations. It is only when third-party objects will be affected through Joints that the Solver requires additional effort to mathematically integrate the final result.

The Solver Iteration Count can be modified under Edit | Project Settings | Physics | Solver Iteration Count. In most cases, the default value of six iterations (seven iterations in Unity 4) is perfectly acceptable. But, games that include very complex joint systems may wish to increase this count to suppress any erratic (or downright explosive) Character Joint behaviors, while some projects may be able to get away with reducing this count. Testing must be performed after changing this value to see whether the project still maintains the intended levels of quality.

Incidentally, if we find our game regularly runs into jarring, erratic, and physics-breaking situations with complex Joint-based objects (such as ragdolls), then we should consider gradually increasing the Solver Iteration Count until the problems have been suppressed. These problems typically occur if our ragdolls absorb too much energy from colliding objects and the Solver is unable to iterate the solution down to something reasonable before it is asked to give up. At this point, one of the Joints goes supernova, dragging the rest of them into orbit along with it!

We should also double check that our ragdoll's Rigidbody masses are obeying the rules set forth earlier in this chapter, so that the resultant energy exchange and velocities will be more reasonable.

Optimizing ragdolls

Speaking of Character Joints, ragdolls are incredibly popular features for good reason; they're tons of fun! Ignoring the morbidity of flinging corpses around a game world for the moment, there's something about watching a complex chain of objects flail around and smash into things that hits a lot of psychological "fun buttons".

This makes it very tempting to allow many ragdolls to coexist within our Scene at the same time, but as we quickly discover, this risks an enormous performance hit when too many ragdolls are in motion and/or collide with other objects due to the amount of iterations the Solver would need to resolve them all. So, let's explore some ways to improve the performance of any ragdolls we wish to use.

Reduce Joints and Colliders

Unity provides a simple ragdoll-generation tool under GameObject | 3D Object | Ragdoll… in Unity 5, or GameObject | Create Other | Ragdoll… in Unity 4. This tool can be used to create ragdolls from a given object by selecting the appropriate child objects to attach colliders to for any given body part or limb. This tool always creates 11 different colliders and associated Joints (pelvis, chest, head, and two colliders per limb), but we might wish to consider using only six colliders (body, head, and one collider per limb) to greatly reduce the overhead cost at the expense of ragdoll realism. This can be achieved by deleting unwanted colliders and reassigning the Character Joint's Connected Body properties to the proper parent joints.

Such simplifications could be implemented as a means of reducing overhead for weaker hardware/lower quality settings, as a simple compromise to allow more ragdolls to coexist in our Scene. It could even be used dynamically if a particular number of ragdolls are already present. A god class would need to keep track of how many ragdolls currently exist, but when new ragdolls are introduced, we could instantiate a simpler version in order to keep things running smoothly.

Avoid inter-ragdoll collisions

Unless we really desire inter-ragdoll collisions then we should perhaps disable them using the Collision Matrix. The performance cost of ragdolls grows exponentially when ragdolls collide with one another, since we would be asking the Solver to work extra hard and risking erratic behavior due to the approximations it is forced to make.

Disable or remove inactive ragdolls

Finally, we could consider disallowing ragdolls from re-entering the physics simulation after they have come to rest. In some games, once a ragdoll has reached its final "destination," we no longer need it to remain in the game world as an interactable object.

We can poll any given collider's sleep state with the IsSleeping method and, once it has reached this state, we have a number of options we can pursue. We could disable all of the ragdoll's colliders, preventing it from being reintroduced to the simulation, we could remove the ragdoll from the Scene for the sake of cleanup, or we could keep track of this ragdoll and only remove it when a different ragdoll has been introduced to the Scene.

Whatever approach we choose to improve the performance of our ragdolls will no doubt result in limiting ragdolls as a gameplay feature, either by instantiating fewer of them, giving them less complexity, or giving them a shorter lifetime, but these are reasonable compromises to make given the performance-saving opportunities.

Know when to use physics

As always, the best method of improving the performance of a feature is to avoid using it as much as possible. For all moveable objects in our game, we should take a moment to ask ourselves if getting the Physics Engine involved is even necessary. If not, we should look for opportunities to replace them with something simpler and less costly.

Perhaps we're using physics to detect whether the player fell into a kill-zone, but our game is simple enough that we only have kill-zones at a specific height. In this case, we could avoid physics colliders altogether and get away with only checking whether the player's y position falls below a particular value.

As another example, maybe we're trying to simulate a meteor shower, and our first instinct was to have many falling objects that move via physics Rigidbody objects, detect collisions with the ground via colliders, and then generate an explosion at the point of impact. But perhaps the ground is consistently flat or we have access to the Terrain's Height Map for some rudimentary collision detection. In this case, object travel could be simplified by tweening the objects' transform.position properties over time to simulate the same behavior without requiring any physics components. In both cases, we can reduce the physics overhead by simplifying the situation and pushing the work into Script code.

Tip

Tweening is a common short-hand term for in-betweening, which is the act of interpolating properties from one value to another mathematically over time. There are many useful (and free!) tweening libraries, available on the Unity Asset Store, that provide a lot of useful functionality.

The reverse is also possible; there might be occasions where we're performing a great deal of calculation through Script code that could be handled through physics relatively simply. For example, we might have implemented an inventory system with many objects that can be picked up. When the player hits the Pick up key, each of these objects might be compared against the player's position to figure out which object is closest. We could consider replacing all of the Script code with a single Physics.OverlapSphere() call to get nearby objects when the key is pressed, and then figure out the closest pickup object from the result (or just pick up all of them!). This could greatly reduce the total number of objects that must be compared each time the key is pressed.

The opportunities are as wide and far-reaching as our own ingenuity. The ability to recognize opportunities to remove unnecessary physics grunt-work from our Scenes, and/or use physics to replace behavior that is costly when performed through Script code, is a vital skill that will serve us well when saving performance in current and future game development projects.

Consider upgrading to Unity 5

If we're running Unity 4, an absolute last resort to improve physics performance would be to consider upgrading to Unity 5. There were a multitude of huge performance enhancements with Unity 5's upgrade from PhysX version 2.8.3 to version 3.3. These improvements cannot be overstated as they grant about double the performance of the Physics System as compared to Unity 4. The upgrade includes less overhead in moving Static Colliders, improved performance in continuous collision detection methods, support for more rigid bodies, improved Cloth and Wheel Collider components, as well as multicore physics support. In short, it allows us to reduce the performance cost of the same Scene, or cram more physics activity into our Scenes for the same cost.

However, these changes resulted in some significant API changes for certain tasks, which means Scripting and Components in Unity 4 may not be fully compatible with Unity 5 (and not just for physics-related tasks). Consequently, the upgrade itself is unlikely to be trivial, and we should not forget to make a backup of our project before attempting to do so. The amount of work will ultimately depend on the complexity of our project and how it affects any Asset Store purchases we rely on. Each asset is likely to need updates of its own and any assets that have fallen out of support will need to be replaced.

..................Content has been hidden....................

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