This document is about: FUSION 2
SWITCH TO

Unity Physics

Overview

Includes components necessary for client prediction and synchronization of Rigidbody and Rigidbody2D components.

  • NetworkRigidbody3D - Synchronizes Rigidbody state from the State Authority to other peers.
  • NetworkRigidbody2D - Synchronizes Rigidbody2D state from the State Authority to other peers.
  • RunnerPhysicsSimulate3D - Optional component which may be added to the NetworkRunner GameObject. Allows Fusion to handle Physics Simulation.
  • RunnerPhysicsSimulate2D - Optional component which may be added NetworkRunner GameObject, Allows Fusion to handle Physics2D Simulation.

Usage

To use the Network Rigidbody components follow these steps:

  1. Add NetworkRigidbody3D (or NetworkRigidbody2D for 2D) and NetworkObject to a rigidbody prefab or scene object. This component replicates the rigidbody state from the State Authority to other peers. It also handles reset and reconciliation of the rigidbody for client prediction re-simulation.
  2. Add RunnerSimulatePhysics3D (or 2D if applicable) to your NetworkRunner prefab. This component takes over Unity's Physics.Simulate() calls.
  3. Be sure all simulation/controller code executes inside of FixedUpdateNetwork().

Download

The Unity Physics Addon is included in the main Fusion SDK.

Migrating from Fusion 1 to Fusion 2

  • IBeforePhysicsStep and IAfterPhysicsStep callbacks have been removed, and can be replaced with callbacks in RunnerSimulatePhysics. See Callbacks below.

Updating the Addon

The recommended way to update is to delete the addon root folder (Assets > Photon > FusionAddons > Physics) before importing the new Fusion SDK package.

Extending / Modifying

The NetworkRigidbody and RunnerSimulatePhysics components are designed to work for most use cases. However, you may want to modify these to meet your own project specific needs. Some members of these classes are virtual and can be overridden. However, in most cases you will probably want to make your own class using these components as guides, or may just modify them directly. No internals of the Fusion core rely on these components (though some Samples and Demos may), so you may modify them as you see fit.

NetworkRigidbody2D / NetworkRigidbody3D

NetworkRigidbody3D and NetworkRigidbody2D derive from the class NetworkTRSP, and inherit all of its AOI handling. For Area Of Interest specifics see the NetworkTRSP section of this manual.

Sync Scale

When enabled, transform.localScale will be synchronized.

Sync Parent

When enabled, transform.parent will be synchronized. Rigidbodies which have parent Rigidbodies should be set to kinematic. Parented Rigidbodies will automatically set their Object AreaOfInterest Override to the parent Network Object.

There are a few parenting caveats:

  • Parent transforms MUST have a NetworkBehaviour component. This is how the parent is found, using a NetworkBehaviourId.
  • The NetworkRigidbody must be on the root of the Network Object (this typically will be the case anyway).
  • The parent transform can be a child transform of a Network Object. For example the hand of a player.

Interpolation Target

The transform which will be moved for interpolation during Render(). This typically is a child transform of the Rigidbody which contains no Colliders.

When null, the root of the Network Rigidbody will be moved for interpolation, and will be returned to its tick correct position at the start of the Simulation loop. The InterpolationTarget value may be changed at runtime via code.

The benefit of using an Interpolation Target rather than the root, is that interpolation will not break physics caching for the object. However, this does make Sync Scale invalid for any child Rigidbodies.

We recommend leaving Interpolation Target null, and only explore using one if you experience some undesired Rigidbody behaviour (in regards to friction/stacking).

Cases where Interpolation Target is necessary:

  • Using MovePosition() to move a kinematic Rigidbody. Without using an Interpolation Target interpolation is done by setting the transform.position and rotation. Direct changes to the transform like this however override any MovePosition() calls to the RB, even if they are made after the transform was modified.
  • Physics need to be unaffected by interpolation. Interpolating by moving the Transform directly has some side effects, such as breaking the Rigidbodies static friction and sleep. The Render Sleep Thresholds mitigate this, but there may be some cases were any interference is unacceptable.

If an Interpolation Target is used, be sure that it it does not contain any colliders, as moving these will dirty the Rigidbody (which is what we are trying to avoid by using an Interpolation Target). All cosmetic effects should be part of this Interpolation Target.

NOTE: If using both Scale and Parenting, using an Interpolation Target will NOT give correct results. GameObject scale is affected by its parents scale in ways that nearly impossible to replicate via code, so for accurate scale results leaving InterpolationTarget null is recommended.

NOTE: A common mistake is not having Camera's follow the Interpolation Target. It is important to have cameras follow the Interpolation Target for the Camera to also be interpolated.

Sleep Thresholds

When enabled, interpolation of the root transform will not happen if the changes to the current transform state would fall below all of the indicated threshold values. This mitigates the effects of moving the root transform - which breaks physics caching, and prevents sleep. Allowing sleep is especially important for the Server Authority instance, as it will generate network traffic even when moving very slightly. Allowing rigidbodies to sleep greatly reduces network traffic.

Note: Only Sleep Thresholds are only applicable when an Interpolation Target is not being used.

Use Render Sleep Thresholds

Enables the checks for thresholds. When disabled, the object is always interpolated.

Render Thresholds

  • Use Energy - Tests if energy level of velocity and angular velocity of the local rigidbody are above the sleep threshold, and will interpolate if so.
  • Position: If interpolation would produce a position change to the current transform state greater than this value, then interpolation will occur. A value of 0 excludes Position from the test.
  • Rotation: If interpolation would produce a rotation angle change to the current transform state greater than this value, then interpolation will occur. A value of 0 excludes rotation from the test.
  • Scale: If interpolation would produce a local scale change to the current transform state greater than this value, then interpolation will occur. A value of 0 excludes scale from the test.

Teleport()

See NetworkTransform.Teleport.

MovingTeleport()

Initiates a moving teleport. This method must be called in FixedUpdateNetwork() before RunnerSimulatePhysics3D and RunnerSimulatePhysics2D have simulated physics. This teleport is deferred until after physics has simulated, and captures position and rotation values both before and after simulation. This allows interpolation leading up to the teleport to have a valid pre-teleport TO target. This is an alternative to the basic Teleport(), which causes interpolation to freeze for one tick.

RunnerPhysicsSimulate2D / RunnerPhysicsSimulate3D

These components may be added to the NetworkRunner prefab, and allows you to specify how you want physics simulation to be handled. If Every FixedUpdateNetwork() this component will call the applicable Physics Simulate().

Physics Authority

Indicates whether Fusion will use this component to call Physics.Simulate()/Physics2D.Simulate() or if that will be left to Unity's Physics settings (auto-simulate or script modes).

Auto indicates that Fusion will take over Simulate() calls if:

  • Game is running in any Game Mode other than Shared Mode. Shared Mode by default assumes users are moving objects outside of FixedUpdateNetwork(). If your controller code applies inside of FixedUpdateNetwork(), then it is necessary to add this component and set the PhysicsAuthority to Fusion.
  • Multi-Peer mode is enabled. Unity does not auto-simulate non-primary Physics scenes, so any time Multi-Peer mode is used Fusion needs to control the Simulate() calls.

Physics Timing

Indicates which timing segment is to be used if Fusion is the Physics Authority.

Forward Only

When enabled (disabled by default) the applicable Physics Simulate() will not be called for re-simulated ticks. By default this should be disabled. Only applicable to Modes capable of re-simulation.

DeltaTime Multiplier

Multiplier for value passed to Physic's Simulate(). Use values greater than 1 to accelerate the passing of time and between 0 and 1 to slow time.

Set FixedTimestep

When enabled, this component will set Unity's Time FixedTimestep value to match Fusion's DeltaTime. The helps ensure any user code inside of FixedUpdate at mostly in agreement with Fusion's ticks.

IMPORTANT: FixedUpdate() and FixedUpdateNetwork() will never be fully aligned. All attempts should be made to avoid mixing usages of these two timing segments. Typically with Fusion all simulation code should be in FixedUpdateNetwork(), with the exception of Shared Mode where changes in Update() may be desired or necessary.

Callbacks

INetworkRunnerCallbacks.IBeforePhysicsStep and INetworkRunnerCallbacks.IAfterPhysicsStep have been removed in Fusion 2, and have been replaced with the following.

OnBeforeSimulate / OnAfterSimulate

Once registered, these methods will be called every time RunnerSimulatePhysics simulates.

C#

using Fusion;
using Fusion.Addons.Physics;
using UnityEngine;

public class FusionPhysicsAddonExample : NetworkBehaviour 
{
  private RunnerSimulatePhysics3D _physicsSimulator;
  
  public override void Spawned() 
  {
    // Get our RunnerSimulatorPhysics instance (this needs to be added to the Runner)
    _physicsSimulator = Runner.GetComponent<RunnerSimulatePhysics3D>();
    
    // Register callback for EVERY simulation tick
    _physicsSimulator.OnBeforeSimulate += OnBeforeEverySimulate;
  }
  
  public override void Despawned(NetworkRunner runner, bool hasState) 
  {
    // Unregister (a good practice)
    _physicsSimulator.OnBeforeSimulate -= OnBeforeEverySimulate;     
  }
  
  void OnBeforeEverySimulate() { 
    // Implement code to execute before every Physics.Simulate
  }
}

QueueBeforeSimulationCallback / QueueAfterSimulationCallback

These callbacks queue a one time callback that will trigger the next time a Simulation occurs.

C#

using Fusion;
using Fusion.Addons.Physics;
using UnityEngine;

public class FusionPhysicsAddonExample : NetworkBehaviour 
{
  private RunnerSimulatePhysics3D _physicsSimulator;
  
  public override void Spawned() 
  {
    // Get our RunnerSimulatorPhysics instance (this needs to be added to the Runner)
    _physicsSimulator = Runner.GetComponent<RunnerSimulatePhysics3D>
  }

  public override void FixedUpdateNetwork() 
  {
    if (_physicsSimulator.HasSimulatedThisTick) 
    {
      Debug.LogWarning($"Component is running FixedUpdateNetwork AFTER Physics Simulation, check my Script Exec Order");
      PostSimulateActivity();
    } else {
      // Queue our method for a one time deferred callback
      _physicsSimulator.QueueAfterSimulationCallback(PostSimulateActivity);
    }
  }

  void PostSimulateActivity() { 
    // Implement code to execute after specific Physics.Simulate calls
  }
}

Known issues

  • Child Rigidbodies interact with physics in an unpredictable way (even when set to Kinematic), and may collide with objects as if the rigidbody is in a prior position during simulation. Therefore it is recommended that carried objects not rely on collisions, and colliders should be disabled while nested.
  • Scale with nesting (parenting) relies on all parents not having Interpolation Targets. If you want to combine scaling with nesting as well as use Interpolation Targets, you will want to write custom handling that allows the interpolation targets to separate from the root and re-parent to the interpolation targets of their parents. Essentially creating a copy of the Rigidbody collider/transform hierarchy with all of the involved interpolation targets.
  • MovePosition() and MoveRotation require the use of an Interpolation Target. Moving kinematic rigidbodies instead by setting transform.position and transform.rotation values directly avoids this requirement.
Back to top