Please note: development of Photon TrueSync & Thunder is ceased and we will not publish any further updates. While existings apps are not affected, we recommend to migrate apps that are still in development.

Photon TrueSync Intro

Get Started

Photon TrueSync is a multiplayer lockstep system for Unity built on top of Photon Unity Networking.

In TrueSync, client machines exchange only the input generated by their respective players, and the simulation happens in lockstep across game clients, greatly reducing traffic, and allowing perfect synchronization.

It includes a custom physics engine and other classes to help the developer keep the simulation deterministic across machines.

TrueSync exports to almost all platforms supported by Unity.

Some Code Required

To get the best out of TrueSync, you will need to write some scripts. This page shows you the most important pieces to get you started.

Start a TrueSync Deterministic Section

A TrueSync section happens between game clients connected to a PUN (Photon Unity Network) room, so refer to its documentation on how to setup and create/join a room.

The actual TrueSync managed gameplay section is started by the master-client when a scene that contains a TrueSyncManager component attached to a game object is sync-loaded through PUN.

The TrueSyncManager checks if the game client is connected to a room, which implies a multiplayer lockstep section should be started. You can also use it without being connected to a PUN room, in which case the system works in offline mode. This is great for (re)using the same code and components for single-player parts of the game.

Queueing Input

In a lockstep system, input from the local player must be queued before being used in gameplay code. This way, the system can wait for input to come from remote players and sync its use with the queued local input.

C#

    public override void OnSyncedInput () {
        FP x = Input.GetAxis("Horizontal");
        TrueSyncInput.SetFP(0, x);
    }

The code above is all you need to register a player input into TrueSync's input queue to be used both locally and on remote clients.

The first parameter is a key, so you can register as many input values as you need.

Notice that you need to convert from float to FixedPoint (FP). Float numbers are not computed exactly the same way in different architectures, so this is needed to keep the simulation in sync across all clients.

Using input to update gamestate

Next, you'll use input values to update your game objects, just like any other game on Unity. The difference is that you get the input values from TrueSync's queue system instead of directly from Unity.

C#

    public override void OnSyncedUpdate () {
        TSVector move = new TSVector(TrueSyncInput.GetInt(0), 0, 0);
        tsTransform.Translate(move * TrueSyncManager.DeltaTime);
    }

The above code shows the TSVector class as well, which works exactly the same way as a Unity Vector3, but uses FP instead of floats for x, y and z. Notice that we also use TrueSync's delta-time instead of Unity's.

Physics

TrueSync comes with two custom physics engines, one for 2D games, and one for 3D. Both use a component architecture and APIs that are very similar the ones found on Unity's original physics engines.

truesync physics
TrueSync's custom 3D physics engine sphere collider, rigidbody and transform components with property drawers and gizmos on display.

TrueSync is packed with several game samples that demonstrate how to use its built in physics engines. The code below shows how to apply a force to a TSRigidbody, which is TrueSync's deterministic version of a physics rigidbody.

C#

    public override void OnSyncedUpdate () {
        TSRigidBody rb = GetComponent<TSRigidBody>();
        rb.AddForce(TSVector.forward * TrueSyncManager.DeltaTime);
    }

Collisions and triggers work similarly to Unity's physics counterparts, but all callback methods start with "OnSynced", meaning they's are called from TrueSync's deterministic physics engines.

C#

    public void OnSyncedCollisionEnter(GameObject other) {
        Destroy(other);
    }
Back to top