Using the same scene as in the previous lesson, let's see how to easily sync animation over the network.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
Animation | Bindings
We haven't mentioned it before, but the player Prefab does a lot more than just syncing its position and rotation.
If you perform the actions mentioned in the controls, you will notice that animation is also replicated across Clients. This is done via synced Animator parameters (and Network Commands, but we cover these in the next lesson).
Very much like in the example about position and rotation, just sending these across the network allows us to synchronize of the state of animation, making it look like network-instantiated Prefabs on other Clients (the other players) are performing actions.
Open the player Prefab located in the Characters/Player
__ folder. Browse the Hierarchy until you find the sub-object called PlayerModel. You will notice it has an Animator
component. Select this object and open the Animator window.
As you can imagine, animation is controlled by a few parameters of different types (int, bool, float).
Make sure to keep the GameObject with the Animator component selected, and open the coherence Configure window:
You will see that a group of animation parameters are being synced. It's that simple: just checking them will start sending the values across, once the game starts.
Did you notice that you are able to configure bindings even if this particular GameObject doesn't have a CoherenceSync
component on it? This is done via the one attached to the root of the player Prefab. These parameters are what we call deep bindings. Learn more in the Complex hierarchies lesson.
There is only one piece missing: animation triggers. They are not a variable holding a value that changes over time, but rather an action that happens instantaneously, so we will see how to sync them in the next lesson using Network Commands.
Using the same scene as in the previous lesson, we now take a look at another way to make Clients communicate: Network Commands. Network Commands are like sending direct messages to objects, instead of syncing the value of a variable.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
Q or D-pad up: Wave
Building on top of previous examples, let's now focus on two key player actions. Press Space to jump, or Q to wave. For both of these actions to play their animation, we need to send a command over the network to call Animator.SetTrigger()
on the other Client.
Like before, select the player Prefab located in the Characters/Player
__ folder, and browse the Hierarchy until you find the sub-object called PlayerModel.
Open the coherence Configure window on the Methods tab:
You can see how the method Animator.SetTrigger(string)
has been marked as a Network Command. Once this is done, it is possible to invoke it over the network.
You can find the code doing so in the Hail
class (located in /Scripts/PlayerActions/Hail.cs
):
With this simple line of code, we're asking to:
Send a command to an object of class Animator
.
Invoke a method called Animator.SetTrigger
.
Do so only for network entities other than the one with authority (MessageTarget.Other
).
Pass the string "Hail"
as the first parameter (which is the name of the animation trigger parameter).
Because we don't invoke this on the one with authority, you will notice that just before invoking the Network Command, we also call SetTrigger
locally in the usual way:
An alternative to avoid this would have been to pass MessageTarget.All
to CoherenceSync.SendCommand()
, but in this case it made more sense to avoid that additional network traffic and just execute locally.
In this example we used a Network Command to trigger a transition in an animation state machine, but they can be used to call any instantaneous behavior that has to be replicated over the network. As an example, it is also used in the Persistence sample to change a number in a UI element across all Clients.
This scene demonstrates the simplest networking scenario possible with coherence. Characters sync their position and rotation, which immediately creates a feeling of presence. Someone else is connected!
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
CoherenceSync | Bindings | Component behaviours | Authority
Upon connecting, the PlayerHandler
script (attached to the PlayerHandler GameObject) creates a new instance of the character Prefab, located in the Prefabs/Characters
folder. When disconnecting, the same script destroys the instance created.
coherence takes care of keeping all Game Clients in sync regarding network entities. When another Client connects, a new instance of your game character is instantiated in their scene, and a copy of their character is instantiated into yours. This is called network instantiation.
Now you can move and jump around, and you will see other characters move too.
You can see what is synced over the network by selecting the Prefab asset, and opening coherence's Configuration window (either by clicking on the Configure button on the CoherenceSync
component, or by going to coherence > GameObject setup > Configure).
When this window opens on the first tab you will notice that, at the very top, Transform.position
and Transform.rotation
are checked.
Are you wondering why the position is checked by default? You'll find answers in the lesson regarding LiveQueries.
This is the data being transferred over the network. Each Client sends position and rotation of the character they have authority over to every other connected Client, every time there is a change to it that is significant enough. We call these bindings.
Each connected Client receives these values and applies them to the Transform
component of their own instance of the remote player character.
To ensure that Clients don't modify properties of entities they don't have authority on, some components are either disabled or changed on instances that are non-authoritative. If you open the third tab of the Configuration window, you will see that 3 components are modified:
In particular:
The PlayerInput
and KinematicMove
scripts are disabled.
The Rigidbody
component is made kinematic.
One important concept to get familiar with is the fact that every networked entity exists as a GameObject on every Client currently connected. However, only one of them has what we call authority over the network entity, and can control its synced variables.
For instance, if we play this scene with two Clients, each one will have 2 instances in their respective worlds:
This is something to keep in mind as you decide which components have to keep running or be disabled on remote instances, in order to not have the same code running unnecessarily on various Clients. This could create a conflict or put the two GameObjects in a very different state, generating unwanted results.
In the Unity Editor, the name of a GameObject and the icon next to it informs you about its current authority state (see image).
There are two types of authority in coherence: State and Input. For the sake of simplicity, in this project we often refer just to a generic "authority", and what we mean is State authority. Go here for more info on authority.
If you want to see which entities are currently local and which ones are remote, we included a debug visualisation in this project. Hit the Tab key (or click the Joystick) to switch to a view that shows authority. You can keep playing the game while in this view, and see how things change.