Search Unity

Improving and expanding use cases: Physics changes in Unity 2022.2

Improving and expanding use cases: Physics changes in Unity 2022.2 | Hero image
Improving and expanding use cases: Physics changes in Unity 2022.2 | Hero image
Share

Is this article helpful for you?

Thank you for your feedback!

Any successful tool needs to keep evolving and adapting to its new usages. With this in mind, the 2022.2 Tech Stream delivers changes to existing physics functionality in the Unity Editor, each of which is aimed at improving fitness for existing use cases while opening new ways of using the same approach.

The evolution of tools: Highlights

Physics queries

Physics queries, such as Physics.Raycast, are multipurpose tools that can discover physics objects and their overlaps at certain locations. This overlap information serves as input for application-specific logic, such as sensors, custom physics forces, and more. It’s at the core of many applications, so the 2022.2 updates bring a set of improvements to boost the efficiency of queries even further by allowing Unity to run more query types as batch queries.

The physics queries were easy to start using, but they had one particular disadvantage that only became apparent when simulations were scaled to more bodies, more colliders and more custom agents: they can only run on the thread they are invoked from (that is, the main thread). Because of this, running a lot of queries tends to create a performance bottleneck, even though each individual query is fast. To address this, we built batched physics queries which expose a new buffer. You can add the physics queries to this new buffer, which can then schedule the queries to run on other threads while something else is being computed on the main thread.

Prior to this release, batched physics queries could only run queries that return a single hit each time, severely limiting the possible use cases. Now, we’re introducing a way to run queries that return multiple hits.

To understand how it works, let’s take the batched raycast as an example. Comparing the RaycastCommand class between 2022.1 and 2022.2, you may notice that the layerMask and maxHits parameters have disappeared, and a new queryParameters parameter has been added. The layerMask parameter is now part of a new structure that holds more parameters than ever before, including a new option that allows the query to produce multiple hits from a single mesh. The maxHits parameter has moved from the command constructor to the batch schedule function. With this change, there's no need to iterate the commands array at the time the batch is scheduled, so it's possible to postpone memory initialization. This can be useful when chaining jobs, because the other job can now fill up the commands buffer when it has data.

Of course this is not only exclusive to raycasts. All of the other batched queries were updated, too: Both shape casts and shape overlaps can now return multiple hits per command.

Contacts

Unity provides access to the set of current physics contacts through three OnContact callbacks that the user code has to implement. These three callbacks send notifications about a new contact, a persisting contact, and a lost contact. This was a great modular approach in the early days of Unity, allowing flexibility and usability.

However, as Unity projects grew larger and more sophisticated, it was clear that the approach didn’t scale well. Invoking these callbacks from native code and marshaling the contacts data to the managed code caused suboptimal garbage collector (GC) memory usage patterns (largely solved in the 2018.3 Tech Stream by reusing the Collision class instance when invoking said callbacks). It also caused significant performance issues that were previously unsolved, rendering larger-scale contact collection nearly impossible for larger projects.

Some time ago, while working on the contacts modification feature, our team noticed that it was about four times faster to read contacts via the threaded contact modification interface rather than the traditional OnContact callbacks. Threading aside, the root cause for such a dramatic performance boost was contact modification providing scripts with direct access to the contact data array.

Direct access to the contact data array results in an increase in performance because:

  • You don’t need to create Collision class instances per each contact pair.
  • You can iterate contacts completely in the managed code, whereas the OnContact approach relied on the native code invoking a managed function to receive contact data for each contact pair.

Native-to-managed invocations don’t scale very well. Inspired by this finding, the current release brings a new, faster way to read contact data, based on contact modification processes.

To access the contacts, subscribe to the new Physics.ContactEvent event. It’s invoked once for each physics scene, and provides access to all of the current contact pairs in a large, continuous array. Accessing elements of this array is a fast direct memory operation that doesn’t require passing anything between the managed and native contexts. In addition, you can iterate contacts using C# jobs in order to gain advantage of multiple cores. That’s why it’s so much faster than traditional OnContact callbacks.

Speaking of callbacks, we also reimplemented the traditional callback path in C#. It now relies on Physics.ContactEvent internally. This reduces the number of times that control has to bounce between managed and native to drive some acceleration for existing projects that are not yet upgrading to the new way. This does not affect the functionality of callbacks themselves.

Collision override

Unity uses a layer-based collision system. When the physics system discovers a contact between two Colliders, it uses the Layer Collision Matrix to check whether they are actually allowed to collide or not. There is also an additional script function, Physics.IgnoreCollision, that allows you to mark specific Collider pairs as ignoring each other.

The downside of this approach is that a GameObject can only belong to a single layer, and there can only be 32 layers in total (keep in mind that these are general-purpose layers used by other subsystems, such as rendering). This configuration means that the system is not very flexible.

To improve flexibility, we’ve added a simple way to override the layer settings per Collider and per Rigidbody.

BoxCollider with the Layer Overrides section expanded.
BoxCollider with the Layer Overrides section expanded

With this change, any physics object can now override the layer matrix setting, and specify explicitly which layers to collide with and which layers to ignore.

Simulation update

By default, physics simulation happens automatically at a fixed frequency, as set by the Time.fixedDeltaTime property. In the 2017.1 release, we exposed the Physics.Simulate method, which allows you to have manual control over the simulation. You can now use any time-stepping mechanism that makes sense for your application, design custom reactions to time spikes, and much more. This feature also includes an Editor setting that controls whether physics is run automatically, or by a script.

In this latest release, we’ve added a new option to the set that enables physics to be simulated every game tick, with variable delta time. Simulating physics every tick has an advantage of never needing to use interpolation, because the motion is always smooth. Additionally, the stepping scheme is really simple – no fixed delta time catchup is necessary.

The possible downside is that such a simulation can be nondeterministic or inaccurate because, for numerical algorithms, a smooth, small delta is often required. For this reason, we recommend this mode only for projects that maintain a high and stable framerate.

The new simulation mode is available in the Physics settings.
The new simulation mode is available in the Physics settings.

Additionally, you can now use interpolation and extrapolation when using extra physics scenes. We exposed a new method, PhysicsScene.InterpolateBodies, to apply interpolation and extrapolation if needed.

Addressing user feedback

We always aim to include as many user-requested changes as possible in new releases. Here, we’ve compiled a list of changes that were requested through the forums by early adopters:

  • Joint drive force application modes: Regular joints have a new Use Acceleration property. When enabled, accelerations are set to both of the connected bodies, instead of forces. This mode is easier to configure in cases where the connected bodies have different masses. The drives of ArticulationBody can be set to apply forces or accelerations, and to set the target position or target velocity directly.
ArticulationBody where the X Drive section changes based on the Drive Type setting.
ArticulationBody where the X Drive section changes based on the Drive Type setting
  • HingeJoint range: There is a new property, HingeJoint.extendedLimits, which extends the range of the HingeJoint. Previously, without this option set, the range was [-180, 180]. With the new option set, it is [-360, 360].
  • Center of Mass and Inertia Tensor: Both the Center of Mass and the Inertia Tensor can now be set explicitly in the Rigidbody Inspector, and serialized for convenience.
Rigidbody with the new expandable sections that allow you to set the center of mass and inertia tensor.
Rigidbody with the new expandable sections that allow you to set the Center of Mass and Inertia Tensor
  • Physics Debug: All values shown in the Physics Debug window are now copyable.
You can now highlight and copy any displayed value in the Physics Debug view.
You can now highlight and copy any displayed value in the Physics Debug view.
  • ArticulationBody: There is now an added set of methods for setting different individual properties of an ArticulationBody drive, without having to allocate a temporary copy on stack. For example, see ArticulationBody.SetTargetDrive, which only sets the damping of a drive.
  • WheelCollider: You can now get and set the rotational speed of a WheelCollider using the newly exposed property WheelCollider.rotationSpeed.
  • Inverse Dynamics: You can now use the new Inverse Dynamics function ArticulationBody.GetJointExternalForces to calculate the forces that counteract the currently acting external forces.

If you’re interested in discussing the physics features of the 2022.2 release, we would love to hear from you – join us in the forums to share feedback.

Is this article helpful for you?

Thank you for your feedback!

Related Posts