From f128592d75a187a0b03dfb8dd2ba8e07af35bc9e Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 10:59:04 -0500 Subject: [PATCH 1/6] fix Fixing issue with rigidbody and physics mode. --- .../Components/NetworkRigidBodyBase.cs | 35 ++++++++++++++++--- .../Runtime/Configuration/NetworkPrefab.cs | 11 ++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index d55b2f30e8..d80c590b80 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -274,6 +274,13 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network #endif #if UNIFIED_NETCODE + + protected override void OnNetworkPostSpawn() + { + InitializeHybrid(); + base.OnNetworkPostSpawn(); + } + // The authored kinematic state, restored in OnDestroy. private bool m_OriginalKinematicState; @@ -283,18 +290,36 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network /// true for a hybrid prefab private bool InitializeHybrid() { - if (NetworkObject == null || !NetworkObject.HasGhost) + if (NetworkObject == null) { return false; } - // Clears any registration left behind by a prior Initialize call. - if (NetworkTransform) + m_OriginalKinematicState = IsKinematic(); + + if (!NetworkObject.HasGhost) + { + if (NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.AlwaysRun) + { + if (m_InternalRigidbody) + { + if (IsServer) + { + m_InternalRigidbody.interpolation = RigidbodyInterpolation.Extrapolate; + } + else + { + m_InternalRigidbody.interpolation = RigidbodyInterpolation.None; + } + } + } + return false; + } + else if (NetworkTransform) { + // Clears any registration left behind by a prior Initialize call. NetworkTransform.UnregisterRigidbody(); } - - m_OriginalKinematicState = IsKinematic(); return true; } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs index 68725f3bbd..471533cdce 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs @@ -1,4 +1,5 @@ using System; +using Unity.Netcode.Components; using UnityEngine; namespace Unity.Netcode @@ -179,6 +180,16 @@ public bool Validate(int index = -1) #if UNIFIED_NETCODE // Mark this network prefab as having to be registered via the unified API HasGhost = networkObject.HasGhost; + + if (HasGhost) + { + var networkRigidbodyBase = networkObject.GetComponent(); + if(networkRigidbodyBase != null && NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.UserControlled) + { + NetworkLog.LogWarning(new Logging.Context(LogLevel.Developer, "Full tick smoothing is being disabled.").AddNetworkObject(networkObject)); + networkObject.GhostObject.FullTickSmoothing = FullTickSmoothingMode.Disabled; + } + } #endif if (networkObject.InScenePlaced) { From a8e6bc2e9ea1610c0a785241b5317351639f9690 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 12:12:47 -0500 Subject: [PATCH 2/6] update Adding minor blurb about the rigidbody adjustments --- com.unity.netcode.gameobjects/Documentation~/ghostobjects.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md index 5e135b699a..b553152c0a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md @@ -86,6 +86,11 @@ To convert a hybrid object back into a prefab that uses only one package, remove > [!NOTE] > Unity might prevent you from removing a **GhostObject** or **NetworkObject** component while the GameObject has `GhostBehaviour` or `NetworkBehaviour` components that depend on it. Remove the dependent components first. +## Mixed mode objects and physics + +- When the `NetcodeConfig` asset is configured for **Always run**, non-hybrid NGO prefabs with a NetworkRigidbody will automatically adjust the Rigidbody's **Interpolate** value to **Extrapolate** on the server or host side and to **None** on the client side in order to avoid visual anomalies. +- When the `NetcodeConfig` asset is configured for **User Controlled**, hybrid NGO prefabs with a NetworkRigidbody will automatically disable **Full Tick Smoothing* for these prefabs. + ## Additional resources - [GhostObjects in Unity Netcode](https://docs.unity3d.com/Packages/com.unity.netcode@latest?subfolder=/manual/ghostobject/ghostobject.html) From 7411f6bca24beabe6cc1bd449c5f28ca775c274d Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 14:03:23 -0500 Subject: [PATCH 3/6] style Adding whitespace --- .../Runtime/Configuration/NetworkPrefab.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs index 471533cdce..5251b356fd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs @@ -184,7 +184,7 @@ public bool Validate(int index = -1) if (HasGhost) { var networkRigidbodyBase = networkObject.GetComponent(); - if(networkRigidbodyBase != null && NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.UserControlled) + if (networkRigidbodyBase != null && NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.UserControlled) { NetworkLog.LogWarning(new Logging.Context(LogLevel.Developer, "Full tick smoothing is being disabled.").AddNetworkObject(networkObject)); networkObject.GhostObject.FullTickSmoothing = FullTickSmoothingMode.Disabled; From d5341fef5b614ab4e37e8d64acbf888177477ac1 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 15:12:50 -0500 Subject: [PATCH 4/6] fix: scope physics mode workarounds to hybrid sessions and keep them across authority changes - AlwaysRun interpolation override now applies only when the session has hybrid prefabs (N4E defaults to AlwaysRun, so plain NGO sessions were affected), only to 3D Rigidbodies, and is reapplied on every kinematic/ownership update instead of being reset to the authored value. - Restores the authored kinematic capture for hybrid prefabs to Awake only, so OnDestroy no longer restores the runtime kinematic state. - Guards both NetcodeConfig.Global reads against a missing config, and compiles the full tick smoothing change only when a physics module is present. --- .../Documentation~/ghostobjects.md | 4 +- .../Components/NetworkRigidBodyBase.cs | 65 ++++++++++--------- .../Runtime/Configuration/NetworkPrefab.cs | 17 ++--- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md index c7c4e191be..c9af80cff8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md @@ -88,8 +88,8 @@ To convert a hybrid object back into a prefab that uses only one package, remove ## Mixed mode objects and physics -- When the `NetcodeConfig` asset is configured for **Always run**, non-hybrid NGO prefabs with a NetworkRigidbody will automatically adjust the Rigidbody's **Interpolate** value to **Extrapolate** on the server or host side and to **None** on the client side in order to avoid visual anomalies. -- When the `NetcodeConfig` asset is configured for **User Controlled**, hybrid NGO prefabs with a NetworkRigidbody will automatically disable **Full Tick Smoothing* for these prefabs. +- When the `NetcodeConfig` asset is configured for **Always run** and the session has hybrid prefabs, non-hybrid NGO prefabs with a NetworkRigidbody and a 3D Rigidbody will automatically adjust the Rigidbody's **Interpolate** value to **Extrapolate** on the server or host side and to **None** on the client side in order to avoid visual anomalies. +- When the `NetcodeConfig` asset is configured for **User Controlled**, hybrid NGO prefabs with a NetworkRigidbody will automatically disable **Full Tick Smoothing** for these prefabs. ## Additional resources diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index d80c590b80..8f526639a5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -274,13 +274,6 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network #endif #if UNIFIED_NETCODE - - protected override void OnNetworkPostSpawn() - { - InitializeHybrid(); - base.OnNetworkPostSpawn(); - } - // The authored kinematic state, restored in OnDestroy. private bool m_OriginalKinematicState; @@ -290,39 +283,40 @@ protected override void OnNetworkPostSpawn() /// true for a hybrid prefab private bool InitializeHybrid() { - if (NetworkObject == null) + if (NetworkObject == null || !NetworkObject.HasGhost) { return false; } - m_OriginalKinematicState = IsKinematic(); - - if (!NetworkObject.HasGhost) - { - if (NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.AlwaysRun) - { - if (m_InternalRigidbody) - { - if (IsServer) - { - m_InternalRigidbody.interpolation = RigidbodyInterpolation.Extrapolate; - } - else - { - m_InternalRigidbody.interpolation = RigidbodyInterpolation.None; - } - } - } - return false; - } - else if (NetworkTransform) + // Clears any registration left behind by a prior Initialize call. + if (NetworkTransform) { - // Clears any registration left behind by a prior Initialize call. NetworkTransform.UnregisterRigidbody(); } + + m_OriginalKinematicState = IsKinematic(); return true; } + // Overrides the authored interpolation while spawned when the NetcodeConfig physics mode requires it. + private bool m_UsePhysicsModeInterpolation; + private InterpolationTypes m_PhysicsModeInterpolation; + + /// + /// Prevents the jitter an interpolated Rigidbody that is not a GhostObject shows in a hybrid session with . + /// + private void SetPhysicsModeInterpolation() + { + var netcodeConfig = NetcodeConfig.Global; + m_UsePhysicsModeInterpolation = !m_IsRigidbody2D && m_LocalNetworkManager.NetworkConfig.Prefabs.HasGhostPrefabs + && netcodeConfig != null && netcodeConfig.PhysicsRunMode == PhysicGroupRunMode.AlwaysRun; + if (m_UsePhysicsModeInterpolation) + { + m_PhysicsModeInterpolation = m_LocalNetworkManager.IsServer ? InterpolationTypes.Extrapolate : InterpolationTypes.None; + SetInterpolation(m_PhysicsModeInterpolation); + } + } + /// /// Kinematic on every peer except the server. Called at spawn because an in-scene placed instance has no session during Awake. /// @@ -981,6 +975,14 @@ private void PostSetIsKinematic() return; } +#if UNIFIED_NETCODE + if (m_UsePhysicsModeInterpolation) + { + SetInterpolation(m_PhysicsModeInterpolation); + return; + } +#endif + if (UseRigidBodyForMotion) { // Exit early if the original interpolation type is not set to extrapolate or NetworkTransform interpolate is disabled @@ -1147,6 +1149,9 @@ public override void OnNetworkSpawn() #endif m_TickFrequency = 1.0f / m_LocalNetworkManager.NetworkConfig.TickRate; m_TickRate = m_LocalNetworkManager.NetworkConfig.TickRate; +#if UNIFIED_NETCODE + SetPhysicsModeInterpolation(); +#endif UpdateOwnershipAuthority(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs index 5251b356fd..3a03a59bd8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs @@ -1,5 +1,4 @@ using System; -using Unity.Netcode.Components; using UnityEngine; namespace Unity.Netcode @@ -181,15 +180,17 @@ public bool Validate(int index = -1) // Mark this network prefab as having to be registered via the unified API HasGhost = networkObject.HasGhost; - if (HasGhost) +#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D + // With UserControlled physics, full tick smoothing holds a hybrid Rigidbody in place on the host. + var netcodeConfig = NetcodeConfig.Global; + if (HasGhost && netcodeConfig != null && netcodeConfig.PhysicsRunMode == PhysicGroupRunMode.UserControlled + && networkObject.GhostObject.FullTickSmoothing != FullTickSmoothingMode.Disabled + && networkObject.GetComponent() != null) { - var networkRigidbodyBase = networkObject.GetComponent(); - if (networkRigidbodyBase != null && NetcodeConfig.Global.PhysicsRunMode == PhysicGroupRunMode.UserControlled) - { - NetworkLog.LogWarning(new Logging.Context(LogLevel.Developer, "Full tick smoothing is being disabled.").AddNetworkObject(networkObject)); - networkObject.GhostObject.FullTickSmoothing = FullTickSmoothingMode.Disabled; - } + NetworkLog.LogWarning(new Logging.Context(LogLevel.Developer, $"Disabling full tick smoothing on {networkObject.name} because the {nameof(NetcodeConfig)} physics mode is {PhysicGroupRunMode.UserControlled}.").AddNetworkObject(networkObject)); + networkObject.GhostObject.FullTickSmoothing = FullTickSmoothingMode.Disabled; } +#endif #endif if (networkObject.InScenePlaced) { From 4f90c863e5142d8ed95296051c7c8ba798c44ead Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 16:20:13 -0500 Subject: [PATCH 5/6] fix: AlwaysRun interpolation follows transform authority, not the server Uses the cached authority that UpdateOwnershipAuthority refreshes on spawn and on every ownership change, so an owner-authoritative body extrapolates on its owner and stops on the server. --- .../Components/NetworkRigidBodyBase.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 8f526639a5..cbc4b3a5ad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -300,7 +300,6 @@ private bool InitializeHybrid() // Overrides the authored interpolation while spawned when the NetcodeConfig physics mode requires it. private bool m_UsePhysicsModeInterpolation; - private InterpolationTypes m_PhysicsModeInterpolation; /// /// Prevents the jitter an interpolated Rigidbody that is not a GhostObject shows in a hybrid session with . @@ -310,11 +309,12 @@ private void SetPhysicsModeInterpolation() var netcodeConfig = NetcodeConfig.Global; m_UsePhysicsModeInterpolation = !m_IsRigidbody2D && m_LocalNetworkManager.NetworkConfig.Prefabs.HasGhostPrefabs && netcodeConfig != null && netcodeConfig.PhysicsRunMode == PhysicGroupRunMode.AlwaysRun; - if (m_UsePhysicsModeInterpolation) - { - m_PhysicsModeInterpolation = m_LocalNetworkManager.IsServer ? InterpolationTypes.Extrapolate : InterpolationTypes.None; - SetInterpolation(m_PhysicsModeInterpolation); - } + } + + // The authority extrapolates and every other peer does not interpolate. + private void ApplyPhysicsModeInterpolation() + { + SetInterpolation(m_IsAuthority ? InterpolationTypes.Extrapolate : InterpolationTypes.None); } /// @@ -978,7 +978,7 @@ private void PostSetIsKinematic() #if UNIFIED_NETCODE if (m_UsePhysicsModeInterpolation) { - SetInterpolation(m_PhysicsModeInterpolation); + ApplyPhysicsModeInterpolation(); return; } #endif @@ -1130,6 +1130,12 @@ internal void UpdateOwnershipAuthority() { SetIsKinematic(!m_IsAuthority); } +#if UNIFIED_NETCODE + else if (m_UsePhysicsModeInterpolation) + { + ApplyPhysicsModeInterpolation(); + } +#endif } internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManager) From edfb9641fb4c0233b89395daf331ef11fd35a421 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 28 Oct 2026 16:29:19 -0500 Subject: [PATCH 6/6] docs: describe the Always Run interpolation by authority, not server and client --- .../Documentation~/ghostobjects.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md index c9af80cff8..fa9fe14402 100644 --- a/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/ghostobjects.md @@ -88,8 +88,10 @@ To convert a hybrid object back into a prefab that uses only one package, remove ## Mixed mode objects and physics -- When the `NetcodeConfig` asset is configured for **Always run** and the session has hybrid prefabs, non-hybrid NGO prefabs with a NetworkRigidbody and a 3D Rigidbody will automatically adjust the Rigidbody's **Interpolate** value to **Extrapolate** on the server or host side and to **None** on the client side in order to avoid visual anomalies. -- When the `NetcodeConfig` asset is configured for **User Controlled**, hybrid NGO prefabs with a NetworkRigidbody will automatically disable **Full Tick Smoothing** for these prefabs. +To avoid visual anomalies, NGO automatically adjusts some Rigidbody settings based on the **Physics Run Mode** of the `NetcodeConfig` asset: + +- **Always Run**: In a session that has hybrid prefabs, non-hybrid NGO prefabs with a NetworkRigidbody and a 3D Rigidbody have the Rigidbody's **Interpolate** value set to **Extrapolate** on the authority instance and to **None** on all non-authority instances. The authority instance is the server or host for a server-authoritative NetworkTransform, or the owner for an owner-authoritative one. The value follows authority when ownership changes, and the original value is restored when the instance despawns. +- **User Controlled**: Hybrid NGO prefabs with a NetworkRigidbody have **Full Tick Smoothing** set to **Disabled**. ## Additional resources