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 only ever 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

Callbacks

The NetworkRunner allow the application to hook into important network related events by implementing the INetworkRunnerCallbacks interface and registering with the runner by calling NetworkRunner.AddCallbacks().

Note: Fusion will also automatically register any SimulationBehaviour implementing the INetworkRunnerCallbacks interface as a callback target.

The most important callbacks are OnServerConnected and OnPlayerJoined which is where the game will spawn initial player objects:

public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
    _players[player] = runner.Spawn(_playerPrefab, Vector3.zero, Quaternion.identity, player);
}

Consult the API reference for a detailed explanation of all available callbacks.

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

NetworkCharacterController

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

NetworkMechanimAnimator

The NetworkMechanimAnimator synchronises the values of the parameters held by the associated Unity mechanim Animator.

N.B.: It does NOT synchronise the animations themselves!

Back To Top

NetworkObjectPool

To reduce run-time memory allocation and, more importantly, garbage collection every Unity game will need to use some form of Object Pooling so old game objects can be re-used rather than destroyed. Network objects are no exception.

Because every game is different and has different needs, Fusion (like Unity) does not have any built-in object pooling but instead provides two hooks - one for getting an object from the pool and another for returning a used object to the pool.

Back To Top

NetworkSceneManager

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