This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs
Unity Technologies 63c7e4c78a com.unity.netcode.gameobjects@2.0.0-exp.4
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [2.0.0-exp.4] - 2024-05-31

### Added

- Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933)
- Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912)
- Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912)
- Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912)

### Fixed

- Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933)
- Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924)
- Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912)
- Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898)

### Changed

- Change all the access modifiers of test class from Public to Internal (#2930)
- Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929)
- Changed `NetworkClient.SessionModeTypes` to `NetworkClient.NetworkTopologyTypes`. (#2875)
- Changed `NetworkClient.SessionModeType` to `NetworkClient.NetworkTopologyType`. (#2875)
- Changed `NetworkConfig.SessionMode` to `NeworkConfig.NetworkTopology`. (#2875)
2024-05-31 00:00:00 +00:00

231 lines
8.1 KiB
C#

#if COM_UNITY_MODULES_PHYSICS
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
namespace Unity.Netcode.Components
{
public interface IContactEventHandler
{
Rigidbody GetRigidbody();
void ContactEvent(ulong eventId, Vector3 averagedCollisionNormal, Rigidbody collidingBody, Vector3 contactPoint, bool hasCollisionStay = false, Vector3 averagedCollisionStayNormal = default);
}
[AddComponentMenu("Netcode/Rigidbody Contact Event Manager")]
public class RigidbodyContactEventManager : MonoBehaviour
{
public static RigidbodyContactEventManager Instance { get; private set; }
private struct JobResultStruct
{
public bool HasCollisionStay;
public int ThisInstanceID;
public int OtherInstanceID;
public Vector3 AverageNormal;
public Vector3 AverageCollisionStayNormal;
public Vector3 ContactPoint;
}
private NativeArray<JobResultStruct> m_ResultsArray;
private int m_Count = 0;
private JobHandle m_JobHandle;
private readonly Dictionary<int, Rigidbody> m_RigidbodyMapping = new Dictionary<int, Rigidbody>();
private readonly Dictionary<int, IContactEventHandler> m_HandlerMapping = new Dictionary<int, IContactEventHandler>();
private void OnEnable()
{
m_ResultsArray = new NativeArray<JobResultStruct>(16, Allocator.Persistent);
Physics.ContactEvent += Physics_ContactEvent;
if (Instance != null)
{
NetworkLog.LogError($"[Invalid][Multiple Instances] Found more than one instance of {nameof(RigidbodyContactEventManager)}: {name} and {Instance.name}");
NetworkLog.LogError($"[Disable][Additional Instance] Disabling {name} instance!");
gameObject.SetActive(false);
return;
}
Instance = this;
}
public void RegisterHandler(IContactEventHandler contactEventHandler, bool register = true)
{
var rigidbody = contactEventHandler.GetRigidbody();
var instanceId = rigidbody.GetInstanceID();
if (register)
{
if (!m_RigidbodyMapping.ContainsKey(instanceId))
{
m_RigidbodyMapping.Add(instanceId, rigidbody);
}
if (!m_HandlerMapping.ContainsKey(instanceId))
{
m_HandlerMapping.Add(instanceId, contactEventHandler);
}
}
else
{
m_RigidbodyMapping.Remove(instanceId);
m_HandlerMapping.Remove(instanceId);
}
}
private void OnDisable()
{
m_JobHandle.Complete();
m_ResultsArray.Dispose();
Physics.ContactEvent -= Physics_ContactEvent;
m_RigidbodyMapping.Clear();
Instance = null;
}
private bool m_HasCollisions;
private int m_CurrentCount = 0;
private void ProcessCollisions()
{
// Process all collisions
for (int i = 0; i < m_Count; i++)
{
var thisInstanceID = m_ResultsArray[i].ThisInstanceID;
var otherInstanceID = m_ResultsArray[i].OtherInstanceID;
var rb0Valid = thisInstanceID != 0 && m_RigidbodyMapping.ContainsKey(thisInstanceID);
var rb1Valid = otherInstanceID != 0 && m_RigidbodyMapping.ContainsKey(otherInstanceID);
// Only notify registered rigid bodies.
if (!rb0Valid || !rb1Valid || !m_HandlerMapping.ContainsKey(thisInstanceID))
{
continue;
}
if (m_ResultsArray[i].HasCollisionStay)
{
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);
}
else
{
m_HandlerMapping[thisInstanceID].ContactEvent(m_EventId, m_ResultsArray[i].AverageNormal, m_RigidbodyMapping[otherInstanceID], m_ResultsArray[i].ContactPoint);
}
}
}
private void FixedUpdate()
{
// Only process new collisions
if (!m_HasCollisions && m_CurrentCount == 0)
{
return;
}
// This assures we won't process the same collision
// set after it has been processed.
if (m_HasCollisions)
{
m_CurrentCount = m_Count;
m_HasCollisions = false;
m_JobHandle.Complete();
}
ProcessCollisions();
}
private void LateUpdate()
{
m_CurrentCount = 0;
}
private ulong m_EventId;
private void Physics_ContactEvent(PhysicsScene scene, NativeArray<ContactPairHeader>.ReadOnly pairHeaders)
{
m_EventId++;
m_HasCollisions = true;
int n = pairHeaders.Length;
if (m_ResultsArray.Length < n)
{
m_ResultsArray.Dispose();
m_ResultsArray = new NativeArray<JobResultStruct>(Mathf.NextPowerOfTwo(n), Allocator.Persistent);
}
m_Count = n;
var job = new GetCollisionsJob()
{
PairedHeaders = pairHeaders,
ResultsArray = m_ResultsArray
};
m_JobHandle = job.Schedule(n, 256);
}
private struct GetCollisionsJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<ContactPairHeader>.ReadOnly PairedHeaders;
public NativeArray<JobResultStruct> ResultsArray;
public void Execute(int index)
{
Vector3 averageNormal = Vector3.zero;
Vector3 averagePoint = Vector3.zero;
Vector3 averageCollisionStay = Vector3.zero;
int count = 0;
int collisionStaycount = 0;
int positionCount = 0;
for (int j = 0; j < PairedHeaders[index].pairCount; j++)
{
ref readonly var pair = ref PairedHeaders[index].GetContactPair(j);
if (pair.isCollisionExit)
{
continue;
}
for (int k = 0; k < pair.contactCount; k++)
{
ref readonly var contact = ref pair.GetContactPoint(k);
averagePoint += contact.position;
positionCount++;
if (!pair.isCollisionStay)
{
averageNormal += contact.normal;
count++;
}
else
{
averageCollisionStay += contact.normal;
collisionStaycount++;
}
}
}
if (count != 0)
{
averageNormal /= count;
}
if (collisionStaycount != 0)
{
averageCollisionStay /= collisionStaycount;
}
if (positionCount != 0)
{
averagePoint /= positionCount;
}
var result = new JobResultStruct()
{
ThisInstanceID = PairedHeaders[index].bodyInstanceID,
OtherInstanceID = PairedHeaders[index].otherBodyInstanceID,
AverageNormal = averageNormal,
HasCollisionStay = collisionStaycount != 0,
AverageCollisionStayNormal = averageCollisionStay,
ContactPoint = averagePoint
};
ResultsArray[index] = result;
}
}
}
}
#endif