Skip to content

Commit 4fe7a30

Browse files
authored
feat: network physics (#1175)
* feat: NetworkRigidbody and sample scene * feat: NetworkRigidbody2D component to support 2d physics * test: add network rigidbody tests
1 parent 2091e2a commit 4fe7a30

33 files changed

+3677
-35
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using UnityEngine;
2+
3+
namespace Unity.Netcode.Components
4+
{
5+
/// <summary>
6+
/// NetworkRigidbody allows for the use of <see cref="Rigidbody"/> on network objects. By controlling the kinematic
7+
/// mode of the rigidbody and disabling it on all peers but the authoritative one.
8+
/// </summary>
9+
[RequireComponent(typeof(Rigidbody))]
10+
[RequireComponent(typeof(NetworkTransform))]
11+
public class NetworkRigidbody : NetworkBehaviour
12+
{
13+
private Rigidbody m_Rigidbody;
14+
15+
private bool m_OriginalKinematic;
16+
17+
// Used to cache the authority state of this rigidbody during the last frame
18+
private bool m_IsAuthority;
19+
20+
/// <summary>
21+
/// Gets a bool value indicating whether this <see cref="NetworkRigidbody"/> on this peer currently holds authority.
22+
/// </summary>
23+
internal bool HasAuthority => NetworkManager.IsServer; // TODO update this once we support owner authoritative NetworkTransform.
24+
25+
private void Awake()
26+
{
27+
m_Rigidbody = GetComponent<Rigidbody>();
28+
}
29+
30+
// Currently commented out because it is not needed as authority currently can't change at runtime.
31+
// private void FixedUpdate()
32+
// {
33+
// if (NetworkManager.IsListening)
34+
// {
35+
// if (HasAuthority != m_IsAuthority)
36+
// {
37+
// m_IsAuthority = HasAuthority;
38+
// UpdateRigidbodyKinematicMode();
39+
// }
40+
// }
41+
// }
42+
43+
// Puts the rigidbody in a kinematic non-interpolated mode on everyone but the server.
44+
private void UpdateRigidbodyKinematicMode()
45+
{
46+
if (m_IsAuthority == false)
47+
{
48+
m_OriginalKinematic = m_Rigidbody.isKinematic;
49+
m_Rigidbody.isKinematic = true;
50+
}
51+
else
52+
{
53+
// Resets the rigidbody back to it's non replication only state. Happens on shutdown and when authority is lost
54+
m_Rigidbody.isKinematic = m_OriginalKinematic;
55+
}
56+
}
57+
58+
/// <inheritdoc />
59+
public override void OnNetworkSpawn()
60+
{
61+
m_IsAuthority = HasAuthority;
62+
m_OriginalKinematic = m_Rigidbody.isKinematic;
63+
UpdateRigidbodyKinematicMode();
64+
}
65+
66+
/// <inheritdoc />
67+
public override void OnNetworkDespawn()
68+
{
69+
UpdateRigidbodyKinematicMode();
70+
}
71+
}
72+
}

com.unity.netcode.gameobjects/Components/NetworkRigidbody.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.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using UnityEngine;
2+
3+
namespace Unity.Netcode.Components
4+
{
5+
/// <summary>
6+
/// NetworkRigidbody allows for the use of <see cref="Rigidbody2D"/> on network objects. By controlling the kinematic
7+
/// mode of the rigidbody and disabling it on all peers but the authoritative one.
8+
/// </summary>
9+
[RequireComponent(typeof(Rigidbody2D))]
10+
[RequireComponent(typeof(NetworkTransform))]
11+
public class NetworkRigidbody2D : NetworkBehaviour
12+
{
13+
private Rigidbody2D m_Rigidbody;
14+
15+
private bool m_OriginalKinematic;
16+
17+
// Used to cache the authority state of this rigidbody during the last frame
18+
private bool m_IsAuthority;
19+
20+
/// <summary>
21+
/// Gets a bool value indicating whether this <see cref="NetworkRigidbody2D"/> on this peer currently holds authority.
22+
/// </summary>
23+
internal bool HasAuthority => NetworkManager.IsServer; // TODO update this once we support owner authoritative NetworkTransform.
24+
25+
private void Awake()
26+
{
27+
m_Rigidbody = GetComponent<Rigidbody2D>();
28+
}
29+
30+
// Currently commented out because it is not needed as authority currently can't change at runtime.
31+
// private void FixedUpdate()
32+
// {
33+
// if (NetworkManager.IsListening)
34+
// {
35+
// if (HasAuthority != m_IsAuthority)
36+
// {
37+
// m_IsAuthority = HasAuthority;
38+
// UpdateRigidbodyKinematicMode();
39+
// }
40+
// }
41+
// }
42+
43+
// Puts the rigidbody in a kinematic non-interpolated mode on everyone but the server.
44+
private void UpdateRigidbodyKinematicMode()
45+
{
46+
if (m_IsAuthority == false)
47+
{
48+
m_OriginalKinematic = m_Rigidbody.isKinematic;
49+
m_Rigidbody.isKinematic = true;
50+
}
51+
else
52+
{
53+
// Resets the rigidbody back to it's non replication only state. Happens on shutdown and when authority is lost
54+
m_Rigidbody.isKinematic = m_OriginalKinematic;
55+
}
56+
}
57+
58+
/// <inheritdoc />
59+
public override void OnNetworkSpawn()
60+
{
61+
m_IsAuthority = HasAuthority;
62+
m_OriginalKinematic = m_Rigidbody.isKinematic;
63+
UpdateRigidbodyKinematicMode();
64+
}
65+
66+
/// <inheritdoc />
67+
public override void OnNetworkDespawn()
68+
{
69+
m_IsAuthority = false;
70+
UpdateRigidbodyKinematicMode();
71+
}
72+
}
73+
}

com.unity.netcode.gameobjects/Components/NetworkRigidbody2D.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.

com.unity.netcode.gameobjects/Tests/Runtime/Physics.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Netcode.Components;
4+
using UnityEngine;
5+
using UnityEngine.TestTools;
6+
7+
namespace Unity.Netcode.RuntimeTests.Physics
8+
{
9+
public class NetworkRigidbody2DDynamicTest : NetworkRigidbodyTestBase
10+
{
11+
public override bool Kinematic => false;
12+
}
13+
14+
public class NetworkRigidbody2DKinematicTest : NetworkRigidbodyTestBase
15+
{
16+
public override bool Kinematic => true;
17+
}
18+
19+
public abstract class NetworkRigidbody2DTestBase : BaseMultiInstanceTest
20+
{
21+
protected override int NbClients => 1;
22+
23+
public abstract bool Kinematic { get; }
24+
25+
[UnitySetUp]
26+
public override IEnumerator Setup()
27+
{
28+
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab =>
29+
{
30+
playerPrefab.AddComponent<NetworkTransform>();
31+
playerPrefab.AddComponent<Rigidbody2D>();
32+
playerPrefab.AddComponent<NetworkRigidbody>();
33+
playerPrefab.GetComponent<Rigidbody2D>().isKinematic = Kinematic;
34+
});
35+
}
36+
37+
/// <summary>
38+
/// Tests that a server can destroy a NetworkObject and that it gets despawned correctly.
39+
/// </summary>
40+
/// <returns></returns>
41+
[UnityTest]
42+
public IEnumerator TestRigidbodyKinematicEnableDisable()
43+
{
44+
// This is the *SERVER VERSION* of the *CLIENT PLAYER*
45+
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
46+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult));
47+
var serverPlayer = serverClientPlayerResult.Result.gameObject;
48+
49+
// This is the *CLIENT VERSION* of the *CLIENT PLAYER*
50+
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
51+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult));
52+
var clientPlayer = clientClientPlayerResult.Result.gameObject;
53+
54+
Assert.IsNotNull(serverPlayer);
55+
Assert.IsNotNull(clientPlayer);
56+
57+
int waitFor = Time.frameCount + 2;
58+
yield return new WaitUntil(() => Time.frameCount >= waitFor);
59+
60+
// server rigidbody has authority and should have a kinematic mode of false
61+
Assert.True(serverPlayer.GetComponent<Rigidbody2D>().isKinematic == Kinematic);
62+
63+
// client rigidbody has no authority and should have a kinematic mode of true
64+
Assert.True(clientPlayer.GetComponent<Rigidbody2D>().isKinematic);
65+
66+
// despawn the server player
67+
serverPlayer.GetComponent<NetworkObject>().Despawn(false);
68+
69+
yield return null;
70+
71+
Assert.IsTrue(serverPlayer.GetComponent<Rigidbody2D>().isKinematic == Kinematic);
72+
73+
yield return null;
74+
Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned.
75+
}
76+
}
77+
}

com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.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.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Netcode.Components;
4+
using UnityEngine;
5+
using UnityEngine.TestTools;
6+
7+
namespace Unity.Netcode.RuntimeTests.Physics
8+
{
9+
public class NetworkRigidbodyDynamicTest : NetworkRigidbodyTestBase
10+
{
11+
public override bool Kinematic => false;
12+
}
13+
14+
public class NetworkRigidbodyKinematicTest : NetworkRigidbodyTestBase
15+
{
16+
public override bool Kinematic => true;
17+
}
18+
19+
public abstract class NetworkRigidbodyTestBase : BaseMultiInstanceTest
20+
{
21+
protected override int NbClients => 1;
22+
23+
public abstract bool Kinematic { get; }
24+
25+
[UnitySetUp]
26+
public override IEnumerator Setup()
27+
{
28+
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab =>
29+
{
30+
playerPrefab.AddComponent<NetworkTransform>();
31+
playerPrefab.AddComponent<Rigidbody>();
32+
playerPrefab.AddComponent<NetworkRigidbody>();
33+
playerPrefab.GetComponent<Rigidbody>().isKinematic = Kinematic;
34+
});
35+
}
36+
37+
/// <summary>
38+
/// Tests that a server can destroy a NetworkObject and that it gets despawned correctly.
39+
/// </summary>
40+
/// <returns></returns>
41+
[UnityTest]
42+
public IEnumerator TestRigidbodyKinematicEnableDisable()
43+
{
44+
// This is the *SERVER VERSION* of the *CLIENT PLAYER*
45+
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
46+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult));
47+
var serverPlayer = serverClientPlayerResult.Result.gameObject;
48+
49+
// This is the *CLIENT VERSION* of the *CLIENT PLAYER*
50+
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
51+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult));
52+
var clientPlayer = clientClientPlayerResult.Result.gameObject;
53+
54+
Assert.IsNotNull(serverPlayer);
55+
Assert.IsNotNull(clientPlayer);
56+
57+
int waitFor = Time.frameCount + 2;
58+
yield return new WaitUntil(() => Time.frameCount >= waitFor);
59+
60+
// server rigidbody has authority and should have a kinematic mode of false
61+
Assert.True(serverPlayer.GetComponent<Rigidbody>().isKinematic == Kinematic);
62+
63+
// client rigidbody has no authority and should have a kinematic mode of true
64+
Assert.True(clientPlayer.GetComponent<Rigidbody>().isKinematic);
65+
66+
// despawn the server player
67+
serverPlayer.GetComponent<NetworkObject>().Despawn(false);
68+
69+
yield return null;
70+
71+
Assert.IsTrue(serverPlayer.GetComponent<Rigidbody>().isKinematic == Kinematic);
72+
73+
yield return null;
74+
Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned.
75+
}
76+
}
77+
}

com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbodyTest.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)