Skip to content

Commit 60e40b3

Browse files
feat: ClientPlayerAvatarNetworkAnimator added to handle client synchronizations [MTT-3152] (Unity-Technologies#886)
* ClientPlayerAvatarNetworkAnimator introduced to handle client syncs * reformat class * format * changelog addition
1 parent 55a56be commit 60e40b3

8 files changed

+366
-328
lines changed

Assets/Prefabs/Character/PlayerAvatar.prefab

+285-240
Large diffs are not rendered by default.

Assets/Scripts/Gameplay/GameplayObjects/Character/ClientAvatarGuidHandler.cs

-50
This file was deleted.

Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ public override void OnNetworkSpawn()
140140
{
141141
name = "AvatarGraphics" + m_ServerCharacter.OwnerClientId;
142142

143-
if (m_ServerCharacter.TryGetComponent(out ClientAvatarGuidHandler clientAvatarGuidHandler))
143+
if (m_ServerCharacter.TryGetComponent(out ClientPlayerAvatarNetworkAnimator characterNetworkAnimator))
144144
{
145-
m_ClientVisualsAnimator = clientAvatarGuidHandler.graphicsAnimator;
145+
m_ClientVisualsAnimator = characterNetworkAnimator.Animator;
146146
}
147147

148148
m_CharacterSwapper = GetComponentInChildren<CharacterSwap>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Unity.Netcode;
2+
using Unity.Netcode.Components;
3+
using UnityEngine;
4+
5+
namespace Unity.BossRoom.Gameplay.GameplayObjects.Character
6+
{
7+
/// <summary>
8+
/// Component that spawns a PlayerAvatar's Avatar. It does this in two places:
9+
/// 1) either inside OnNetworkSpawn() or
10+
/// 2) inside NetworkAnimator's OnSynchronize method.
11+
/// The latter is necessary for clients receiving initial synchronizing data, where the Animator needs to be present
12+
/// and bound (Animator.Bind()) *before* the incoming animation data is applied.
13+
/// </summary>
14+
public class ClientPlayerAvatarNetworkAnimator : NetworkAnimator
15+
{
16+
[SerializeField]
17+
NetworkAvatarGuidState m_NetworkAvatarGuidState;
18+
19+
bool m_AvatarInstantiated;
20+
21+
public override void OnNetworkSpawn()
22+
{
23+
base.OnNetworkSpawn();
24+
if (!IsClient || m_AvatarInstantiated)
25+
{
26+
return;
27+
}
28+
29+
InstantiateAvatar();
30+
}
31+
32+
public override void OnNetworkDespawn()
33+
{
34+
base.OnNetworkDespawn();
35+
m_AvatarInstantiated = false;
36+
var avatarGraphics = Animator.transform.GetChild(0);
37+
if (avatarGraphics != null)
38+
{
39+
Destroy(avatarGraphics.gameObject);
40+
}
41+
}
42+
43+
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
44+
{
45+
if (NetworkManager.Singleton.IsClient && !m_AvatarInstantiated)
46+
{
47+
InstantiateAvatar();
48+
}
49+
50+
base.OnSynchronize(ref serializer);
51+
}
52+
53+
void InstantiateAvatar()
54+
{
55+
if (Animator.transform.childCount > 0)
56+
{
57+
// we may receive a NetworkVariable's OnValueChanged callback more than once as a client
58+
// this makes sure we don't spawn a duplicate graphics GameObject
59+
return;
60+
}
61+
62+
// spawn avatar graphics GameObject
63+
Instantiate(m_NetworkAvatarGuidState.RegisteredAvatar.Graphics, Animator.transform);
64+
65+
Animator.Rebind();
66+
67+
m_AvatarInstantiated = true;
68+
}
69+
}
70+
}

Assets/Scripts/Gameplay/GameplayObjects/Character/ClientAvatarGuidHandler.cs.meta Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs.meta

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Gameplay/GameplayObjects/Character/ServerAnimationHandler.cs

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections;
32
using Unity.BossRoom.Gameplay.Configuration;
43
using Unity.Netcode;
54
using Unity.Netcode.Components;
@@ -24,18 +23,7 @@ public override void OnNetworkSpawn()
2423
{
2524
if (IsServer)
2625
{
27-
// Wait until next frame before registering on OnValueChanged to make sure NetworkAnimator has spawned before.
28-
StartCoroutine(WaitToRegisterOnLifeStateChanged());
29-
}
30-
}
31-
32-
IEnumerator WaitToRegisterOnLifeStateChanged()
33-
{
34-
yield return new WaitForEndOfFrame();
35-
m_NetworkLifeState.LifeState.OnValueChanged += OnLifeStateChanged;
36-
if (m_NetworkLifeState.LifeState.Value != LifeState.Alive)
37-
{
38-
OnLifeStateChanged(LifeState.Alive, m_NetworkLifeState.LifeState.Value);
26+
m_NetworkLifeState.LifeState.OnValueChanged += OnLifeStateChanged;
3927
}
4028
}
4129

@@ -59,7 +47,7 @@ void OnLifeStateChanged(LifeState previousValue, LifeState newValue)
5947

6048
public override void OnNetworkDespawn()
6149
{
62-
if (IsServer)
50+
if (IsServer && m_NetworkLifeState != null)
6351
{
6452
m_NetworkLifeState.LifeState.OnValueChanged -= OnLifeStateChanged;
6553
}

Assets/Scripts/Gameplay/UI/UIStateDisplayHandler.cs

+3-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections;
32
using Unity.BossRoom.Gameplay.GameplayObjects;
43
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
@@ -44,7 +43,7 @@ public class UIStateDisplayHandler : NetworkBehaviour
4443

4544
ServerCharacter m_ServerCharacter;
4645

47-
ClientAvatarGuidHandler m_ClientAvatarGuidHandler;
46+
ClientPlayerAvatarNetworkAnimator m_ClientPlayerAvatarNetworkAnimator;
4847

4948
NetworkAvatarGuidState m_NetworkAvatarGuidState;
5049

@@ -111,18 +110,11 @@ public override void OnNetworkSpawn()
111110
m_VerticalOffset = new Vector3(0f, m_VerticalScreenOffset, 0f);
112111

113112
// if PC, find our graphics transform and update health through callbacks, if displayed
114-
if (TryGetComponent(out m_ClientAvatarGuidHandler) && TryGetComponent(out m_NetworkAvatarGuidState))
113+
if (TryGetComponent(out m_ClientPlayerAvatarNetworkAnimator) && TryGetComponent(out m_NetworkAvatarGuidState))
115114
{
116115
m_BaseHP = m_NetworkAvatarGuidState.RegisteredAvatar.CharacterClass.BaseHP;
117116

118-
if (m_ServerCharacter.clientCharacter)
119-
{
120-
TrackGraphicsTransform(m_ServerCharacter.clientCharacter.gameObject);
121-
}
122-
else
123-
{
124-
m_ClientAvatarGuidHandler.AvatarGraphicsSpawned += TrackGraphicsTransform;
125-
}
117+
m_TransformToTrack = m_ClientPlayerAvatarNetworkAnimator.Animator.transform;
126118

127119
if (m_DisplayHealth)
128120
{
@@ -154,11 +146,6 @@ void OnDisable()
154146
m_NetworkHealthState.HitPointsReplenished -= DisplayUIHealth;
155147
m_NetworkHealthState.HitPointsDepleted -= RemoveUIHealth;
156148
}
157-
158-
if (m_ClientAvatarGuidHandler)
159-
{
160-
m_ClientAvatarGuidHandler.AvatarGraphicsSpawned -= TrackGraphicsTransform;
161-
}
162149
}
163150

164151
void DisplayUIName()
@@ -213,11 +200,6 @@ IEnumerator WaitToHideHealthBar()
213200
m_UIState.HideHealth();
214201
}
215202

216-
void TrackGraphicsTransform(GameObject graphicsGameObject)
217-
{
218-
m_TransformToTrack = graphicsGameObject.transform;
219-
}
220-
221203
/// <remarks>
222204
/// Moving UI objects on LateUpdate ensures that the game camera is at its final position pre-render.
223205
/// </remarks>

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Additional documentation and release notes are available at [Multiplayer Documen
1414
* ClientConnectedState has been modified to account for server/host now populating DisconnectReason before disconnecting a client before shutting down
1515
* Upgraded editor version to 2022.3.22f1 (#884)
1616
* com.unity.render-pipelines.universal upgraded to v14.0.10
17+
* ClientPlayerAvatarNetworkAnimator has been created to: instantiate the player model based on a networked GUID, rebind this rig to the player's Animator, and apply synchronize data to said Animator (#886)
18+
* This change allows for NetworkAnimator's synchronize step to properly apply its sync data to clients instead of applying an animation state change on OnNetworkSpawn()
19+
* A side-effect of this change has been that a coroutine that had been awaiting the assignment of NetworkAnimator has since been removed as it is no longer an issue on Netcode for GameObjects (since v1.3.1)
1720

1821
### Cleanup
1922
* Removed NetworkObject from MainMenuState (#881)

0 commit comments

Comments
 (0)