Skip to content

Commit 0055e1c

Browse files
fix: provide non Rigidbody contact events and Rigidbody prioritization (#3094)
* fix Provide an extended IContactEventHandlerWithInfo that allows users to prioritize which object is being collided with as well as being able to determine if the instance should return non-rigidbody contact events. * update Adding changelog entry. * update Adding associated PR to the entry * style Adding XML API documentation. * fix Assuring the default non-info contact event handler sets priority to the non-kinematic bodies. * test Added validation test for RigidbodyContactEventManager
1 parent 058481b commit 0055e1c

File tree

3 files changed

+549
-7
lines changed

3 files changed

+549
-7
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ Additional documentation and release notes are available at [Multiplayer Documen
99
[Unreleased]
1010

1111
### Added
12-
12+
13+
- Added `IContactEventHandlerWithInfo` that derives from `IContactEventHandler` that can be updated per frame to provide `ContactEventHandlerInfo` information to the `RigidbodyContactEventManager` when processing collisions. (#3094)
14+
- `ContactEventHandlerInfo.ProvideNonRigidBodyContactEvents`: When set to true, non-`Rigidbody` collisions with the registered `Rigidbody` will generate contact event notifications. (#3094)
15+
- `ContactEventHandlerInfo.HasContactEventPriority`: When set to true, the `Rigidbody` will be prioritized as the instance that generates the event if the `Rigidbody` colliding does not have priority. (#3094)
1316
- Added a static `NetworkManager.OnInstantiated` event notification to be able to track when a new `NetworkManager` instance has been instantiated. (#3088)
1417
- Added a static `NetworkManager.OnDestroying` event notification to be able to track when an existing `NetworkManager` instance is being destroyed. (#3088)
1518

com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs

+162-6
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,70 @@
66

77
namespace Unity.Netcode.Components
88
{
9+
/// <summary>
10+
/// Information a <see cref="Rigidbody"/> returns to <see cref="RigidbodyContactEventManager"/> via <see cref="IContactEventHandlerWithInfo.GetContactEventHandlerInfo"/> <br />
11+
/// if the <see cref="Rigidbody"/> registers itself with <see cref="IContactEventHandlerWithInfo"/> as opposed to <see cref="IContactEventHandler"/>.
12+
/// </summary>
13+
public struct ContactEventHandlerInfo
14+
{
15+
/// <summary>
16+
/// When set to true, the <see cref="RigidbodyContactEventManager"/> will include non-Rigidbody based contact events.<br />
17+
/// When the <see cref="RigidbodyContactEventManager"/> invokes the <see cref="IContactEventHandler.ContactEvent"/> it will return null in place <br />
18+
/// of the collidingBody parameter if the contact event occurred with a collider that is not registered with the <see cref="RigidbodyContactEventManager"/>.
19+
/// </summary>
20+
public bool ProvideNonRigidBodyContactEvents;
21+
/// <summary>
22+
/// When set to true, the <see cref="RigidbodyContactEventManager"/> will prioritize invoking <see cref="IContactEventHandler.ContactEvent(ulong, Vector3, Rigidbody, Vector3, bool, Vector3)"/> <br /></br>
23+
/// if it is the 2nd colliding body in the contact pair being processed. With distributed authority, setting this value to true when a <see cref="NetworkObject"/> is owned by the local client <br />
24+
/// will assure <see cref="IContactEventHandler.ContactEvent(ulong, Vector3, Rigidbody, Vector3, bool, Vector3)"/> is only invoked on the authoritative side.
25+
/// </summary>
26+
public bool HasContactEventPriority;
27+
}
28+
29+
/// <summary>
30+
/// Default implementation required to register a <see cref="Rigidbody"/> with a <see cref="RigidbodyContactEventManager"/> instance.
31+
/// </summary>
32+
/// <remarks>
33+
/// Recommended to implement this method on a <see cref="NetworkBehaviour"/> component
34+
/// </remarks>
935
public interface IContactEventHandler
1036
{
37+
/// <summary>
38+
/// Should return a <see cref="Rigidbody"/>.
39+
/// </summary>
1140
Rigidbody GetRigidbody();
1241

42+
/// <summary>
43+
/// Invoked by the <see cref="RigidbodyContactEventManager"/> instance.
44+
/// </summary>
45+
/// <param name="eventId">A unique contact event identifier.</param>
46+
/// <param name="averagedCollisionNormal">The average normal of the collision between two colliders.</param>
47+
/// <param name="collidingBody">If not null, this will be a registered <see cref="Rigidbody"/> that was part of the collision contact event.</param>
48+
/// <param name="contactPoint">The world space location of the contact event.</param>
49+
/// <param name="hasCollisionStay">Will be set if this is a collision stay contact event (i.e. it is not the first contact event and continually has contact)</param>
50+
/// <param name="averagedCollisionStayNormal">The average normal of the collision stay contact over time.</param>
1351
void ContactEvent(ulong eventId, Vector3 averagedCollisionNormal, Rigidbody collidingBody, Vector3 contactPoint, bool hasCollisionStay = false, Vector3 averagedCollisionStayNormal = default);
1452
}
1553

54+
/// <summary>
55+
/// This is an extended version of <see cref="IContactEventHandler"/> and can be used to register a <see cref="Rigidbody"/> with a <see cref="RigidbodyContactEventManager"/> instance. <br />
56+
/// This provides additional <see cref="ContactEventHandlerInfo"/> information to the <see cref="RigidbodyContactEventManager"/> for each set of contact events it is processing.
57+
/// </summary>
58+
public interface IContactEventHandlerWithInfo : IContactEventHandler
59+
{
60+
/// <summary>
61+
/// Invoked by <see cref="RigidbodyContactEventManager"/> for each set of contact events it is processing (prior to processing).
62+
/// </summary>
63+
/// <returns><see cref="ContactEventHandlerInfo"/></returns>
64+
ContactEventHandlerInfo GetContactEventHandlerInfo();
65+
}
66+
67+
/// <summary>
68+
/// Add this component to an in-scene placed GameObject to provide faster collision event processing between <see cref="Rigidbody"/> instances and optionally static colliders.
69+
/// <see cref="IContactEventHandler"/> <br />
70+
/// <see cref="IContactEventHandlerWithInfo"/> <br />
71+
/// <see cref="ContactEventHandlerInfo"/> <br />
72+
/// </summary>
1673
[AddComponentMenu("Netcode/Rigidbody Contact Event Manager")]
1774
public class RigidbodyContactEventManager : MonoBehaviour
1875
{
@@ -34,6 +91,7 @@ private struct JobResultStruct
3491

3592
private readonly Dictionary<int, Rigidbody> m_RigidbodyMapping = new Dictionary<int, Rigidbody>();
3693
private readonly Dictionary<int, IContactEventHandler> m_HandlerMapping = new Dictionary<int, IContactEventHandler>();
94+
private readonly Dictionary<int, ContactEventHandlerInfo> m_HandlerInfo = new Dictionary<int, ContactEventHandlerInfo>();
3795

3896
private void OnEnable()
3997
{
@@ -49,6 +107,15 @@ private void OnEnable()
49107
Instance = this;
50108
}
51109

110+
/// <summary>
111+
/// Any <see cref="IContactEventHandler"/> implementation can register a <see cref="Rigidbody"/> to be handled by this <see cref="RigidbodyContactEventManager"/> instance.
112+
/// </summary>
113+
/// <remarks>
114+
/// You should enable <see cref="Collider.providesContacts"/> for each <see cref="Collider"/> associated with the <see cref="Rigidbody"/> being registered.<br/>
115+
/// You can enable this during run time or within the editor's inspector view.
116+
/// </remarks>
117+
/// <param name="contactEventHandler"><see cref="IContactEventHandler"/> or <see cref="IContactEventHandlerWithInfo"/></param>
118+
/// <param name="register">true to register and false to remove from being registered</param>
52119
public void RegisterHandler(IContactEventHandler contactEventHandler, bool register = true)
53120
{
54121
var rigidbody = contactEventHandler.GetRigidbody();
@@ -64,6 +131,22 @@ public void RegisterHandler(IContactEventHandler contactEventHandler, bool regis
64131
{
65132
m_HandlerMapping.Add(instanceId, contactEventHandler);
66133
}
134+
135+
if (!m_HandlerInfo.ContainsKey(instanceId))
136+
{
137+
var handlerInfo = new ContactEventHandlerInfo()
138+
{
139+
HasContactEventPriority = true,
140+
ProvideNonRigidBodyContactEvents = false,
141+
};
142+
var handlerWithInfo = contactEventHandler as IContactEventHandlerWithInfo;
143+
144+
if (handlerWithInfo != null)
145+
{
146+
handlerInfo = handlerWithInfo.GetContactEventHandlerInfo();
147+
}
148+
m_HandlerInfo.Add(instanceId, handlerInfo);
149+
}
67150
}
68151
else
69152
{
@@ -88,25 +171,98 @@ private void OnDisable()
88171

89172
private void ProcessCollisions()
90173
{
174+
foreach (var contactEventHandler in m_HandlerMapping)
175+
{
176+
var handlerWithInfo = contactEventHandler.Value as IContactEventHandlerWithInfo;
177+
178+
if (handlerWithInfo != null)
179+
{
180+
m_HandlerInfo[contactEventHandler.Key] = handlerWithInfo.GetContactEventHandlerInfo();
181+
}
182+
else
183+
{
184+
var info = m_HandlerInfo[contactEventHandler.Key];
185+
info.HasContactEventPriority = !m_RigidbodyMapping[contactEventHandler.Key].isKinematic;
186+
m_HandlerInfo[contactEventHandler.Key] = info;
187+
}
188+
}
189+
190+
ContactEventHandlerInfo contactEventHandlerInfo0;
191+
ContactEventHandlerInfo contactEventHandlerInfo1;
192+
91193
// Process all collisions
92194
for (int i = 0; i < m_Count; i++)
93195
{
94196
var thisInstanceID = m_ResultsArray[i].ThisInstanceID;
95197
var otherInstanceID = m_ResultsArray[i].OtherInstanceID;
96-
var rb0Valid = thisInstanceID != 0 && m_RigidbodyMapping.ContainsKey(thisInstanceID);
97-
var rb1Valid = otherInstanceID != 0 && m_RigidbodyMapping.ContainsKey(otherInstanceID);
98-
// Only notify registered rigid bodies.
99-
if (!rb0Valid || !rb1Valid || !m_HandlerMapping.ContainsKey(thisInstanceID))
198+
var contactHandler0 = (IContactEventHandler)null;
199+
var contactHandler1 = (IContactEventHandler)null;
200+
var preferredContactHandler = (IContactEventHandler)null;
201+
var preferredContactHandlerNonRigidbody = false;
202+
var preferredRigidbody = (Rigidbody)null;
203+
var otherContactHandler = (IContactEventHandler)null;
204+
var otherRigidbody = (Rigidbody)null;
205+
206+
var otherContactHandlerNonRigidbody = false;
207+
208+
if (m_RigidbodyMapping.ContainsKey(thisInstanceID))
209+
{
210+
contactHandler0 = m_HandlerMapping[thisInstanceID];
211+
contactEventHandlerInfo0 = m_HandlerInfo[thisInstanceID];
212+
if (contactEventHandlerInfo0.HasContactEventPriority)
213+
{
214+
preferredContactHandler = contactHandler0;
215+
preferredContactHandlerNonRigidbody = contactEventHandlerInfo0.ProvideNonRigidBodyContactEvents;
216+
preferredRigidbody = m_RigidbodyMapping[thisInstanceID];
217+
}
218+
else
219+
{
220+
otherContactHandler = contactHandler0;
221+
otherContactHandlerNonRigidbody = contactEventHandlerInfo0.ProvideNonRigidBodyContactEvents;
222+
otherRigidbody = m_RigidbodyMapping[thisInstanceID];
223+
}
224+
}
225+
226+
if (m_RigidbodyMapping.ContainsKey(otherInstanceID))
227+
{
228+
contactHandler1 = m_HandlerMapping[otherInstanceID];
229+
contactEventHandlerInfo1 = m_HandlerInfo[otherInstanceID];
230+
if (contactEventHandlerInfo1.HasContactEventPriority && preferredContactHandler == null)
231+
{
232+
preferredContactHandler = contactHandler1;
233+
preferredContactHandlerNonRigidbody = contactEventHandlerInfo1.ProvideNonRigidBodyContactEvents;
234+
preferredRigidbody = m_RigidbodyMapping[otherInstanceID];
235+
}
236+
else
237+
{
238+
otherContactHandler = contactHandler1;
239+
otherContactHandlerNonRigidbody = contactEventHandlerInfo1.ProvideNonRigidBodyContactEvents;
240+
otherRigidbody = m_RigidbodyMapping[otherInstanceID];
241+
}
242+
}
243+
244+
if (preferredContactHandler == null && otherContactHandler != null)
245+
{
246+
preferredContactHandler = otherContactHandler;
247+
preferredContactHandlerNonRigidbody = otherContactHandlerNonRigidbody;
248+
preferredRigidbody = otherRigidbody;
249+
otherContactHandler = null;
250+
otherContactHandlerNonRigidbody = false;
251+
otherRigidbody = null;
252+
}
253+
254+
if (preferredContactHandler == null || (preferredContactHandler != null && otherContactHandler == null && !preferredContactHandlerNonRigidbody))
100255
{
101256
continue;
102257
}
258+
103259
if (m_ResultsArray[i].HasCollisionStay)
104260
{
105-
m_HandlerMapping[thisInstanceID].ContactEvent(m_EventId, m_ResultsArray[i].AverageNormal, m_RigidbodyMapping[otherInstanceID], m_ResultsArray[i].ContactPoint, m_ResultsArray[i].HasCollisionStay, m_ResultsArray[i].AverageCollisionStayNormal);
261+
preferredContactHandler.ContactEvent(m_EventId, m_ResultsArray[i].AverageNormal, otherRigidbody, m_ResultsArray[i].ContactPoint, m_ResultsArray[i].HasCollisionStay, m_ResultsArray[i].AverageCollisionStayNormal);
106262
}
107263
else
108264
{
109-
m_HandlerMapping[thisInstanceID].ContactEvent(m_EventId, m_ResultsArray[i].AverageNormal, m_RigidbodyMapping[otherInstanceID], m_ResultsArray[i].ContactPoint);
265+
preferredContactHandler.ContactEvent(m_EventId, m_ResultsArray[i].AverageNormal, otherRigidbody, m_ResultsArray[i].ContactPoint);
110266
}
111267
}
112268
}

0 commit comments

Comments
 (0)