Pre-built Components
Introduction
Fusion offers various prebuilt NetworkBehaviour
s to get you up and running quickly.
NetworkRunner
The NetworkRunner
is at the core of Fusion. The NetworkRunner
manages the connection to the network and controls the simulation - from gathering input to merging snapshots, over to invoking callbacks and life-cycle methods on SimulationBehaviour
s. There is normally only one NetworkRunner
in the scene on each client and server.
To perform these tasks, the NetworkRunner
keeps track of all NetworkObject
s, NetworkBehaviour
s and SimulationBehaviour
s.
Properties
All NetworkBehaviour
s can access the current NetworkRunner
via the Runner
property, and the Runner itself exposes some important system properties.
IsServer
: true if this runner represent an authoritative server.Stage
: the current stage of the simulation. Can be eitherForward
if the simulation is currently predicting the next state orResimulate
if the simulation is re-simulating an old state to reconcile a server update. Note thatResimulate
never occur on the server or in Client-Authority mode where simulation is alwaysForward
.
GameMode
The GameMode
defines how the local peer will behave. The GameMode
is passed as a parameter to NetworkRunner.StartGame()
method.
client
: runs as client, creates a local player, to connect to a client host (cloud-shared) or dedicated (server-based)host
: runs as server, creates a local player (Server + Client)server
: runs as dedicated server, no playershared
: runs as client, creates a local player, to connect to the Fusion Plugin Serversingle
: runs as "server", creates a local player, no connections allowed (Singleplayer)
NetworkTransform
The NetworkTransform
manages a simple kinematic object by interpolating between the objects actual local Transform
and its simulated state.
NetworkRigidbody
For rigid bodies, the NetworkRigidbody
does better than simply interpolating between old states and uses the predictable nature of a physics object to extrapolate a more accurate local position.
NetworkMecanimAnimator
The NetworkMecanimAnimator
synchronises the state and values of the parameters held by an associated Unity mecanim Animator
component.
It is important to note that because Unity's Animator
component cannot be rewound and re-simulated (it is designed to run forward only) and because it cannot be accurately set to a tick's state, it cannot be relied on for tick accurate animations.
Because of this "forward only" limitation NetworkMecanimAnimator
does not attempt to re-simulate, and NetworkMecanimAnimator
will only sync the Animator
component from the State Authority to Proxies. The Input Authority should also apply changes to the Animator on Forward ticks.
C#
void FixedUpdateNetwork()
{
// Only apply changes to the Animator if input is available
// (which is true for StateAuthority and InputAuthority),
// and only on Forward ticks (resimulation should be ignored).
if (GetInput(out var input) && Runner.IsForward)
{
// Apply inputs to Animator
}
}
Shared Mode without using Fusion Input Handling
Shared Mode does not require the use of the Fusion Input System, and you can handle input gathering and application yourself. In this case, be sure to restrict inputs to only apply on the State Authority.
C#
void FixedUpdateNetwork()
{
// In Shared Mode:
// Only apply changes to the Animator on the StateAuthority.
if (HasStateAuthority)
{
// Apply inputs to Animator
}
}
SetTrigger()
The pass-through NetworkMecanimAnimator.SetTrigger()
methods should used instead of Animator.SetTrigger()
calls, as triggers are transient and it is possible for the backing bool to reset to false before NetworkMecanimAnimator
captures the values of the Animator
component. There is also a passThroughOnInputAuthority
option for NetworkMecanimAnimator.SetTrigger()
, which immediately passes through to Animator.SetTrigger()
on the Input Authority, as a convenience.
NetworkCharacterControllerPrototype
This component is a prototyping example which demonstrates how to to sync Unity's CharacterController
on a NetworkObject
. Unity's CharacterController
isn't inherently compatible with prediction and resimulation, so this prototype demonstrates how to work around that limitation by enabling and disabling the CC at the appropriate times. This component can be used as a base class and extended as needed, or it can be copied and renamed as a starting point for your own custom implementation.
NetworkCharacterController (Obsolete)
The most erratic objects are those controlled directly by players, commonly referred to as Character Controllers, and Fusion has a NetworkCharacterController
for that particular use-case as well.
The pre-built NetworkCharacterController
allows for quick prototyping as it includes commonly desired behaviour. However, there is no one-size-fits-all character controller as character controller implementations are exceedingly game specific.
It is therefore advised to read through the source code of the two core methods Move()
and ComputeRawMovement()
used by the NetworkCharacterController
to draw inspiration for creating a replacement custom character controller for the production of a game.
ComputeRawSteer()
ComputeRawSteer()
is an internal method that does the bulk of the movement calculations based on the type of movement the character is currently performing. In the pre-built NetworkCharacterController
, Move()
requests the movementPack
values from ComputeRawMovement()
by passing a reference to the struct to be filled out.
C#
void ComputeRawSteer(ref Movement movementPack, float dt) {
Grounded = movementPack.Grounded;
float minYSpeed = -100;
float maxYSpeed = 100;
var current = Velocity;
switch (movementPack.Type) {
case MovementType.FreeFall:
current.y -= Config._gravityStrength * dt;
if (!Config.AirControl || movementPack.Tangent == default(Vector3)) {
current.x = Mathf.Lerp(current.x, 0, dt * Config.Braking);
current.z = Mathf.Lerp(current.z, 0, dt * Config.Braking);
} else {
current += movementPack.Tangent * Config.Acceleration * dt;
}
break;
case MovementType.Horizontal:
// apply tangent velocity
current += movementPack.Tangent * Config.Acceleration * dt;
var tangentSpeed = Vector3.Dot(current, movementPack.Tangent);
// lerp current velocity to tangent
var tangentVel = tangentSpeed * movementPack.Tangent;
var lerp = Config.Braking * dt;
current.x = Mathf.Lerp(current.x, tangentVel.x, lerp);
current.z = Mathf.Lerp(current.z, tangentVel.z, lerp);
// we only lerp the vertical velocity if the character is not jumping in this exact frame,
// otherwise it will jump with a lower impulse
if (Jumped == false) {
current.y = Mathf.Lerp(current.y, tangentVel.y, lerp);
}
// clamp tangent velocity with max speed
if (tangentSpeed > MaxSpeed) {
current -= movementPack.Tangent * (tangentSpeed - MaxSpeed);
}
break;
case MovementType.SlopeFall:
current += movementPack.SlopeTangent * Config.Acceleration * dt;
minYSpeed = -Config.MaxSlopeSpeed;
break;
case MovementType.None:
var lerpFactor = dt * Config.Braking;
if (current.x != 0) {
current.x = Mathf.Lerp(current.x, default, lerpFactor);
if (Mathf.Abs(current.x) < float.Epsilon) {
current.x = 0;
}
}
if (current.z != 0) {
current.z = Mathf.Lerp(current.z, default, lerpFactor);
if (Mathf.Abs(current.z) < float.Epsilon) {
current.z = 0;
}
}
// we only lerp the vertical velocity back to 0 if the character is not jumping in this exact frame,
// otherwise it will jump with a lower impulse
if (current.y != 0 && Jumped == false) {
current.y = Mathf.Lerp(current.y, default, lerpFactor);
if (Mathf.Abs(current.y) < float.Epsilon) {
current.y = 0;
}
}
minYSpeed = 0;
break;
}
// horizontal is clamped elsewhere
if (movementPack.Type != MovementType.Horizontal) {
Vector2 h = new Vector2(current.x, current.z);
if (h.sqrMagnitude > MaxSpeed * MaxSpeed) {
h = h.normalized * MaxSpeed;
}
current.x = h.x;
current.y = Mathf.Clamp(current.y, minYSpeed, maxYSpeed);
current.z = h.y;
}
Velocity = current;
// set jump state
Jumped = false;
}
Move()
This is a basic implementation of a full Move()
function. Performs a movement query, uses its result to compute new Velocity and then applies penetration corrections + velocity integration into the transform position. It does not change Rotation.
direction
: Intended movement direction, subject to movement query + acceleration.callback
: Optional custom callbacks object.layerMask
: Optional layermask. If not passed, the default one from Config will be used.
C#
public void Move(Vector3 direction, ICallbacks callback = null, LayerMask? layerMask = null) {
var dt = Runner.DeltaTime;
var movementPack = ComputeRawMovement(direction, callback, layerMask);
ComputeRawSteer(ref movementPack, dt);
var movement = Velocity * dt;
if (movementPack.Penetration > float.Epsilon) {
if (movementPack.Penetration > Config.AllowedPenetration) {
movement += movementPack.Correction;
} else {
movement += movementPack.Correction * Config.PenetrationCorrection;
}
}
_transform.position += movement;
#if DEBUG
LastMovement = movementPack;
#endif
}
NetworkEvents
NetworkEvents
may be used in place of NetworkRunner.AddCallbacks()
to wire up event handlers directly in the Unity Inspector. Simply add the component to a NetworkRunner
and used drag and drop to register individual event handlers.
There is no requirement to use NetworkEvents
and most games will not need it, it is included for convenience in the rare cases where the network events needs to be configurable from the Unity scene.