feat: ClientDriven upgrade to Netcode for GameObjects v1.8.1, client position place of execution change, Universal Rpc upgrade [MTT-7985] (#164)
* upgrade to Netcode for GameObjects v1.8.1, centralizing position changes client-side to one class * redundant cast to NetworkObject removed, summary fix * starting position set to origin once past all spawn points * Rpcs converted to Universal Rpcs * changelog addition * more context on pattern change * renaming of auto-gen method
This commit is contained in:
Родитель
559f6678ac
Коммит
1072e7d471
|
@ -25,13 +25,13 @@ Transform:
|
|||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7567938585585123398}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 303777381631976334}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!135 &6758041290174012784
|
||||
SphereCollider:
|
||||
|
@ -180,7 +180,7 @@ PrefabInstance:
|
|||
addedObject: {fileID: 5801232430748397462}
|
||||
- targetCorrespondingSourceObject: {fileID: 4416926081852918481, guid: 64dce48905ffd9b4293e595fa6941544, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 7243582772389402832}
|
||||
addedObject: {fileID: 5997952762632541840}
|
||||
- targetCorrespondingSourceObject: {fileID: 8187455079231382173, guid: 64dce48905ffd9b4293e595fa6941544, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 3615669438616969171}
|
||||
|
@ -230,11 +230,13 @@ MonoBehaviour:
|
|||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
GlobalObjectIdHash: 951099334
|
||||
GlobalObjectIdHash: 3868643671
|
||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||
AlwaysReplicateAsRoot: 0
|
||||
SynchronizeTransform: 1
|
||||
ActiveSceneSynchronization: 0
|
||||
SceneMigrationSynchronization: 1
|
||||
SpawnWithObservers: 1
|
||||
DontDestroyWithOwner: 0
|
||||
AutoObjectParentSync: 1
|
||||
--- !u!114 &-2755784201116707001
|
||||
|
@ -252,6 +254,8 @@ MonoBehaviour:
|
|||
isObjectPickedUp:
|
||||
m_InternalValue: 0
|
||||
m_LocalHeldPosition: {x: 0, y: 2.85, z: 0}
|
||||
spawnPosition:
|
||||
m_InternalValue: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &5667156634780145037
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -284,7 +288,7 @@ MonoBehaviour:
|
|||
m_EditorClassIdentifier:
|
||||
TransitionStateInfoList: []
|
||||
m_Animator: {fileID: 5969265393934875124}
|
||||
--- !u!114 &7243582772389402832
|
||||
--- !u!114 &5997952762632541840
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
|
@ -293,9 +297,10 @@ MonoBehaviour:
|
|||
m_GameObject: {fileID: 1116025501350672692}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: df2868252ab5c4d1da357e8f11f1b524, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: 53b28d1b2108d4d199dae3ac8f537c27, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
UseUnreliableDeltas: 0
|
||||
SyncPositionX: 1
|
||||
SyncPositionY: 1
|
||||
SyncPositionZ: 1
|
||||
|
@ -314,6 +319,7 @@ MonoBehaviour:
|
|||
InLocalSpace: 0
|
||||
Interpolate: 1
|
||||
SlerpPosition: 0
|
||||
m_ServerPlayerMove: {fileID: -2755784201116707001}
|
||||
--- !u!114 &1116025501350672696 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 4416926081852918493, guid: 64dce48905ffd9b4293e595fa6941544, type: 3}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using Unity.Multiplayer.Samples.Utilities.ClientAuthority;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Component inheriting from <see cref="ClientNetworkTransform"/>, where server-driven player position changes are
|
||||
/// applied to the owning client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Handling movement inside this component's OnNetworkSpawn method only ensures the mitigation of race condition issues
|
||||
/// arising due to the execution order of other NetworkBehaviours' OnNetworkSpawn methods.
|
||||
/// </remarks>
|
||||
[RequireComponent(typeof(ServerPlayerMove))]
|
||||
[DisallowMultipleComponent]
|
||||
public class ClientDrivenNetworkTransform : ClientNetworkTransform
|
||||
{
|
||||
[SerializeField]
|
||||
ServerPlayerMove m_ServerPlayerMove;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsClient && IsOwner)
|
||||
{
|
||||
SetPosition(Vector3.zero, m_ServerPlayerMove.spawnPosition.Value);
|
||||
m_ServerPlayerMove.spawnPosition.OnValueChanged += SetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
base.OnNetworkDespawn();
|
||||
|
||||
if (m_ServerPlayerMove != null)
|
||||
{
|
||||
m_ServerPlayerMove.spawnPosition.OnValueChanged -= SetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPosition(Vector3 previousValue, Vector3 newValue)
|
||||
{
|
||||
transform.position = newValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53b28d1b2108d4d199dae3ac8f537c27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -38,10 +38,10 @@ public class ClientPlayerMove : NetworkBehaviour
|
|||
Cursor.visible = false;
|
||||
|
||||
// ThirdPersonController & CharacterController are enabled only on owning clients. Ghost player objects have
|
||||
// these two components disabled, and will enable a CapsuleCollider. Per the CharacterController documentation:
|
||||
// these two components disabled, and will enable a CapsuleCollider. Per the CharacterController documentation:
|
||||
// https://docs.unity3d.com/Manual/CharacterControllers.html, a Character controller can push rigidbody
|
||||
// objects aside while moving but will not be accelerated by incoming collisions. This means that a primitive
|
||||
// CapsuleCollider must instead be used for ghost clients to simulate collisions between owning players and
|
||||
// CapsuleCollider must instead be used for ghost clients to simulate collisions between owning players and
|
||||
// ghost clients.
|
||||
m_ThirdPersonController.enabled = false;
|
||||
m_CapsuleCollider.enabled = false;
|
||||
|
@ -77,7 +77,7 @@ public class ClientPlayerMove : NetworkBehaviour
|
|||
{
|
||||
if (m_ServerPlayerMove.isObjectPickedUp.Value)
|
||||
{
|
||||
m_ServerPlayerMove.DropObjectServerRpc();
|
||||
m_ServerPlayerMove.ServerDropObjectRpc();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -99,7 +99,7 @@ public class ClientPlayerMove : NetworkBehaviour
|
|||
// Netcode is a server driven SDK. Shared objects like ingredients need to be interacted with using ServerRPCs. Therefore, there
|
||||
// will be a delay between the button press and the reparenting.
|
||||
// This delay could be hidden with some animations/sounds/VFX that would be triggered here.
|
||||
m_ServerPlayerMove.PickupObjectServerRpc(netObj);
|
||||
m_ServerPlayerMove.ServerPickupObjectRpc(netObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using Unity.Netcode;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
|
@ -17,6 +16,8 @@ public class ServerPlayerMove : NetworkBehaviour
|
|||
[SerializeField]
|
||||
Vector3 m_LocalHeldPosition;
|
||||
|
||||
public NetworkVariable<Vector3> spawnPosition;
|
||||
|
||||
// DOC START HERE
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
|
@ -33,12 +34,21 @@ public class ServerPlayerMove : NetworkBehaviour
|
|||
|
||||
void OnServerSpawnPlayer()
|
||||
{
|
||||
// Note: specific to Netcode for GameObjects v1.8.0 & v1.8.1
|
||||
// Previous versions of this sample utilizing Netcode for GameObjects <v1.8.0 had set the player's position at
|
||||
// this point. However, a regression with these two particular versions forced the new pattern for modifying a
|
||||
// player's initial position, which is:
|
||||
// we store the server-determined spawn position inside of a NetworkVariable, and have that be consumed by the
|
||||
// owning client, inside ClientDrivenNetworkTransform. This approach navigates potential OnNetworkSpawn race
|
||||
// conditions that popped up in these two Netcode versions, and is now a recommended approach for setting spawn
|
||||
// positions on OnNetworkSpawn for owner-authoritative NetworkTransforms.
|
||||
|
||||
// this is done server side, so we have a single source of truth for our spawn point list
|
||||
var spawnPoint = ServerPlayerSpawnPoints.Instance.ConsumeNextSpawnPoint();
|
||||
var spawnPosition = spawnPoint ? spawnPoint.transform.position : Vector3.zero;
|
||||
transform.position = spawnPosition;
|
||||
spawnPosition.Value = spawnPoint != null ? spawnPoint.transform.position : Vector3.zero;
|
||||
|
||||
// A note specific to owner authority:
|
||||
// A note specific to owner authority (see the note above why this would not work for Netcode for GameObjects
|
||||
// v1.8.0 & 1.8.1):
|
||||
// Setting the position works as and can be set in OnNetworkSpawn server-side unless there is a
|
||||
// CharacterController that is enabled by default on the authoritative side. With CharacterController, it
|
||||
// needs to be disabled by default (i.e. in Awake), the server applies the position (OnNetworkSpawn), and then
|
||||
|
@ -47,15 +57,19 @@ public class ServerPlayerMove : NetworkBehaviour
|
|||
// transform after synchronization with the initial position, thus overwriting the synchronized position.
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
public void PickupObjectServerRpc(ulong objToPickupID)
|
||||
[Rpc(SendTo.Server)]
|
||||
public void ServerPickupObjectRpc(ulong objToPickupID)
|
||||
{
|
||||
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(objToPickupID, out var objectToPickup);
|
||||
if (objectToPickup == null || objectToPickup.transform.parent != null) return; // object already picked up, server authority says no
|
||||
|
||||
if (objectToPickup.TryGetComponent(out NetworkObject networkObject) && networkObject.TrySetParent(transform))
|
||||
if (objectToPickup == null || objectToPickup.transform.parent != null)
|
||||
{
|
||||
m_PickedUpObject = networkObject;
|
||||
// object already picked up, server authority says no
|
||||
return;
|
||||
}
|
||||
|
||||
if (objectToPickup.TrySetParent(transform))
|
||||
{
|
||||
m_PickedUpObject = objectToPickup;
|
||||
objectToPickup.transform.localPosition = m_LocalHeldPosition;
|
||||
objectToPickup.GetComponent<ServerIngredient>().ingredientDespawned += IngredientDespawned;
|
||||
isObjectPickedUp.Value = true;
|
||||
|
@ -68,14 +82,14 @@ public class ServerPlayerMove : NetworkBehaviour
|
|||
isObjectPickedUp.Value = false;
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
public void DropObjectServerRpc()
|
||||
[Rpc(SendTo.Server)]
|
||||
public void ServerDropObjectRpc()
|
||||
{
|
||||
if (m_PickedUpObject != null)
|
||||
{
|
||||
m_PickedUpObject.GetComponent<ServerIngredient>().ingredientDespawned -= IngredientDespawned;
|
||||
// can be null if enter drop zone while carrying
|
||||
m_PickedUpObject.transform.parent = null;
|
||||
m_PickedUpObject.TrySetParent(parent: (Transform)null);
|
||||
m_PickedUpObject = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"com.unity.ide.vscode": "1.2.5",
|
||||
"com.unity.inputsystem": "1.7.0",
|
||||
"com.unity.multiplayer.samples.coop": "https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop.git?path=/Packages/com.unity.multiplayer.samples.coop#v2.4.0",
|
||||
"com.unity.netcode.gameobjects": "1.7.1",
|
||||
"com.unity.netcode.gameobjects": "1.8.1",
|
||||
"com.unity.render-pipelines.universal": "14.0.9",
|
||||
"com.unity.test-framework": "1.1.33",
|
||||
"com.unity.textmeshpro": "3.0.6",
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.netcode.gameobjects": {
|
||||
"version": "1.7.1",
|
||||
"version": "1.8.1",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -9,6 +9,11 @@
|
|||
|
||||
### Client Driven
|
||||
|
||||
#### Changed
|
||||
- Upgraded to Netcode for GameObjects v1.8.1 (#164)
|
||||
- Upgraded to the newer API for Rpcs, Universal Rpcs
|
||||
- The place of execution for a client's position was moved to ClientNetworkTransform child class, ClientDrivenNetworkTransform. This ensures no race condition issues on a client's first position sync. Server code now modifies a NetworkVariable that client-owned instances of ClientDrivenNetworkTransform use on OnNetworkSpawn to initially move a player
|
||||
|
||||
#### Fixed
|
||||
- Added Spawner with event executed on Server Start to fix inconsistent ghost ingredients issue (#157)
|
||||
|
||||
|
@ -86,13 +91,6 @@
|
|||
- Upgraded to Netcode for GameObjects v1.6.0 (#134)
|
||||
- Upgraded sample to 2022.3.9f1 LTS (#134)
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Dedicated Game Server
|
||||
todo: the changelog entry for this sample will be updated with subsequent PRs before being merged when ready
|
||||
|
||||
- New Dedicated Game Server sample. (#1) This sample's goal is to demonstrate how to use the different tools and packages available to create a game using the dedicated server approach. It requires editor version 2023.3.
|
||||
|
||||
## [1.3.0] - 2023-07-07
|
||||
|
||||
### Dynamic Addressables Network Prefabs
|
||||
|
|
Загрузка…
Ссылка в новой задаче