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.

Source: Unity Technologies Blog