This page is a work in progress and could be pending updates.

Pre-built Components

Introduction

Fusion offers various prebuilt NetworkBehaviours to get you up and running quickly.

Back To Top

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 SimulationBehaviours. There is normally only one NetworkRunner in the scene on each client and server.

To perform these tasks, the NetworkRunner keeps track of all NetworkObjects, NetworkBehaviours and SimulationBehaviours.

Back To Top

Properties

All NetworkBehaviours 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 either Forward if the simulation is currently predicting the next state or Resimulate if the simulation is re-simulating an old state to reconcile a server update. Note that Resimulate never occur on the server or in Client-Authority mode where simulation is always Forward.

Back To Top

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 player
  • shared: runs as client, creates a local player, to connect to the Fusion Plugin Server
  • single: runs as "server", creates a local player, no connections allowed (Singleplayer)

Back To Top

NetworkTransform

The NetworkTransform manages a simple kinematic object by interpolating between the objects actual local Transform and its simulated state.

Back To Top

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.

Back To Top

NetworkMecanimAnimator

The NetworkMecanimAnimator synchronises the state and values of the parameters held by an associated Unity mechanim Animator component.

It is important to note that because Unity's Animator component cannot be rewound and resimulated (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 resimulate, 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.

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
  }
}

Back To Top

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.

Back To Top

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.

Back To Top

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.

Back To Top

ComputeRawSteer()

This is only a reference implementation.

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.

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;
}

Back To Top

Move()

This is only a reference implementation.

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.
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
}

Back To Top

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.

To Document Top