Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The MonoBridge is a system that makes sure every GameObject is linked to its networked representation. It essentially interfaces between the GameObject world and the coherence SDK code running "under the hood".
When you place a GameObject in your scene, the MonoBridge detects it and makes sure all the synchronization can be done via the CoherenceSync
component.
At runtime, you can inspect which Entites the MonoBridge is currently tracking.
A MonoBridge is associated with the scene it's instantiated on, and keeps track of Entities that are part of that scene. This also allows for multiple connections at the same time coming from the game or within the Unity Editor.
When using a Global MonoBridge (Singleton), the MonoBridge is still associated to the scene it was originally instantiated on, even when the GameObject deattaches from the scene and becomes part of DontDestroyOnLoad.
Refer to the Level of detail and Archetypes and LODing sections for more information.
The coherence Sample UI
is a Prefab that you can add to your scene. It handles interaction with coherence services, is made up of a Unity UI Canvas and includes everything needed to handle connection to coherence.
The UI
component on the root of the Prefab allows us to switch between using Rooms or Worlds. Each of these methods has a dedicated dialog for connection.
The Auto Simulator Connection
component is used by Simulator builds to connect to the relevant Replication Server.
See chapter Simulators to learn more about them.
The Rooms Connect Dialog has a few components that facilitate the usage of Rooms.
At the top of the dialog we have an input field for the player's name.
Next is a dropdown for region selection. This dropdown is populated when regions are fetched. The default selection is the first available region.
Due to current limitations the local Server is fetched only if it's started before you enter Play Mode. The Local Development Mode checkbox must also be checked in the project settings (coherence > Settings).
This affects the local region for Rooms and the local worlds for Worlds.
Beneath these elements is a Rooms list of available Rooms in the selected region.
After selecting a Room from the list the Join button can be used to join that Room.
The New Room tab will take us to the Room creation screen.
This screen contains controls for setting a Room's name and maximum player capacity. Pressing the Create button will create a Room with the specified parameters and immediately add it to the Room List in the previous tab. Create and Join will create the room, and join immediately
The Worlds Connect Dialog is much simpler. It simply holds a dropdown for region selection, an input field for the players name, and a Connect button.
You can also build your own interface to connect players to the Server using thePlayResolver
API. To learn more about the API, see either PlayResolver, Rooms or Worlds according to what your project needs.
The coherence SDK is a set of Prefabs & scripts to help you create multiplayer games super fast.
It makes it easy for anyone to create a multiplayer game by having flexible, intuitive and powerful visual components.
Here are the main building blocks of the SDK.
CoherenceSync
is a component that should be attached to every networked Game Object. It may be your player, an NPC or an inanimate object such as a ball, a projectile or a banana. Anything that needs to be synchronized over the network. You can select which of the attached components you would like to sync across the network as well as individual public properties.
The coherence Settings window is located in coherence > Settings.
LiveQuery, as the name suggests, queries a location set by the developer so that coherence can simulate anything within its extent. In our Starter Project, the LiveQuery position is static with an extent large enough to cover the entire playable level. If the World was very large and potentially set over multiple Simulation Servers, the LiveQuery could be attached to the playable character or camera.
The coherence MonoBridge passes information between the CoherenceSync
component and the networked ECS components.
The sample UI Prefab holds all of the UI and connection functionality to connect to the running project locally or via a server. You can completely rewrite this if you like, it's there to get you up and running quickly.
The built-in coherence scripts are configured to execute in a specific order, using the following DefaultExecutionOrder
setup:
-1000 CoherenceMonoBridge
-900 CoherenceSync
-800 CoherenceInput
1000 CoherenceMonoBridgeSender
The way you get information about the World is through LiveQueries. We set criteria for what part of the World we are interested in at each given moment. That way, the Replicator won’t send information about everything that is going on in the Game World everywhere, at all times.
Instead, we will just get information about what’s within a certain area, kind of like moving a torch to look around in a dark cave.
More complex areas of interest types are coming in future versions of coherence.
A LiveQuery is a cube that defines the area of interest in a particular part of the world. It is defined by its position and its extent (half the side of the cube). There can be multiple LiveQueries in a single scene.
The classic approach is to put a LiveQuery on the camera and set the extent to correspond to the far clipping plane or visibility distance.
Moving the GameObject containing the LiveQuery will also notify the Replication Server that the query for that particular Game Client has moved.
Rooms functionality can be accessed through the PlayResolver
which includes all the methods needed to use rooms.
To manage Rooms we must first decide which region we are working with.
FetchRegions
in PlayResolver.cs
allows us to fetch the regions available for our project. This task returns a list of regions (as strings) and a boolean that indicates if the operation was successful.
FetchLocalRegions
in PlayResolver.cs
returns the local region string for a local running Rooms Server, or null if the operation is un-successful (if the Server isn't running for example).
Every other Rooms API will require a region string that indicates the relevant region for the operation so these strings should not be changed before using them for other operations.
The RoomsConnectDialog
populates a dropdown with the region strings returned by both of these methods directly for easy selection.
These methods also call EnsurePlayConnection
which initializes the needed mechanisms in the PlayResolver
if necessary. EnsurePlayConnection
can also be called directly for initialization.
After we have the available regions we can start managing Rooms, for instance:
CreateRoom
in PlayResolver.cs
allows us to create a Room in the region we send it.
the RoomCreationOptions
is used to optionally specify:
a Room name
the maximum number of Clients allowed for the Room
a list of tags for Room filtering and other uses
a key-value collection for the Room
This task returns the RoomData
for the created Room assuming the operation was successful.
FetchRooms
in PlayResolver.cs
allows us to search for available Rooms in a region. We can also optionally specify tags for filtering the Rooms.
This task returns a list of RoomData
objects for the Rooms available for our specifications.
JoinRoom
in PlayResolver.cs
connects the client that we pass to the method to the Room we pass to the method. This RoomData
object can be either the one we get back from CreateRoom
or one of the ones we got from FetchRooms
.
When joining a Room, the method is optionally supplied if the connecting Client is a Simulator, as well.
The RoomsConnectDialog
demonstrates both of these cases in CreateRoom
when called with true for autoJoin and in JoinRoom
respectively.
This is an advanced topic that aims to bring access to coherence's internals to the end user.
CustomBindingProviders
are editor classes that tell coherence how a specific component should expose its bindings and how it generates baked scripts.
For example, we could create a custom binding provider for our Health
component:
Place CustomBindingProviders
inside an editor folder.
We can add additional (custom) bindings:
In order for these new custom bindings to work on reflected mode, we'll need to implement a runtime serializer that understands how to deal with them.
Check the CustomBinding.Descriptor
API for further properties, like interpolation or marking the binding as required.
For custom bindings to work on reflected mode, we need to implement how their values are serialized and deserialized at runtime:
CustomBindingRuntimeSerializers
should be placed in a non-editor folder.
Once we have our runtime serializer, we need to make our binding provider use it:
You can extend an already existing CustomBindingProvider
. For example, coherence ships with a CustomBindingProvider
for Transforms:
This way, you can define additional rules on how you want to treat your Transforms, for example.
Any amount of CustomBindingProviders
can be registered over the same component, but only one being used. The one being used, is resolved by a priority integer that you can specify in the CustomBindingProvider
attribute. The class with the higher priority defined in the attribute will be the only provider taken into account:
The default priority is set at 0
, and coherence's internal CustomBindingProviders
have a priority of -1
.
To understand how these APIs are used, check out TransformBindingProvider
and AnimatorBindingProvider
, both shipped with the coherence SDK (<package>/Coherence.Editor/Toolkit/CustomBindingProviders).
Worlds functionality can also be accessed through the PlayResolver
just like rooms. Worlds work a differently however and are a bit simpler.
First we need to fetch the available Worlds. Unlike Rooms, Worlds cannot be created by a Client and need to be setup in the Developer Portal.
FetchWorlds
in PlayResolver.cs
allows us to fetch the available Worlds for our project. This task returns a list of Worlds in the form of WorldsData
objects and a boolean that indicates if the operation was successful.
This method also calls EnsurePlayConnection
which initializes the needed mechanisms in thePlayResolver
if necessary. EnsurePlayConnection
can also be called directly for initialization.
FetchLocalWorld
in PlayResolver.cs
returns the local World for a local running World Server.
The WorldsConnectDialog
populates a dropdown with the Worlds returned by both of these methods so we can select a World.
After we've selected a World we can connect to it using:
JoinWorld
in PlayResolver.cs
connects the Client that we pass to the method to the World we pass to the method.
The isSimulator
optional parameter is used for Simulators and can be ignored for regular Client connections (see ).
The WorldsConnectDialog
is an example implementation for Worlds usage.
When connected to a Room or a World, the Client can access the currently connected endpoint by accessing the Coherence.IClient.LastEndpointData
property of the CoherenceMonoBridge,
e.g. myBridge.Client.LastEndpointData
.
CoherenceSync
is a component that should be attached to every networked GameObject. It may be your player, an NPC or an inanimate object such as a ball, a projectile or a banana. Anything that needs to be synchronized over the network and turned into an Entity. You can select which of the attached components you would like to sync across the network as well as individual public properties.
All networked Entities need to be placed in the Resources folder.
Any scripts attached to the component with CoherenceSync
that have public variables will be shown here and can be synced across the network. Enable the script + the variable to sync, it's that easy. Those variables with a lightning bolt next to them signify a public method that can be activated via commands.
Ownership transfer
When you create a networked GameObject, you automatically become the owner of that GameObject. That means only you are allowed to update or destroy it. But sometimes it is necessary to pass ownership from one player to another. For example, you could snatch the football in a soccer game or throw a mind control spell in a strategy game. In this case, you will need to transfer ownership from one Client to another.
Entity lifetime
When a player disconnects, all the GameObjects created by that player are usually destroyed. If you want any GameObjects to stay in the Game World after the owner disconnects, you need to set Entity lifetime type of that GameObject to Persistent.
Session Based - will be removed when the Client disconnects. GameObjects will stay on the scene of the Client who is an Authority owner for session-based objects until the scene reloads.
Uniqueness
Allow Duplicates - no restrictions on which objects can be instantiated over the network.
No Duplicates - ensure objects are not duplicated by marking them with a UUID.
You can set the UUID manually
If you leave the field blank a GUID will be assigned at runtime (This is rarely what you want)
Use the CoherenceUUID
component helper to generate GUIDs for you at editor time. CoherenceUUID
is an design time only tool for assigning unique UUIDs to prefab instances.
Uniqueness examples
Manager: If your game has a prefab, of which there can only be 1 in-game instance at any time (Such as a Game Controller), assign a UUID manually on the prefab asset.
Interactable objects: If you have several instances of a given prefab, but each instance must be unique (Such as doors, elevators, pickups etc) you should add the CoherenceUUID script and set it to Auto-generate in scene. This means that i.e. a door will only spawn once, but still replicate its state across the network.
Entity simulation type
Client Side - Simulates everything on the local Client and passes the information to the Replication Server to distribute that information to the other Clients.
Other forms of simulation (Server; Server with Client Input).
Authority transfer style
Not Transferable - The default value is Not Transferable because most often objects are not meant to be transferred.
Stealing - Allows the GameObject to be transferred to another Client.
Request - This option is intended for conditional transfers, which is not yet supported.
Orphaned entities
By making the GameObject persistent, you ensure that it remains in the game world even after its owner disconnects. But once the GameObject has lost its owner, it will remain frozen in place because no Client is allowed to update or delete it. This is called an orphaned GameObject.
In order to make the orphaned GameObject interactive again, another Client needs to take ownership of it. To do this, enable Auto-adopt orphan.
Once you have set the transfer style to Stealing, any Client can request ownership by calling the RequestAuthority()
method on the CoherenceSync
component of that GameObject:
someGameObject.GetComponent<CoherenceSync>().RequestAuthority();
A request will be sent to the GameObject's current owner. The current owner will then accept the request and complete the transfer.
You are now the new owner of the GameObject. This means the isSimulated
flag has been set to true, indicating that you are now in full control of the GameObject. The previous owner is no longer allowed to update or destroy it.
The state of the CoherenceSync.isSimulated
flag is not guaranteed to have a proper value during the Awake()
callback (right after an object is created). All scripts that use this flag should wait at least until the Start()
callback.
You can set up Custom Events for handling user connection and disconnection. Manual Destroy is useful for session based objects that you want to keep "semi-persistent" which would be removed when all the Clients disconnect.
When CoherenceSync
variables/components are sent over the network, by default, Reflection Mode is used to sync all the data at runtime. Whilst this is really useful for prototyping quickly and getting things working, it can be quite slow and unperformant. A way to combat this is to bake the CoherenceSync component, creating a compatible schema and then generating code for it.
The schema is a file that defines which data types in your project are synced over the network. It is the source from which coherence SDK generates C# struct types (and helper functions) that are used by the rest of your game. The coherence Replication Server also reads the schema file so that it knows about those types and communicates them with all of its Clients efficiently.
The schema must be baked in the coherence Settings window, before the check box to bake this Prefab can be clicked.
When the CoherenceSync
component is baked, it generates a new file in the baked folder called CoherenceSync<NameOfThePrefab>
. This component will be instantiated at runtime, and will take care of networked serialization and deserialization, instead of the built-in reflection-based one.
Persistence - Entities with this option will persist as long as the server is running. For more details, see .
with a custom implementation of authority transfer can be found here.
Refer to the section.