Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The basics of coherence
The First Steps project contains 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 in one or more built instances alongside the Unity Editor, and even better - with other people.
The Unity project can be downloaded from its Github repo. The Releases page contains pre-packaged .zip files.
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 Replication Server > Run for Worlds.
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 or watch this quick video to learn how to host builds on the coherence Cloud.
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!
CoherenceSync | Bindings | Component behaviors | Authority
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
Upon connecting, a script instantiates a character for you. Now you can move and jump around, and you will see other characters move too.
To be able to connect, you need to also run a local Replication Server, that can be started via coherence > Local Replication Server > Run for Worlds.
coherence takes care of keeping network entities in sync on all Clients. When another Client connects, an instance of your character is instantiated in their scene, and an instance of their character is instantiated into yours. We refer to this as network instantiation.
When you click Connect in the sample UI, the CoherenceBridge
opens a connection. The PlayerHandler GameObject on the root of the hierarchy controls character instantiation by responding to that connection event.
Its PlayerHandler
script implements something like this:
On connection, a character is created. On disconnection, the same script destroys the character's instance. Note how instantiating and removing a network entity is done just with regular Unity Instantiate
and Destroy
.
Now let's take a look at the Prefab that is being instantiated. You can find it in the /Prefabs/Characters
folder.
By opening coherence's Configuration window (either by clicking on the Configure button on the CoherenceSync
component, or by going to coherence > GameObject Setup > Configure), you can see what is synced over the network.
When this window opens on the Variables 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 for this object. Each Client sends the position and rotation of the character that 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.
In First Steps, all the variables are set to public by default. The network code that coherence automatically generates can only access public variables and methods, without them being public syncing would not work.
In your own projects, keep it in mind to always set synced variables to public!
To ensure that Clients don't modify the properties of entities they don't have authority over, we need to make sure that they are not running on the character instances that are non-authoritative.
coherence offers a rapid way to make this happen. If you open the Components tab of the Configuration window, you will see that 3 components are configured to do something special:
In particular:
The PlayerInput
and KinematicMove
scripts get disabled.
The Rigidbody
component is made kinematic.
While in Play Mode, try selecting a remote player character. You will notice that some of its script have been disabled by coherence:
You can learn more about Component Actions here.
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 connected, each one will have 2 player 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, when connected, the name of a GameObject and the icon next to it informs you about its current authority state (see image above).
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 visualization in the 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 (try the Physics scene!).
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 inside one of these cubes.
Select one of the two GameObjects named LiveQuery. You will see they have a CoherenceLiveQuery component:
This component defines an area of interest, in this case a 10x10x10 cube (5 is the Extent). 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 to Infinite
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 at least one 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 special 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.
If a GameObject can be in a state that needs to be computed somehow, it might not appear correctly in the instant it gets recreated.
Using the same scene as in the , let's see how to easily sync animation over the network.
| Bindings
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
We haven't mentioned it before, but the character Prefab does a lot more than just syncing its position and rotation.
When you move around, 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 ).
Very much like in the example about position and rotation, just sending these across the network allows us to keep animation states in sync, making it look like network-instantiated Prefabs on other Clients are performing the same actions.
Open the player Prefab located in the /Prefabs/Characters
folder. Browse its Hierarchy until you find the child GameObject called Workman. You will notice it has an Animator
component.
Select this GameObject and open the Animator window.
As is usually the case, animation is controlled by a few Animator parameters of different types (int, bool, float, etc.).
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, just like other regular public properties.
Did you notice that we 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 on child GameObjects are what we call deep bindings.
There is only one piece missing: animation Triggers. We use one to trigger the transition to the Jump state.
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.
Moving platforms | | |
WASD or Left stick: Move character
Hold Shift or Shoulder button left: Run
Spacebar or Joypad button down: Jump
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()
. 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
...
When parenting entities, it is important that the child's position, rotation, and local scale are replicated so that all Clients see the relative state of the child when connected to a parent. If these properties are not replicated on the child, it is possible that different Clients will see different states of the child relative to the parent.
We have seen a lot of examples where objects belonging to a Client would disappear with them when they disconnect. We call these objects 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)
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 ).
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, notice how the Lifetime property 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
:
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.
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 when nobody's connected 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:
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 that runs the physics and prevents cheating.
However, that makes running the game much more expensive for the developer, since a Simulator has to be always on.
Physics | | Uniqueness |
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
This scene features 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 at 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 begins simulating the physics for them.
That Client now syncs 5 values over the network, including the most important ones that will drive the crate's motion: Transform.position
and Transform.rotation
.
On other Clients however (the ones that connect after the first one) these crates will become "remote". Their Rigidbody will become kinematic, so that now their movement is controlled by the authority (i.e. the first Client).
At this point, the first Client to connect is simulating all the crates. However, if we were to leave things like this, interacting with physical objects that are simulated by another Client would be 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 Grabbable
class. Essentially, it boils down to this:
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.
If 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.
Also note that while it's totally possible to configure an object so that Clients can just steal authority from each other, we configured the crates here to require an authority request.
When they want authority, Clients have to request it and most importantly, wait for an answer.
We implemented this request / answer mechanism to avoid problems of concurrency, where two players are requesting authority on a crate at the same time, and end up with a broken state because the game code assumes that they both got it.
So who is running the physics, after all? We can now say that it's everyone at the same time, as roles change all the time.
As mentioned 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 Grabbable
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 have No Duplicates. This is generally the correct way of configuring networked Prefab instances that have been manually placed in the scene.
In addition to a unique identifier (the Manual Unique ID), coherence will auto-assign an additional identifier (the Prefab Instance Unique ID) whenever the crate is instantiated in the scene at edit time.
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).
Client A connects. They sync the crates onto the network. Being unique, the Replication Server takes note of their ID.
Client B connects. They try to bring the same crates onto the network, but because it is set to be No Duplicates and coherence finds there is already a network entity with the same ID, it doesn't create a new network entity but recognises that crate as the one on the server, and just makes it non-authoritative for Client B.
If Client A 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 Client B due to the option Auto-adopt Orphan being on.
If everyone disconnects, the crates remain 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 again, the same thing happens again: the crates in the scene are associated with the orphaned entities and are adopted by the new client, who 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, like normal.
Using the same scene as in the , we now take a look at another way to make Clients communicate: Network Commands. Network Commands are commonly referred to as "RPCs" (Remote Procedure Calls) in other networking frameworks. You can think of them as sending 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 greet other players. For both of these actions to play their animation, we need to send a command over the network to invoke Animator.SetTrigger()
on the other Client.
Like before, select the player Prefab located in the /Prefabs/Characters
folder, and browse its Hierarchy until you find the child GameObject called Workman.
Open the coherence Configure window on the third tab, Methods:
You can see how the method Animator.SetTrigger(string)
has been marked as a Network Command. With this done, it is now possible to invoke it over the network using code.
You can find the code doing so in the Wave
class (located in /Scripts/Player/Wave.cs
):
Analysing this line of code, we can recognize 5 key parts:
First, notice how the command is invoked on a specific CoherenceSync
(that sync
property).
We want to invoke this command on a component that is an Animator
.
We invoke a method called "Animator.SetTrigger".
With MessageTarget.Other
, we are asking to send this message only to network entities other than the one that has the CoherenceSync
we chose to use.
We pass the string "Wave"
as the first parameter of the method to invoke.
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 this would have been to call CoherenceSync.SendCommand()
with MessageTarget.All
.
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: Pick up and release crate
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/Characters
, 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, a Network Command, or a Component action is enabled on the root GameObject.
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, it references the correct grabbed crate in the grabbableObject
variable due to it being synced over the network. So when its authority 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, and it transfers authority on both the robot arm and the crate. This script takes care also of what happens as a result of the transfer, including setting the crate to be kinematic or not.
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.
Now it's clear why Transform.position
cannot be excluded from synchronization, as we saw in . coherence needs to know where network entities are in space at all times, to detect if they fall within a Live Query or not.
For instance, an animation state machine might not be in the correct animation state if it had previously reached that state via a trigger parameter. You would have to ensure that the trigger is called again when the instance gets network-instantiated (via a ) or switch your state machine to use other type of animation parameters, which would be automatically synced as soon as the entity gets reinstantiated.
Learn more in the lesson, or on .
Since Triggers are not a variable holding a value that changes over time, but rather an action that happens instantaneously, we can't just enable in the Config window like with other animator parameters. We will see how to sync them in the , using Network Commands.
One important note: this sample describes parenting at runtime. For more information on edit-time parenting, see the page about .
For cases like these, coherence takes care of them automatically. More complex hierarchies require a different handling, and we cover them in .
When it gets instantiated, the flower writes the current into the timePlanted
variable. This variable never changes after this, and is used to reconstruct the phase in which the flower is in (see ). Similarly, as the flower is not moving, position and rotation are only synced at the time of planting.
coherence supports the ability to have an instance of the game active in the cloud, running some logic all the time (we call this a ). 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.
As we discussed in the , switching authority is a network operation that is asynchronous, so we need to wait for the reply from the player who currently has authority.
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 has to run in order to make the game playable.
For more information on persistence, there's about it.
In this example we used Network Commands 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 of this, it is also used in the lesson to change a number in a UI element across all Clients.
One important note: this sample describes deep parenting at runtime. For more information on edit-time deep parenting, see the page about .
As mentioned in the lesson about , 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:
Similarly to the crates in the , we don't just want the crate to automatically become non-kinematic when we have authority on it. We want the crate to stay kinematic when authority changes while it's being held by the arm.
Is being held
true
true
Has been released
false
true