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

Network Input

Introduction

Input definition, polling and consumption lies at the core of Fusion.

Back To Top

Input Struct Definition

The input struct can store as simple or as complex data as needed. Fusion will only transmit data that actually changes. Therefore, although it is advantageous to keep data compact (e.g. use flags for buttons), it is fine to include rarely used fields. One such example is collecting input from several local players even if there is mostly just one - if these other players are not actually providing input they will not affect bandwidth usage of the input struct, only local memory.

The input struct has the following constraints:

  • it can only contain primitive types and structs;
  • the input struct and any structs it holds have be top-level structs (i.e. cannot be nested in a class); and,
  • for boolean values use NetworkBool instead of bool - C# does not enforce a consistent size for bools across platforms so NetworkBool is used to properly serialize it as a single bit.
public struct MyGameInput : INetworkedInput {
  public const uint BUTTON_FORWARD  = 1 << 3;
  public const uint BUTTON_BACKWARD = 1 << 4;
  public const uint BUTTON_LEFT     = 1 << 5;
  public const uint BUTTON_RIGHT    = 1 << 6;

  public uint Buttons;

  public bool IsUp(uint button) {
    return IsDown(button) == false;
  }

  public bool IsDown(uint button) {
    return (Buttons & button) == button;
  }
}

Fusion will intelligently map the type of the struct; this permits the use of different structs for different play modes or different parts of the game. When unwrapping the input, Fusion will only return the available input of the correct type.

Back To Top

Input Polling

Fusion collects input by polling the local client and populating a previously defined input struct.

SimulationBehaviour

Fusion automatically calls OnInput() on all SimulationBehaviours implementing the INetworkRunnerCallbacks interface and over which the client has Input Authority - hence the name.

Back To Top

MonoBehaviour And Pure CSharp

To poll input from a script not inheriting from SimultionBehaviour, follow these steps:

  1. Implement INetworkRunnerCallbacks and OnInput(); and,
  2. Register the script with the NetworkRunner by calling AddCallbacks() on it.
var myNetworkRunner = FindObjectOfType<NetworkRunner>();
myNetworkRunner.AddCallbacks( this );

Back To Top

OnInput()

The implementation of OnInput() can populate any struct inheriting from INetworkInput with the chosen data. Then populated struct is passed back to Fusion by calling Set() on the provided NetworkInput.

public class MyInput : SimulationBehaviour {
  public void OnInput(NetworkRunner runner, NetworkedInput inputContainer) {
    var myInput = new MyGameInput();

    if (Input.GetKey(KeyCode.W)) {
      myInput.Buttons |= MyGameInput.BUTTON_FORWARD;
    }

    if (Input.GetKey(KeyCode.S)) {
      myInput.Buttons |= MyGameInput.BUTTON_BACKWARD;
    }

    if (Input.GetKey(KeyCode.A)) {
      myInput.Buttons |= MyGameInput.BUTTON_LEFT;
    }

    if (Input.GetKey(KeyCode.D)) {
      myInput.Buttons |= MyGameInput.BUTTON_RIGHT;
    } 

    inputContainer.Set(myInput);
  }
}

Back To Top

Input Consumption

The input consumption is used by the simulation to modify the existing networked state from its current state to the new one based on the previously polled input. Fusion synchronizes the input struct across the network and makes it available during simulation on the client who has Input Authority and the one who has State Authority (the host).

GetInput()

To get the input struct, call GetInput() in the FixedUpdateNetwork() of on any SimulationBehaviour which has Input Authority over the object in question (e.g. the component controlling the player's movement). The call to GetInput() provides the same input struct that was previously populated in OnInput().

public override void FixedUpdateNetwork()
{
    Vector3 direction = default;

    float rotate = 0;
    if (GetInput(out MyGameInput input))
    {
        if (input.IsDown(MyGameInput.BUTTON_FORWARD)) { 
            direction += input.forward; 
        } else if (input.IsDown(MyGameInput.BUTTON_BACKWARD)) { 
            direction -= input.forward; 
        }

        if (input.IsDown(MyGameInput.BUTTON_LEFT)) { 
            rotate=-1; 
        } else if (input.IsDown(MyGameInput.BUTTON_RIGHT)) { 
            rotate=1; 
        }

        if (input.IsDown(MyGameInput.BUTTON_JUMP)) { 
            _player.Jump(); }
        if (input.IsDown(MyGameInput.BUTTON_FIRE)) { 
            _player.Fire(_player.velocity); }

        _player.Move(direction);
        _player.Rotate(rotate);
    }
}

The call to GetInput() will return false when any of the following happens:

  • the client does not have State Authority;
  • the client does not have Input Authority;
  • the requested type of input does not exist in the simulation.

To Document Top