-
Notifications
You must be signed in to change notification settings - Fork 450
feat: network physics #1175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: network physics #1175
Changes from all commits
549f84c
a043487
45ce892
f068026
d50b0dc
f095947
5b47b25
86a72b0
04fbac5
4507557
95e4c2b
5e89e44
6959e02
5409091
311e3ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using UnityEngine; | ||
|
||
namespace Unity.Netcode.Components | ||
{ | ||
/// <summary> | ||
/// NetworkRigidbody allows for the use of <see cref="Rigidbody"/> on network objects. By controlling the kinematic | ||
/// mode of the rigidbody and disabling it on all peers but the authoritative one. | ||
/// </summary> | ||
[RequireComponent(typeof(Rigidbody))] | ||
[RequireComponent(typeof(NetworkTransform))] | ||
public class NetworkRigidbody : NetworkBehaviour | ||
{ | ||
private Rigidbody m_Rigidbody; | ||
|
||
private bool m_OriginalKinematic; | ||
|
||
// Used to cache the authority state of this rigidbody during the last frame | ||
private bool m_IsAuthority; | ||
|
||
/// <summary> | ||
/// Gets a bool value indicating whether this <see cref="NetworkRigidbody"/> on this peer currently holds authority. | ||
/// </summary> | ||
internal bool HasAuthority => NetworkManager.IsServer; // TODO update this once we support owner authoritative NetworkTransform. | ||
|
||
private void Awake() | ||
{ | ||
m_Rigidbody = GetComponent<Rigidbody>(); | ||
} | ||
|
||
// Currently commented out because it is not needed as authority currently can't change at runtime. | ||
// private void FixedUpdate() | ||
// { | ||
// if (NetworkManager.IsListening) | ||
// { | ||
// if (HasAuthority != m_IsAuthority) | ||
// { | ||
// m_IsAuthority = HasAuthority; | ||
// UpdateRigidbodyKinematicMode(); | ||
// } | ||
// } | ||
// } | ||
|
||
// Puts the rigidbody in a kinematic non-interpolated mode on everyone but the server. | ||
private void UpdateRigidbodyKinematicMode() | ||
{ | ||
if (m_IsAuthority == false) | ||
{ | ||
m_OriginalKinematic = m_Rigidbody.isKinematic; | ||
m_Rigidbody.isKinematic = true; | ||
} | ||
else | ||
{ | ||
// Resets the rigidbody back to it's non replication only state. Happens on shutdown and when authority is lost | ||
m_Rigidbody.isKinematic = m_OriginalKinematic; | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override void OnNetworkSpawn() | ||
{ | ||
m_IsAuthority = HasAuthority; | ||
m_OriginalKinematic = m_Rigidbody.isKinematic; | ||
UpdateRigidbodyKinematicMode(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override void OnNetworkDespawn() | ||
{ | ||
UpdateRigidbodyKinematicMode(); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using UnityEngine; | ||
|
||
namespace Unity.Netcode.Components | ||
{ | ||
/// <summary> | ||
/// NetworkRigidbody allows for the use of <see cref="Rigidbody2D"/> on network objects. By controlling the kinematic | ||
/// mode of the rigidbody and disabling it on all peers but the authoritative one. | ||
/// </summary> | ||
[RequireComponent(typeof(Rigidbody2D))] | ||
[RequireComponent(typeof(NetworkTransform))] | ||
public class NetworkRigidbody2D : NetworkBehaviour | ||
{ | ||
private Rigidbody2D m_Rigidbody; | ||
|
||
private bool m_OriginalKinematic; | ||
|
||
// Used to cache the authority state of this rigidbody during the last frame | ||
private bool m_IsAuthority; | ||
|
||
/// <summary> | ||
/// Gets a bool value indicating whether this <see cref="NetworkRigidbody2D"/> on this peer currently holds authority. | ||
/// </summary> | ||
internal bool HasAuthority => NetworkManager.IsServer; // TODO update this once we support owner authoritative NetworkTransform. | ||
|
||
private void Awake() | ||
{ | ||
m_Rigidbody = GetComponent<Rigidbody2D>(); | ||
} | ||
|
||
// Currently commented out because it is not needed as authority currently can't change at runtime. | ||
// private void FixedUpdate() | ||
// { | ||
// if (NetworkManager.IsListening) | ||
// { | ||
// if (HasAuthority != m_IsAuthority) | ||
// { | ||
// m_IsAuthority = HasAuthority; | ||
// UpdateRigidbodyKinematicMode(); | ||
// } | ||
// } | ||
// } | ||
|
||
// Puts the rigidbody in a kinematic non-interpolated mode on everyone but the server. | ||
private void UpdateRigidbodyKinematicMode() | ||
{ | ||
if (m_IsAuthority == false) | ||
{ | ||
m_OriginalKinematic = m_Rigidbody.isKinematic; | ||
m_Rigidbody.isKinematic = true; | ||
} | ||
else | ||
{ | ||
// Resets the rigidbody back to it's non replication only state. Happens on shutdown and when authority is lost | ||
m_Rigidbody.isKinematic = m_OriginalKinematic; | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override void OnNetworkSpawn() | ||
{ | ||
m_IsAuthority = HasAuthority; | ||
m_OriginalKinematic = m_Rigidbody.isKinematic; | ||
UpdateRigidbodyKinematicMode(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override void OnNetworkDespawn() | ||
{ | ||
m_IsAuthority = false; | ||
UpdateRigidbodyKinematicMode(); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System.Collections; | ||
using NUnit.Framework; | ||
using Unity.Netcode.Components; | ||
using UnityEngine; | ||
using UnityEngine.TestTools; | ||
|
||
namespace Unity.Netcode.RuntimeTests.Physics | ||
{ | ||
public class NetworkRigidbody2DDynamicTest : NetworkRigidbodyTestBase | ||
{ | ||
public override bool Kinematic => false; | ||
} | ||
|
||
public class NetworkRigidbody2DKinematicTest : NetworkRigidbodyTestBase | ||
{ | ||
public override bool Kinematic => true; | ||
} | ||
|
||
public abstract class NetworkRigidbody2DTestBase : BaseMultiInstanceTest | ||
{ | ||
protected override int NbClients => 1; | ||
|
||
public abstract bool Kinematic { get; } | ||
|
||
[UnitySetUp] | ||
public override IEnumerator Setup() | ||
{ | ||
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab => | ||
{ | ||
playerPrefab.AddComponent<NetworkTransform>(); | ||
playerPrefab.AddComponent<Rigidbody2D>(); | ||
playerPrefab.AddComponent<NetworkRigidbody>(); | ||
playerPrefab.GetComponent<Rigidbody2D>().isKinematic = Kinematic; | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. | ||
/// </summary> | ||
/// <returns></returns> | ||
[UnityTest] | ||
public IEnumerator TestRigidbodyKinematicEnableDisable() | ||
{ | ||
// This is the *SERVER VERSION* of the *CLIENT PLAYER* | ||
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>(); | ||
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); | ||
var serverPlayer = serverClientPlayerResult.Result.gameObject; | ||
|
||
// This is the *CLIENT VERSION* of the *CLIENT PLAYER* | ||
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>(); | ||
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult)); | ||
var clientPlayer = clientClientPlayerResult.Result.gameObject; | ||
|
||
Assert.IsNotNull(serverPlayer); | ||
Assert.IsNotNull(clientPlayer); | ||
|
||
int waitFor = Time.frameCount + 2; | ||
yield return new WaitUntil(() => Time.frameCount >= waitFor); | ||
|
||
// server rigidbody has authority and should have a kinematic mode of false | ||
Assert.True(serverPlayer.GetComponent<Rigidbody2D>().isKinematic == Kinematic); | ||
|
||
// client rigidbody has no authority and should have a kinematic mode of true | ||
Assert.True(clientPlayer.GetComponent<Rigidbody2D>().isKinematic); | ||
|
||
// despawn the server player | ||
serverPlayer.GetComponent<NetworkObject>().Despawn(false); | ||
|
||
yield return null; | ||
|
||
Assert.IsTrue(serverPlayer.GetComponent<Rigidbody2D>().isKinematic == Kinematic); | ||
|
||
yield return null; | ||
Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned. | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System.Collections; | ||
using NUnit.Framework; | ||
using Unity.Netcode.Components; | ||
using UnityEngine; | ||
using UnityEngine.TestTools; | ||
|
||
namespace Unity.Netcode.RuntimeTests.Physics | ||
{ | ||
public class NetworkRigidbodyDynamicTest : NetworkRigidbodyTestBase | ||
{ | ||
public override bool Kinematic => false; | ||
} | ||
|
||
public class NetworkRigidbodyKinematicTest : NetworkRigidbodyTestBase | ||
{ | ||
public override bool Kinematic => true; | ||
} | ||
|
||
public abstract class NetworkRigidbodyTestBase : BaseMultiInstanceTest | ||
{ | ||
protected override int NbClients => 1; | ||
|
||
public abstract bool Kinematic { get; } | ||
|
||
[UnitySetUp] | ||
public override IEnumerator Setup() | ||
{ | ||
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab => | ||
{ | ||
playerPrefab.AddComponent<NetworkTransform>(); | ||
playerPrefab.AddComponent<Rigidbody>(); | ||
playerPrefab.AddComponent<NetworkRigidbody>(); | ||
playerPrefab.GetComponent<Rigidbody>().isKinematic = Kinematic; | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. | ||
/// </summary> | ||
/// <returns></returns> | ||
[UnityTest] | ||
public IEnumerator TestRigidbodyKinematicEnableDisable() | ||
{ | ||
// This is the *SERVER VERSION* of the *CLIENT PLAYER* | ||
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>(); | ||
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); | ||
var serverPlayer = serverClientPlayerResult.Result.gameObject; | ||
|
||
// This is the *CLIENT VERSION* of the *CLIENT PLAYER* | ||
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>(); | ||
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult)); | ||
var clientPlayer = clientClientPlayerResult.Result.gameObject; | ||
|
||
Assert.IsNotNull(serverPlayer); | ||
Assert.IsNotNull(clientPlayer); | ||
|
||
int waitFor = Time.frameCount + 2; | ||
yield return new WaitUntil(() => Time.frameCount >= waitFor); | ||
|
||
// server rigidbody has authority and should have a kinematic mode of false | ||
Assert.True(serverPlayer.GetComponent<Rigidbody>().isKinematic == Kinematic); | ||
|
||
// client rigidbody has no authority and should have a kinematic mode of true | ||
Assert.True(clientPlayer.GetComponent<Rigidbody>().isKinematic); | ||
|
||
// despawn the server player | ||
serverPlayer.GetComponent<NetworkObject>().Despawn(false); | ||
|
||
yield return null; | ||
|
||
Assert.IsTrue(serverPlayer.GetComponent<Rigidbody>().isKinematic == Kinematic); | ||
|
||
yield return null; | ||
Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned. | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this change your plans now that we'll have ClientNetworkTransform?