Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
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.
Every now and then it makes sense to parent network entities to each other, for instance when creating vehicles or an elevator. In this sample scene we'll see what are the implications of that, and how coherence uses this to optimize network traffic.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
Moving platforms | Local positions | Parenting at runtime | Optimization
This wintery setting contains 2 moving platforms running along splines. Players can jump on them and they will receive the platform's movement and rotation, while still being able to move relative to the platform itself.
This scene doesn't require anything special in terms of network setup to work.
Direct parenting of network entities in coherence happens exactly like usual, with a simple transform.SetParent()
, or even just dragging one GameObject onto another in the Unity Editor's Hierarchy window. The player's Move
script is set to recognize the moving platforms when it lands on them, and it just parents itself to it.
As for the platforms, they are just moving themselves as kinematic rigid bodies, following the path of their spline (see the FloatingPlatform
script). Their position and rotation is synced on the network, and the first Client to connect assumes authority over them.
Once directly parented, coherence automatically switches to sync the child's position and rotation as local, rather than in world space. This means that when child entities don't move within their parent, no data about them is being sent across the network.
Imagine for instance a situation where 3 players are riding one of the platforms and not moving, only the coordinates of the platform are being synced every frame.
You might have noticed we always mentioned "direct" parenting. One limitation of this simple setup is that the parented network entity has to be a first-level child of the parent one. This doesn't exclude that the parent can have other child GameObjects (and other networked entities!), but networked entities have to be a direct child.
A hierarchy could look like this:
Platform
Player
Character graphics
Bones
...
Platform's graphics
...
(In bold is the root of each Prefab, which has a CoherenceSync
component)
You can even parent multiple network entities to each other. For example, a networked character holding a networked crate, riding a networked elevator, on a networked spaceship. In that case:
Spaceship
Elevator1
Elevator graphics
Elevator2
Player
Crate
Character graphics
Elevator graphics
Spaceship graphics
...
For cases like these, coherence takes care of them automatically. More complex hierarchies require a different handling, and we cover them in another lesson.
One final note: re-parenting has to happen at runtime. Currently, coherence doesn't support the authoring of Prefabs with more than one CoherenceSync
nested inside each other, but this will come in a future version.
We have seen a lot of examples with objects belonging to a Client, and when that Client disconnects, they disappear with them. We call these session-based entities.
But coherence also has a built-in system to make objects survive the disconnection of a Client, and be ready to be adopted by another Client or a Simulator. We call these objects persistent. Persistent objects stay on the Replication Server even if no Client is connected, creating the feeling that the game world is alive beyond an individual player session.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
P or Right shoulder button: Plant a flower (hold to preview placement)
Persistence | Simulator | Requesting authority
Players can plant flowers in this little valley. Each flower has 3 phases: starts as a bud, blooms into a full flower, and then withers after some time.
Creating a flower generates a new, persistent network entity. Even if the Client disconnects, the flower will persist on the server. When they reconnect, they will see the flower at their correct stage of growth (this is a little trick we explain later).
Planting too many flowers starts erasing older flowers. A button in the UI allows clearing all flowers (belonging to any player) at any time.
When using the plant action, any connected player instantiates a copy of the Flower Prefab (located in the /Prefabs/Nature
folder).
By selecting the Prefab asset, we can see its CoherenceSync
component is set up like this:
In particular, the Lifetime __ is set to Persistent. This means that when the Client who plants a flower disconnects, the network entity won't be automatically destroyed. Auto-adopt Orphan __ set to on makes it so the next player who sees the flower instantly adopts it, and keeps simulating its growth.
Opening coherence's Configuration window, you will see that we sync position, rotation, and a variable called timePlanted
:
When it gets instantiated, the flower writes the current UNIX timestamp into the timePlanted
variable. This variable never changes after this, and is used to reconstruct the phase in which the flower is in (see below). Similarly, as the flower is not moving, position and rotation are only synced at the time of planting.
Once a flower has spawned, all of its logic runs locally (no coherence involved). An internal timer calculates what phase it should be in by looking at the timePlanted
property and doing the math, and playing the appropriate animations and particles as a result.
coherence supports the ability to have an instance of the game active in the cloud, running some logic all the time (a Simulator). However, this might be an expensive setup, and it's good advice to think things through differently to keep the cost of running your game lower.
To achieve this, the flowers of this scene store the Flower.timePlanted
value on the Replication Server. A Replication Server with no connected Clients is dormant, and has a very low cost to run. So the flowers are not actually simulating, they are just waiting.
When a new Client comes online and this value is synced to them, they immediately fast-forward the phase of the flower to the correct value, and then they start simulating locally as normal.
This gives the players the perception that things are still running even when they are not connected.
This setup is not bulletproof, and could be easily cheated if a player comes online with a modified Client, changing the algorithm calculating the flowers' phase.
But for a game in which this calculation is not critical, especially if it doesn't affect other player's experience of the game, this can be a nice setup to cut some costs.
Every Client can, at any time, remove all flowers from the scene by clicking a button in the UI.
It's important to remember that you shouldn't call Destroy()
on a network entity on which the Client doesn't have authority on. To achieve this, we first request authority on remote flowers and listen for a reply. Once obtained it, we destroy them.
Check the code at the end of the Flower
script:
As we discussed in the Physics lesson, switching authority is a network operation that is asynchronous, so we need to wait for the reply from the player who currently has authority.
The First Steps project is a series of small sample scenes, each one demonstrating one or more features of coherence.
If you're a first time user, we suggest to go through the scenes in the established order. They will guide you through some key coherence and networking concepts.
Remember that playing the scenes on your own only shows part of the picture. To fully experience the networked aspects, you have to play them in the Editor plus one or more built instances, and even better - with other people.
The Unity project can be downloaded on its Github repo.
To quickly try a pre-built version of the game, head to this link and either play the WebGL build directly in the browser, or download one of the available desktop versions.
Share the link with friends and colleagues, and have them join you!
Once you open the project in the Unity Editor, you can build scenes via File > Build Settings, as per usual.
If you want to try all the scenes in one go, keep them all in the build and place SceneSelector as the first one in the list.
If you're working on an individual scene instead, bring that one to the top and deselect the others. The build will be faster.
To be able to connect, you need to also run a local Replication Server, that can be started via coherence > Local Server > Run Local Worlds Server.
You can try running multiple Clients rather than just two, and see how replication works for each of them. You can also have one Client just be the Unity Editor. This allows you to inspect GameObjects while the game runs.
Since you might be building frequently, we recommend making native builds (macOS or Windows) as they are created much faster than WebGL.
You can also upload a build to the cloud and share a link with friends. To do that, follow these steps to host builds on the coherence Cloud.
Keep in mind that the custom builds you create in the Unity Editor will not be able to play together with the builds mentioned in the first section of this page.
Getting updates about every entity in the whole scene is unfeasible for big-world games, like MMOs. For this, coherence has a flexible system for creating areas of interest, and getting updates only about the entities that each Client cares about, using a tool called Live Query.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
|
This scene contains two cubes that represent areas of interest. Every connected Client can only see other players if they are standing in one of these cubes.
Select one of the two GameObjects named LiveQuery. You will see they have a Coherence Live Query component.
This component defines an area of interest, in this case a 10x10x10 cube (5 is the Radius). This is telling the Replication Server that this Clients is only interested in network entities that are physically present within this volume.
If a Client has to know about the whole world, it's just enough to set the Live Query Radius to 0, to make it capture all updates.
In addition, Live Queries can be moved in space. They can be parented to the camera, to the player, or to other moving elements that denote an area of interest - depending on the type of game.
It is also possible, like in this scene, to have more than one Live Query. They will act as additive, requesting updates from entities that are within at least one of the volumes.
Notice that a Live Query is needed: a Client with no Live Query in the scene will receive no updates at all.
If you explored previous scenes you might have noticed that GameObjects with a Live Query component were actually there, but in this scene we gave them a visual representation, just for demo purposes.
Try moving in and out of volumes. You will notice that network-instantiation takes care of destroying the GameObject representing a remote entity that exits a Live Query, and reinstantiates it when it enters one again.
Also, notice that the player belonging to the local Client doesn't disappear. coherence will stop sending updates about this instance to other Clients, but the instance is not destroyed locally, as long as the Client retains authority on it.
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
| Bindings | Component behaviours |
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.
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).
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.
In this sample we look at how to network simple physics simulated directly on the Clients, and the implications of this setup.
If we were making a game that relied on precise physics at play between the players (like a sports match, for instance), we would probably go with a setup where the Clients connect to a Simulation Server that runs the physics and prevents cheating.
However, that makes running the game much more expensive for the developer, since a Simulation Server has to be always-on.
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
E or Joypad button left: Pick up / throw objects
Physics | | Uniqueness |
In this scene we have mostly static scenery, and a few crates that the players can pick up and throw around. Who runs the physics simulation here? You could say that everyone runs their part. Let's take a closer look into the setup.
Select one of the crates in the scene. You can see that they have normal Box Collider
and Rigidbody
components. Up until a player is connected, they are being simulated locally. In fact if you press Play, they will fall down and settle.
The crates also have a CoherenceSync
component. The first player to connect gets authority over them, and keeps running their simulation without interruption. That Client now syncs 2 values over the network: Transform.position
and Transform.rotation
.
On other Clients however (the ones that connect after the first), these crates will become remote. The configured component action makes their Rigidbody kinematic, so that now their movement is controlled by the remote authority (i.e. the first Client).
At this point, the first Client to connect is simulating all the crates. However, interacting with physical objects that are simulated by another Client is quite unpleasant due to the lag. To make it better, other Clients steal authority over crates, whenever they either:
Touch/collide with a crate directly
Pick a crate up
In code, this authority switch is a trivial operation, done in a single line. You can find the code in the NetworkGrabbable
class:
As you can see, it's good practice to ask first if the requesting script already has authority over an object, to avoid wasted work.
Once the request succeeds, the instance of the crate on the requesting Client becomes authoritative, and the Client starts simulating its physics. On the other Client (the previous owner), the object becomes remote (and its Rigidbody kinematic), and is now just receiving position and rotation over the network.
Careful! Since authority request is a network operation, you can't run follow-up code right away after having requested it. It's good practice to set a listener to the events that are available on the Coherence Sync component, like this:
This way, as soon as the reply comes back, we can perform the rest of the code.
So who is running the physics, after all? We can now say that it's everyone at the same time, as the roles change all the time.
As we mentioned in the intro - in a simple game where precise physics are non-crucial this might be enough, and it will definitely keep the costs of running the game down, since no Simulation Server has to run in order to make the game playable.
As before, pressing Tab (or clicking the Joystick) switches to an authority view. It's very interesting to see how crates switch sides when a player interacts with them.
For more on authority, take a look inside the NetworkGrabbable
class. It has more code regarding authority events, all commented.
There is one important thing to note in this setup. Since the objects are already in the scene at the start, by default every time a Client connects it would try to sync those instances to the network. This is very similar to what we have seen with character instantiation so far: each Clients brings their own copy.
However, in this case this would effectively duplicate the crates, once online. One extra copy for each connected player! We don't want that.
For this reason, the CoherenceSync
is configured so that these crates are Persistent and have No Duplicates. This is generally the correct way of configuring networked Prefab instances that have been manually placed in the scene.
With these parameters in mind, the way the crates behave is as follows:
At the start, none of the entities exist on the Replication Server (yet).
The first Client connects. They sync the crates onto the network. Being unique, the Replication Server takes note of their Universally Unique ID (UUID).
Another Client connects. They try to bring the same crates onto the network, but because they are set to be No Duplicates and coherence finds there is already a network entity with the same UUID, it destroys the instance in the scene and network-instantiates a new instance, which is now non-authoritative.
If the first Client disconnects, the crates are not destroyed because their Lifetime is set to Persistent. They briefly become orphaned (no one has authority on them) but immediately the authority is passed to the second Client due to the option Auto-adopt Orphan being on.
If everyone disconnects, the crates persist on the Replication Server as network entities that are orphaned. They keep whatever position/rotation they had, since nobody is simulating them anymore.
At this point, nobody is connected. The Replication Server is not doing any work.
When a new Client reconnects and tries to bring the crates online, a duplication is detected: the Prefab instance in the scene and the network instance on the Replication Server have the same UUID. Due to Uniqueness set to No Duplicates, their local instances are destroyed and re-instantiated as network copies. They are orphaned, but thanks to Auto-adopt Orphan, the Client immediately assumes authority on them.
They will also most probably see the crates snap to the last seen position/translation that was stored on the Replication Server, which is synced just before they assume full control over the crates. At this point, they start simulating their physics locally.
Now it's clear why Transform.position
cannot be excluded from synchronization, as we saw in . It needs to know where network entities are in space at all times, to detect if they fall within a Live Query or not.
If a GameObject can be in a state that needs to be computed, it might not appear correctly as it gets recreated. For instance, an animation state machine might not be in the correct animation state if it had previously reached that state via a trigger. You would have to ensure that the trigger is called again when the instance gets network-instantiated (via a ).
Are you wondering why the position is checked by default? You'll find answers in the .
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 for more info on authority.
For more information on persistence, there's about it.
Game characters and other networked entities are often made of very deep hierarchies of nested GameObjects, needing to sync specific properties along these chains. In addition, a common use case is to parent a networked object to the tip of a chain of GameObjects.
Let's see how to handle these cases.
A/D or Left/right joypad triggers: Rotate crane base
W/S or Left joystick up/down: Raise/lower crane head
Q/E or Left joystick left/right: Move crane head forward/back
P/Space/Enter or Joypad button left: Pickup and release crate
Deep bindings | Coherence Node | Parenting at runtime
This scene features a robotic arm that can be controlled by one player at a time. In the scene, a small crate can be picked up and released.
The first player to connect takes control of the arm, and other players can request it via a UI button.
To demonstrate complex hierarchies we choose to sync the movement of a robot arm, made of several GameObjects. In addition to syncing several positions and rotations, we also sync animation variables and other script parameters, present on child objects.
To sync the whole arm we use a coherence feature called deep bindings, that is bindings that are located not on the root object, but deeper in the transform hierarchy.
Select the RobotArm __ Prefab asset located in /Prefabs
, and open it for editing. You will immediately notice a host of little coherence icons to the right of several GameObjects in the Hierarchy window:
These icons are telling us that these GameObjects have one or more binding currently configured (a variable, a method, or a component action).
Now open the coherence Configuration window, and click through those objects to discover what's being synced:
In addition to position and rotation, we also choose to sync the animation parameter ClawsOpen, and enable Animator.SetTrigger()
as a Network Command. Finally we disable the Robot Arm script when losing authority (to disallow input).
This is the base of the robot arm, for which we only sync rotation:
We don't sync the rotation of every object in the chain, since the arm is equipped with an IK solver, which allows us to just sync the target (Two-Bone IK_target) and work out the rotation of the limb (robotarm_bottomarm and robotarm_toparm) on each Client:
By syncing all of these properties, we can have the robotic arm move in sync on all Clients, simply by translating the tip of the IK, and rotating the base of the crane. All of the bindings in this hierarchy are synced through the Coherence Sync component present on the Prefab's root object RobotArm.
As you can see, using deep bindings doesn't require any special setup: they are enabled in exactly the same way as a binding, Network Command or Component action on the root object.
As mentioned in the lesson about Parenting Entities, parenting a network entity to a GameObject that belongs to a chain requires some setup. To be able to pick up the crate with the crane, we equip it with a CoherenceNode
component:
The Path property displays the location in the hierarchy where this object will be inserted. It gets automatically updated by coherence every time the object is parented. Each number represents a child in the root object (and it's 0-based).
Once we have this component set up, parenting the object only requires calling Transform.SetParent()
like any usual parenting operation, and setting its Rigidbody
component to be kinematic.
When we do this, coherence takes care of propagating the parenting to other Clients, so that the crate becomes a child GameObject on every connected Client.
This code is in the RobotArmHand
class, a component attached to the tip of our hierarchy chain: GrabPoint. In OnTriggerEnter
we detect when the crate is in range, storing a reference to it in a variable of type Transform
named grabbableObject
.
This reference is set to sync:
When the player presses the key P (or the Left Gamepad face button), the referenced crate is parented to the GrabPoint GameObject.
Note that coherence natively supports syncing references to CoherenceSync
and Transform
components, and to GameObjects.
Even if the Robot Arm Hand script is disabled on non-authoritative Clients, by referencing the grabbed crate in the grabbableObject
variable and syncing it over the network, even if a Client disconnects, other Clients will already have the correct reference to the crate network entity.
This allows us to gracefully handle a case where, for instance, a Client picks up the crate and disconnects. Because both the crate and the robot arm have Auto-adopt Orphan set to "on", authority is passed onto another Client and they immediately have all the data needed to keep handling the crate.
To move authority between Clients, we can use the UI in the bottom left corner. The button is connected to the Robot Arm Authority script on the ArmAuthoritySwapper GameObject. This script takes care of the authority transfer and what happens as a result, including setting the crate to be kinematic or not.
Differently from other scenes (like the lesson on Physics) where the object is always non-kinematic on the Client simulating it, in this case we want the crate to stay kinematic when authority changes while it's being held.
Is Kinematic is set as follows:
The code is in the RobotArmAuthority
class. To detect whether it's currently being held, it's as simple as checking whether its Transform.parent
is null
:
Remember you can use Tab/click the Gamepad stick to use the authority visualization mode. Try requesting authority from another Client while in this mode.
On the authority Client | On non-authoritative Clients | |
---|---|---|
Is being held
true
true
Has been released
false
true