Skip to content

Commit 2787ac5

Browse files
authored
fix: correct interpolation of NetworkTransform upon space change (#1744)
* fix: initial, naive, fix for localspace change in NetworkTransport. Not working. Also includes test that shows it is not working * fix: clearing the interpolator upon receive * fix: reset to position instead of just clearing buffers, upon interpolation space change * restoring unintended comment change * test: improving the interpolation test * removing the interpolation test as it exposes a different issue * improved fix, added test back * style: readability * test: test improvements * test: removing left-over code
1 parent 4a7e85d commit 2787ac5

File tree

3 files changed

+186
-3
lines changed

3 files changed

+186
-3
lines changed

com.unity.netcode.gameobjects/Components/NetworkTransform.cs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,10 @@ public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReade
260260
/// </summary>
261261
[Tooltip("Sets whether this transform should sync in local space or in world space")]
262262
public bool InLocalSpace = false;
263+
private bool m_LastInterpolateLocal = false; // was the last frame local
263264

264265
public bool Interpolate = true;
265-
private bool m_LastInterpolate = true;
266+
private bool m_LastInterpolate = true; // was the last frame interpolated
266267

267268
/// <summary>
268269
/// Used to determine who can write to this transform. Server only for this transform.
@@ -613,10 +614,46 @@ private void ApplyInterpolatedNetworkStateToTransform(NetworkTransformState netw
613614
}
614615
}
615616

616-
private void AddInterpolatedState(NetworkTransformState newState)
617+
private void AddInterpolatedState(NetworkTransformState newState, bool reset = false)
617618
{
618619
var sentTime = newState.SentTime;
619620

621+
if (reset)
622+
{
623+
if (newState.HasPositionX)
624+
{
625+
m_PositionXInterpolator.ResetTo(newState.PositionX, sentTime);
626+
}
627+
628+
if (newState.HasPositionY)
629+
{
630+
m_PositionYInterpolator.ResetTo(newState.PositionY, sentTime);
631+
}
632+
633+
if (newState.HasPositionZ)
634+
{
635+
m_PositionZInterpolator.ResetTo(newState.PositionZ, sentTime);
636+
}
637+
638+
m_RotationInterpolator.ResetTo(Quaternion.Euler(newState.Rotation), sentTime);
639+
640+
if (newState.HasScaleX)
641+
{
642+
m_ScaleXInterpolator.ResetTo(newState.ScaleX, sentTime);
643+
}
644+
645+
if (newState.HasScaleY)
646+
{
647+
m_ScaleYInterpolator.ResetTo(newState.ScaleY, sentTime);
648+
}
649+
650+
if (newState.HasScaleZ)
651+
{
652+
m_ScaleZInterpolator.ResetTo(newState.ScaleZ, sentTime);
653+
}
654+
655+
return;
656+
}
620657
if (newState.HasPositionX)
621658
{
622659
m_PositionXInterpolator.AddMeasurement(newState.PositionX, sentTime);
@@ -668,8 +705,9 @@ private void OnNetworkStateChanged(NetworkTransformState oldState, NetworkTransf
668705

669706
if (Interpolate)
670707
{
671-
AddInterpolatedState(newState);
708+
AddInterpolatedState(newState, (newState.InLocalSpace != m_LastInterpolateLocal));
672709
}
710+
m_LastInterpolateLocal = newState.InLocalSpace;
673711

674712
if (m_CachedNetworkManager.LogLevel == LogLevel.Developer)
675713
{
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using System;
2+
using System.Collections;
3+
using Unity.Netcode.Components;
4+
using UnityEngine;
5+
using UnityEngine.TestTools;
6+
7+
namespace Unity.Netcode.RuntimeTests
8+
{
9+
public class TransformInterpolationObject : NetworkBehaviour
10+
{
11+
public bool CheckPosition;
12+
public bool IsMoving;
13+
public bool IsFixed;
14+
15+
private void Update()
16+
{
17+
// Check the position of the nested object on the client
18+
if (CheckPosition)
19+
{
20+
if (transform.position.y < 0.0f || transform.position.y > 100.0f)
21+
{
22+
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
23+
}
24+
}
25+
26+
// Move the nested object on the server
27+
if (IsMoving)
28+
{
29+
var y = Time.realtimeSinceStartup;
30+
while (y > 10.0f)
31+
{
32+
y -= 10.0f;
33+
}
34+
35+
// change the space between local and global every second
36+
GetComponent<NetworkTransform>().InLocalSpace = ((int)y % 2 == 0);
37+
38+
transform.position = new Vector3(0.0f, y * 10, 0.0f);
39+
}
40+
41+
// On the server, make sure to keep the parent object at a fixed position
42+
if (IsFixed)
43+
{
44+
transform.position = new Vector3(1000.0f, 1000.0f, 1000.0f);
45+
}
46+
}
47+
}
48+
49+
public class TransformInterpolationTests : BaseMultiInstanceTest
50+
{
51+
protected override int NbClients => 1;
52+
53+
private ulong m_ClientId0;
54+
private GameObject m_PrefabToSpawn;
55+
56+
private NetworkObject m_AsNetworkObject;
57+
private NetworkObject m_SpawnedObjectOnClient;
58+
59+
[UnitySetUp]
60+
public override IEnumerator Setup()
61+
{
62+
yield return StartSomeClientsAndServerWithPlayers(useHost: true, nbClients: NbClients,
63+
updatePlayerPrefab: playerPrefab =>
64+
{
65+
m_PrefabToSpawn = PreparePrefab(typeof(TransformInterpolationObject));
66+
});
67+
}
68+
69+
private IEnumerator RefreshNetworkObjects()
70+
{
71+
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
72+
yield return MultiInstanceHelpers.Run(
73+
MultiInstanceHelpers.GetNetworkObjectByRepresentation(
74+
x => x.NetworkObjectId == m_AsNetworkObject.NetworkObjectId,
75+
m_ClientNetworkManagers[0],
76+
serverClientPlayerResult));
77+
m_SpawnedObjectOnClient = serverClientPlayerResult.Result;
78+
79+
// make sure the objects are set with the right network manager
80+
m_SpawnedObjectOnClient.NetworkManagerOwner = m_ClientNetworkManagers[0];
81+
}
82+
83+
public GameObject PreparePrefab(Type type)
84+
{
85+
var prefabToSpawn = new GameObject();
86+
prefabToSpawn.AddComponent(type);
87+
prefabToSpawn.AddComponent<NetworkTransform>();
88+
var networkObjectPrefab = prefabToSpawn.AddComponent<NetworkObject>();
89+
MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObjectPrefab);
90+
m_ServerNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab() { Prefab = prefabToSpawn });
91+
foreach (var clientNetworkManager in m_ClientNetworkManagers)
92+
{
93+
clientNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab() { Prefab = prefabToSpawn });
94+
}
95+
return prefabToSpawn;
96+
}
97+
98+
[UnityTest]
99+
public IEnumerator TransformInterpolationTest()
100+
{
101+
m_ClientId0 = m_ClientNetworkManagers[0].LocalClientId;
102+
103+
// create an object
104+
var spawnedObject = UnityEngine.Object.Instantiate(m_PrefabToSpawn);
105+
var baseObject = UnityEngine.Object.Instantiate(m_PrefabToSpawn);
106+
baseObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
107+
baseObject.GetComponent<NetworkObject>().Spawn();
108+
109+
m_AsNetworkObject = spawnedObject.GetComponent<NetworkObject>();
110+
m_AsNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
111+
112+
m_AsNetworkObject.TrySetParent(baseObject);
113+
114+
m_AsNetworkObject.Spawn();
115+
116+
yield return RefreshNetworkObjects();
117+
118+
m_AsNetworkObject.TrySetParent(baseObject);
119+
120+
baseObject.GetComponent<TransformInterpolationObject>().IsFixed = true;
121+
spawnedObject.GetComponent<TransformInterpolationObject>().IsMoving = true;
122+
123+
// Give two seconds for the object to settle
124+
yield return new WaitForSeconds(2.0f);
125+
126+
m_SpawnedObjectOnClient.GetComponent<TransformInterpolationObject>().CheckPosition = true;
127+
128+
// Test that interpolation works correctly for 10 seconds
129+
// Increasing this duration gives you the opportunity to go check in the Editor how the objects are setup
130+
// and how they move
131+
yield return new WaitForSeconds(10.0f);
132+
}
133+
}
134+
}

com.unity.netcode.gameobjects/Tests/Runtime/TransformInterpolationTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)