Skip to content

Commit 902f3cf

Browse files
Update networkvariable.md
Did another pass over this script with some improvements.
1 parent ecd19a0 commit 902f3cf

1 file changed

Lines changed: 99 additions & 48 deletions

File tree

com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md

Lines changed: 99 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,22 @@ The [synchronization and notification example](#synchronization-and-notification
148148
The `OnValueChanged` example shows a simple server-authoritative `NetworkVariable` being used to track the state of a door (open or closed) using an RPC that's sent to the server. Each time the door is used by a client, the `Door.ToggleStateRpc` is invoked and the server-side toggles the state of the door. When the `Door.State.Value` changes, all connected clients are synchronized to the (new) current `Value` and the `OnStateChanged` method is invoked locally on each client.
149149

150150
```csharp
151+
using System.Runtime.CompilerServices;
152+
using Unity.Netcode;
153+
using UnityEngine;
154+
151155
/// <summary>
152-
/// A basic NetworkVariable driven door state example.
156+
/// Example of using a <see cref="NetworkVariable{T}"/> to drive changes
157+
/// in state.
153158
/// </summary>
154-
public class Door : NetworkBehaviour
159+
/// <remarks>
160+
/// This is a simple state driven door example.
161+
/// This script was written with recommended usages patterns in mind.
162+
/// </remarks>
163+
public class Door : NetworkBehaviour, INetworkUpdateSystem
155164
{
156165
/// <summary>
157-
/// Only for UI purposes.
158-
/// This provides an initial configuration state for the door.
166+
/// The two door states.
159167
/// </summary>
160168
public enum DoorStates
161169
{
@@ -169,10 +177,22 @@ public class Door : NetworkBehaviour
169177
[Tooltip("Configures the door's initial state when 1st spawned.")]
170178
public DoorStates InitialState = DoorStates.Closed;
171179

180+
/// <summary>
181+
/// Used for <see cref="CanPlayerToggleState"/> example purposes.
182+
/// When true, only the server can open and close the door.
183+
/// Clients will receive a console log saying they could not open the door.
184+
/// </summary>
185+
public bool IsLocked;
186+
172187
/// <summary>
173188
/// A simple door state where the server has write permissions and everyone has read permissions.
174189
/// </summary>
175-
public NetworkVariable<bool> State = new NetworkVariable<bool>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
190+
private NetworkVariable<DoorStates> m_State = new NetworkVariable<DoorStates>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
191+
192+
/// <summary>
193+
/// The current state of the door.
194+
/// </summary>
195+
public DoorStates CurrentState => m_State.Value;
176196

177197
/// <summary>
178198
/// Invoked while the <see cref="NetworkObject"/> is in the middle of
@@ -187,27 +207,52 @@ public class Door : NetworkBehaviour
187207
{
188208
// Host/Server:
189209
// Applies the configurable state upon spawning.
190-
State.Value = InitialState == DoorStates.Open;
210+
m_State.Value = InitialState;
191211
}
192212
else
193213
{
194214
// Clients:
195215
// Subscribe to changes in the door's state.
196-
State.OnValueChanged += OnStateChanged;
216+
m_State.OnValueChanged += OnStateChanged;
197217
}
198218
}
199219

200220
/// <summary>
201-
/// Invoked once the door and all associated <see cref="NetworkAnimator"/>
202-
/// components have finished the spawn process.
221+
/// Invoked once the door and all associated components
222+
/// have finished the spawn process.
203223
/// </summary>
204224
protected override void OnNetworkPostSpawn()
205225
{
206-
// Everyone updates their door state when finished spawning the door.
207-
UpdateFromState(true);
226+
// Everyone updates their door state when finished spawning the door
227+
// in order to assure the door reflects (visually) its current state.
228+
UpdateFromState();
229+
230+
// Begin to start updating this NetworkBehaviour instance once all
231+
// netcode related components have finished the spawn process.
232+
NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.Update);
208233
base.OnNetworkPostSpawn();
209234
}
210235

236+
/// <summary>
237+
/// Example of using the <see cref="INetworkUpdateSystem"/> usage pattern
238+
/// where it only updates while spawned.
239+
/// </summary>
240+
/// <param name="updateStage">The current update stage being invoked.</param>
241+
public void NetworkUpdate(NetworkUpdateStage updateStage)
242+
{
243+
switch (updateStage)
244+
{
245+
case NetworkUpdateStage.Update:
246+
{
247+
if (Input.GetKeyDown(KeyCode.Space))
248+
{
249+
Interact();
250+
}
251+
break;
252+
}
253+
}
254+
}
255+
211256
/// <summary>
212257
/// Invoked just before this instance runs through its de-spawn
213258
/// sequence. A good time to unsubscribe from things.
@@ -216,56 +261,55 @@ public class Door : NetworkBehaviour
216261
{
217262
if (!IsServer)
218263
{
219-
State.OnValueChanged -= OnStateChanged;
264+
m_State.OnValueChanged -= OnStateChanged;
220265
}
266+
267+
// Stop updating this NetworkBehaviour instance prior to running
268+
// through the de-spawn process.
269+
NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.Update);
221270
base.OnNetworkPreDespawn();
222271
}
223272

224273
/// <summary>
225-
/// Only clients invoke this.
226274
/// Server makes changes to the state.
275+
/// Clients receive the changes in state.
227276
/// </summary>
228277
/// <remarks>
229278
/// When the previous state equals the current state, we are a client
230279
/// that is doing its 1st synchronization of this door instance.
231280
/// </remarks>
232-
/// <param name="previous">The previous state.</param>
233-
/// <param name="current">The current state.</param>
234-
public void OnStateChanged(bool previous, bool current)
281+
/// <param name="previous">The previous <see cref="DoorStates"/> state.</param>
282+
/// <param name="current">The current <see cref="DoorStates"/> state.</param>
283+
public void OnStateChanged(DoorStates previous, DoorStates current)
235284
{
236-
// Update to the current state while also providing a catch for
237-
// the first synchronization where previous == current.
238285
UpdateFromState();
239286
}
240287

241288
/// <summary>
242-
/// Common method used to update the actual door asset based on its current state.
289+
/// Invoke when the state is updated in order to apply the change
290+
/// in door state to the door asset itself.
243291
/// </summary>
244-
/// <param name="isFirstSynchronization">only set upon first spawn by a client</param>
245-
private void UpdateFromState(bool isFirstSynchronization = false)
292+
private void UpdateFromState()
246293
{
247-
if (State.Value)
248-
{
249-
// door is open:
250-
// - rotate door transform
251-
// - play animations, sound etc.
252-
// if first sync, reset to open and don't play sound
253-
}
254-
else
294+
switch(m_State.Value)
255295
{
256-
// door is closed:
257-
// - rotate door transform
258-
// - play animations, sound etc.
259-
// if first sync, reset to closed and don't play sound
260-
}
261-
262-
// Don't log a message about a change in state when first
263-
// synchronizing.
264-
if (!isFirstSynchronization)
265-
{
266-
var openClosed = State.Value ? "open" : "closed";
267-
Debug.Log($"[]The door is now {openClosed}.");
296+
case DoorStates.Closed:
297+
{
298+
// door is open:
299+
// - rotate door transform
300+
// - play animations, sound etc.
301+
/// <see cref="Netcode.Components.Helpers.ComponentCont"
302+
break;
303+
}
304+
case DoorStates.Open:
305+
{
306+
// door is closed:
307+
// - rotate door transform
308+
// - play animations, sound etc.
309+
break;
310+
}
268311
}
312+
Debug.Log($"[{name}] Door is currently {m_State.Value}.");
269313
}
270314

271315
/// <summary>
@@ -277,8 +321,9 @@ public class Door : NetworkBehaviour
277321
/// <returns></returns>
278322
protected virtual bool CanPlayerToggleState(NetworkObject player)
279323
{
280-
// For this example, the door can always be toggled.
281-
return true;
324+
// For this example, if the door "is locked" then clients will
325+
// not be able to open the door but the host-client's player can.
326+
return !IsLocked || player.IsOwnedByServer;
282327
}
283328

284329
/// <summary>
@@ -308,6 +353,12 @@ public class Door : NetworkBehaviour
308353
}
309354
}
310355

356+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
357+
private DoorStates NextToggleState()
358+
{
359+
return m_State.Value == DoorStates.Open ? DoorStates.Closed : DoorStates.Open;
360+
}
361+
311362
/// <summary>
312363
/// Invoked only server-side
313364
/// Primary method to handle toggling the door state.
@@ -319,15 +370,16 @@ public class Door : NetworkBehaviour
319370
var playerObject = NetworkManager.SpawnManager.GetPlayerNetworkObject(clientId);
320371
if (playerObject != null)
321372
{
373+
var nextToggleState = NextToggleState();
322374
if (CanPlayerToggleState(playerObject))
323375
{
324376
// Host toggles the state
325-
State.Value = !State.Value;
377+
m_State.Value = nextToggleState;
326378
UpdateFromState();
327379
}
328380
else
329381
{
330-
ToggleStateFailRpc(RpcTarget.Single(clientId, RpcTargetUse.Temp));
382+
ToggleStateFailRpc(nextToggleState, RpcTarget.Single(clientId, RpcTargetUse.Temp));
331383
}
332384
}
333385
else
@@ -363,11 +415,10 @@ public class Door : NetworkBehaviour
363415
/// </summary>
364416
/// <param name="rpcParams">includes <see cref="RpcReceiveParams.SenderClientId"/> that is automatically populated for you.</param>
365417
[Rpc(SendTo.SpecifiedInParams, InvokePermission = RpcInvokePermission.Server)]
366-
private void ToggleStateFailRpc(RpcParams rpcParams = default)
418+
private void ToggleStateFailRpc(DoorStates doorState, RpcParams rpcParams = default)
367419
{
368420
// Provide player feedback that toggling failed.
369-
var openOrClose = State.Value ? "close" : "open";
370-
Debug.Log($"Failed to {openOrClose} the door!");
421+
Debug.Log($"Failed to {doorState} the door!");
371422
}
372423
}
373424
```

0 commit comments

Comments
 (0)