Fusion 1 Introduction
Overview
Fusion is a new high performance state synchronization networking library for Unity. With a single API, it supports two fundamentally different network topologies as well as a single player mode with no network connection.
It is built with simplicity in mind to integrate naturally into the common Unity workflow, while also offering advanced features like data compression, client-side prediction and lag compensation out of the box.
For example, RPCs and network state is defined with attributes on the methods and properties of the MonoBehaviour themselves with no need for explicit serialization code and network objects can be defined as prefabs using all of Unity's most recent prefab features like nesting and variants.
Behind the covers, Fusion relies on a state-of-the-art compression algorithm to reduce bandwidth requirements with minimal CPU overhead. Data is transferred either as complete compressed snapshots (only hosted mode) or as partial chunks with eventual consistency. In the latter case, a fully configurable area-of-interest system is supplied to allow support for very high player counts.
Fusion implements a robust tick-based simulation and operates in either Shared Mode or Hosted Mode. The main difference is in who has authority over (ability to change) network objects, but this in turn dictates which other SDK features are available.
Hosted Mode / Server Mode
In hosted mode, whether running as a dedicated headless server or a combined client and server on the same device, the server has full and exclusive State Authority over all objects, no exceptions.
Clients can only modify networked objects by sending their input to the server (and have the server react to that input) or by requesting a change using an RPC.
Any changes a client makes directly to the networked state is only a local prediction, which will be overridden with actual authoritative snapshots from the server when those are received. This is known as reconciliation, as the client is rolled back to the server-provided state and re-simulated forward to the local (predicted) tick.
If previous predictions were accurate, this process is seamless. If not, the state will be updated and because the network state is separate from the rendering state, the rendering may either snap to this new state or use various forms of interpolation, error correction and smoothing to reduce the visual artifacts caused by the correction.
In hosted mode, Fusion supports lag compensated hit boxes to account for the fact that each client sees other clients in the past and regular ray cast on the server would not match what the player was seeing. Lag compensation effectively allow the server to see the world from the player's perspective at the time a given input was received.
When running hosted mode from behind a firewall or a router, the Photon cloud transparently provides UDP punch through or package relay as needed, but the session is owned by the host and will be lost if the host disconnects. Fusion does provide a host migration mechanism to allow transfer of network authority to a new client in the event that the current host is disconnected. Do note that, unlike Shared Mode, this requires special handling in client code.
Shared Mode
In shared mode, authority over network objects is distributed among all clients. Specifically, each client initially has State Authority over objects they spawn, but are free to release that State Authority to other clients. Optionally, clients may be allowed to take State Authority at will.
In shared mode the data transfer mode is always Eventual Consistency, and features such as lag compensation, prediction and rollback are not available. Simulation always moves forward at the same tick rate on all clients, but beware that ticks are not guaranteed to be aligned between clients.
The Shared Mode network session is owned by the Photon cloud and remains alive as long as any client is connected to it. The Photon cloud serves as a package relay and has full access to the network state with no need to run Unity, allowing for lightweight server logic and data validation (e.g. cheat protection) to be implemented without the need to spin up dedicated server hardware.
Shared mode is in many ways similar to PUN, albeit more feature complete, faster, and with no run-time allocation overhead.
PUN, Bolt & Fusion Comparison
Fusion was developed to evolve and replace the two existing Photon state-transfer products for Unity (Bolt and PUN); it includes all supported architectures and more!
Although PUN and Bolt are solid networking solutions, their architectures do not allow for further optimizations. Fusion merges all the best concepts of both PUN and Bolt, while being built from the ground up with a high-performance architecture to enable state of the art features right out of the box. The following image summarizes the improvements.
The Basics
The two primary Fusion components that you will use are NetworkRunner
and NetworkObject
. NetworkRunner
can be thought of as the core of Fusion - There is a single runner in your scene managing both networking and simulation. This happens on both servers and clients.
Adding NetworkObject
to an otherwise regular Unity prefab or scene object, will assign it a network identity in runtime, and let it be part of the synchronized tick-based simulation. It also lets you configure a few options (like for example making this particular object always part of the data transfers - global object).
Two other base behaviors are there to be inherited from and add the actual networking to Game Objects: SimulationBehaviour
and NetworkBehaviour
.
Networked properties (that comprise the game state) are to be added to NetworkBehaviour
(a specialized subclass of SimulationBehaviour
), while the latter can be used to control simulation steps and callbacks without carrying networked properties.
NetworkBehaviour
should be used to hold data that is automatically synchronized across the network. In order to define a network state value, simply create a property and mark it as [Networked]
, like this:
C#
[Networked] public byte life { get; set; }
[Networked]
works for all primitive types (except for bool
, for which you should use NetworkBool
instead since it will get properly serialized as a single bit). It also works for structs and references to other NetworkObject
's (even prefabs) since these can be safely identified on all clients.
It is also easy to register a callback to be triggered every time a networked property value changes:
C#
[Networked(OnChanged = "OnTypeChanged")] public Type type { get; set; }
public static void OnTypeChanged(Changed<TheClassWhichHasTheProperty> changed)
{
// your code here - check API docs for more details
}
In addition to the actual callback name, you can also control where the callback is executed:
OnChangedLocal
(true/false) - set true to also have the event hook called on the machine that changed the property, for example a server (default false)OnChangedRemote
(true/false) - set false to only have the event hook called on the machine that changed the property (default true)
Built-in Network Behaviours
Fusion offers various prebuilt NetworkBehaviour
s to get a game or prototype up and running quickly.
NetworkTransform
keeps an object transform synchronized (can also contain colliders). Rendering is automatically kept butter smooth with state-of-the-art snapshot interpolation. It is also straight-forward to do full client-side prediction (on any game client) by directly changing data on Unity's Transform
.
For physics-controlled rigidbodies, NetworkRigidbody
is the recommendation. Same interpolation options work, and Fusion can do full predict/rollback directly with Unity physics (PhysX) by just setting a configuration option.
For objects controlled directly by players, like humanoid characters, Fusion has a built-in NetworkCharacterController
. For more complex use cases, it is possible to reuse the basic static query (that gives you surface tangents, penetration corrections, etc) and match with your custom code for steering/movement.
Tick-based Callbacks
To write gameplay simulation code, SimulationBehaviour
s lifecycle comes into play. It is possible to still use Unity's built in Awake()
and Start()
etc., but it is recommended to use the network-safe counterparts:
Fusion's equivalent to Start()
is called Spawned()
. It is triggered when that object is brought to life for the first time on a specific machine (server or client). Use Awake()
for one-time initialization of things that will not change over the lifetime of the object - Calls to GetComponent<>()
often falls into this category, but generally use Spawned()
where you would normally have used Start()
.
The most important Fusion callback is FixedUpdateNetwork()
. FixedUpdateNetwork()
is what Fusion uses both to execute logic for the next network state, and to resimulate during reconciliations.
There are detailed descriptions of all these callbacks on API documentation, but the important part to understand is that FixedUpdateNetwork()
, like FixedUpdate()
is called at fixed intervals, which are independent of the rendering rate. And, unlike FixedUpdate()
, it can be called many times over for the same state/frame/tick when resimulation happens to reconciliate updates that came from a server.
For network properties, resimulation is transparent because Fusion will implicitly reset all network state properties before calling FixedUpdateNetwork()
.
It is perfectly safe, for example to run something like this from within FixedUpdateNetwork()
, as long as the object in question uses NetworkTransform
or NetworkRigidbody
:
C#
transform.position += transform.forward * Runner.DeltaTime;
Notice the use of Runner.DeltaTime
, necessary to keep simulation in accordance with the network tick rate.
Render()
is guaranteed to run after all calls to FixedUpdateNetwork()
and before LateUpdate()
.
Input
Fusion splits input handling into two separate steps:
- Collect Input from the local hardware and place it in a struct. This is always done only on clients and a host (not on dedicated server), once per new tick. This is sent to the server, and also used locally for immediate client side prediction (on clients).
- From
FixedUpdateNetwork()
this input can be read to change the game state (advance the simulation). This can be done on both the client (from any Network Object for which is has Input Authority) and the Host/server. On clients, this can happen multiple times for the same tick/frame to resimulate on reconciliation rollbacks.
The first step can be thought as a regular Unity input handling mechanism that simply records what the player is doing and stores it for later use, while the second step is a decoupled script to apply that input to modify the networked state.
The first step is polled by Fusion in its OnGetInput()
callback and the second is handled by calling GetInput()
or TryGetInput()
from within FixedUpdateNetwork()
, typically by a SimulationBehaviour
on your main character.
Remote Procedure Calls
While Input structs and the use of [Networked]
properties should be your go-to method for keeping game state synchronized between network clients (and keeping server authority), there are times when that simply isn't the most practical solution.
For example when a client wants to perform a rare complex interaction with an object that it does not have Input Authority over: like using a specific key in your inventory to open a locked door.
One could include some fields in the Input struct that would tell the server which key to use and for which door, but that approach would quickly end up creating a lot of clutter in the Input Struct that is rarely used.
Another thing to consider is that regular Fusion Input is not reliable - packets may be lost. This is rarely noticeable if you're doing some form of continuous input like moving a character, but if you're doing a single one-time action you might want to make sure it arrives at the server.
For these cases, Fusion supports RPCs (Remote Procedure Calls).
Fusion implements a simple yet powerful syntax for RPCs. To define an RPC on any SimulationBehaviour
you declare a regular C# method with return type ;void
and tag it with the [Rpc]
attribute. It may take any primitive parameter (except bool
, see above) as well as structs and references to Fusion objects (e.g. a NetworkObject
or a PlayerRef
- anything that has a network identity).
The [Rpc]
attribute allow you to filter where it may be called from and where it gets executed:
C#
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
public void RPC_Configure(string name, Color color)
{
playerName = name;
playerColor = color;
}
Note the use of the "RPC" prefix - while you don't have to use this exact notation, you must either pre- or post-fix the method name with "rpc" (not case-sensitive).
In addition to the source and target properties, the [Rpc]
attribute has a couple of additional optional parameters:
Channel
(Reliable/Unreliable) - Set to Unreliable if you don't care about the RPC being lost in transmission (defaults to Reliable)InvokeLocal
(true/false) - Set to false if you don't want to invoke the RPC on the local client (defaults to true)InvokeResim
(true/false) - Set to true if you want the RPC to be invoked even during re-simulations (defaults to false)
One final note on RPCs is that you should keep in mind that RPCs have no explicit state - Even if you're sending to "all clients", clients who are not yet online will not receive it, and clients that jump out and back in will forget it ever happened. For this reason you should always make sure that the state of the RPC is either truly transient (e.g. a chat message) or that its effect is indirectly recorded in [Networked]
properties.
Where to go next
To get started with Fusion we strongly recommend beginning with the the Fusion Host Mode Basics tutorial or Fusion Shared Mode Basics tutorial. These tutorials teach you all the necessary basics to get started with Fusion.
Back to top