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.

TrueSync Tutorial Part 2

Tutorial Contents

This tutorial series explains how to use Photon TrueSync to build a simple multiplayer game in Unity from the ground up. In part 1, we showed how to download and install TrueSync, and showed basic Photon Unity Network code to connect game clients to the Photon Cloud and have them join a Photon room.

What To Expect From Part 2:

A working game client that instantiates a box for each player connected to the room. Players can move their own boxes around, and all movement is synchronized by sending each player's input from one client to the other.

TrueSyncManager Prefab and Component

Open the "Game" scene that you created in part 1 of this tutorial. Now insert the TrueSyncManager prefab into the scene (Unity menu: Game Object/TrueSync/TrueSyncManager). Now the scene hierarchy should look like:

scene hierarchy with truesyncmanager
Scene hierarchy with TrueSyncManager.

The TrueSyncManager prefab includes a component, also called TrueSyncManager, that is responsible for calling TrueSync updates and then controlling the lockstep simulation.

truesyncmanager component
TrueSyncManager Component.

Most of TrueSync's options are set in a different asset that we'll cover in part 4 of this tutorial, so the only thing we need to add now is a player prefab.

Don't forget to save the scene, now containing the TrueSyncManager object in it.

Creating a Player Prefab

A Player Prefab is a game object that shall have a copy instantiated for each player connected to the game session/room.

You can always instantiate objects by code, as we'll see in the next section, but TrueSync includes a convenient way to automatically create objects that players will control.

Now, use the Unity menu to conveniently create a TrueSync Cube, which already includes the deterministic physics components needed for our example. Click on "GameObject/TrueSync/Cube" to create one.

Set the TSRigidbody component as the image bellow. We'll control the movement by code, so check it as kinematic, without being affected by gravity. Also make sure the game object has its Tag set to "Player".

tsrigidbody component
TSRigidbody Component.

Now change the name of the game object to "PlayerBox" and drag it to the "Tutorial" folder, so it is saved in a new prefab.

You must now delete the game object from the scene and add a reference to the prefab in the TrueSyncManager object that was created previously. Do this by changing the number of player prefabs in the TrueSyncManager to 1 (one), dragging the prefab from the "Tutorial" folder to the opened slot as in the figure bellow.

player prefab referenced in truesyncmanager
Player prefab referenced in TrueSyncManager.

In each client, TrueSync will create a copy of the player prefab for each player connected to the room. TrueSync also works in offline mode, so you can test-run the "Game" scene from the editor and check if a PlayerBox(Clone) object was automatically created into the hierarchy (see figure bellow).

instantiated player prefab in the scene
Instantiated player prefab in the scene.

Remember to save the scene after finishing this part.

Adding a TrueSyncBehaviour to the Player Prefab

TrueSync is a lockstep system, so the only thing that gets synchronized is player input. In this tutorial, we want the player to control the movement of it's own box only, so we'll add a script that does that.

Create a new C# script into the "Tutorial" folder and name it "PlayerMovement.cs". Now, make it look like the code bellow.

C#

using UnityEngine;
using System.Collections;
using TrueSync;

public class PlayerMovement : TrueSyncBehaviour {

}

The script now inherits from "TrueSyncBehaviour", and that means we're going to use different callbacks then Unity's defaults most of the time. The reason for this is that we want our game object to be controlled by our lockstep game loop instead of Unity's, which is not deterministic.

Now, attach the script to the previously create PlayerBox prefab, so when TrueSync instantiate a copy of it, the "PlayerMovement.cs" script will be part of it.

Always save the scene, so the prefabs and references are saved as well.

Gathering and Queuing Input

A lockstep system works by using the input from all players to run the simulation simultaneously in all clients at the same time, so the result on a frame by frame basis is always the same.

For this to work, we can't use Unity's input to directly manipulate a game object or change any value of the game state.

Instead, we'll add code that collects player input values from Unity and queues it into TrueSync's input system, so it will be distributed to all game clients, including the local one, to be used in another update method that we'll explain later on.

The following code registers the values for Unity's input axis into the TrueSync input queue. Add it to "PlayerMovement.cs".

C#

public override void OnSyncedInput() {
    FP accell = Input.GetAxis ("Vertical");
    FP steer = Input.GetAxis ("Horizontal");

    TrueSyncInput.SetFP (0, accell);
    TrueSyncInput.SetFP (1, steer);
}

Notice that we do this from inside the "OnSyncedInput" custom callback, which is called only for the local player objects. Objects owned by remote players will have this callback executed in their own machines only. TrueSync is responsible for exchanging the input between all machines.

It's also important that we convert from float to FixedPoint (FP), so all computation from now on is deterministic across different machines and platforms. In the next session we'll add more comments on this.

Moving the Box

Now that TrueSync has the axis input queued and distributed across machines, lets use it to move the player box. We'll do this inside another custom callback, called "OnSyncedUpdate", which is called by TrueSync only when all input for a specific tick/frame from all players (including the local one) is available:

C#

public override void OnSyncedUpdate () {
    FP accell = TrueSyncInput.GetFP (0);
    FP steer = TrueSyncInput.GetFP (1);

    accell *= 10 * TrueSyncManager.DeltaTime;
    steer *= 250 * TrueSyncManager.DeltaTime;

    tsTransform.Translate (0, 0, accell, Space.Self);
    tsTransform.Rotate (0, steer, 0);
}

Notice that we get the FixedPoint (FP) input values from the TrueSyncInput queue and multiply it by TrueSync's own version of (deterministic across machines) DeltaTime, and a speed factor.

You can replace the fixed speed factors by FixedPoint attributes if you want to calibrate them from Unity's editor at this time.

The two values are used to move/rotate the box. We do this by using the methods "Translate" and "Rotate", but pay attention that we're applying it to the "TSTransform" component and not Unity's transform.

The tsTransform variable is pre-referenced to the "TSTransform" component for scripts that inherit from "TrueSyncBehaviour".

This is needed because TrueSync requires all gameplay code to be deterministic, and most of Unity's subsystems are not guaranteed to be.

TrueSync includes many components that mimic Unity's APIs to make sure you can create loackstep game easily: two custom deterministic physics engine (2D and 3D), a Transform component (TSTransform), FixedPoint type (FP), Seeded randoms (TSRandom), math library (TSVector, TSMatrix, TSQuaternion), and so on.

Now that the script for your player prefab queues input into TrueSync, and uses its custom update callback to use this input to move the boxes around, lets test the code in multiplayer mode (you can always test the code in offline/single player mode by running the scene directly from the editor as well).

Don't forget to check that you attached the "PlayerMovement.cs" script to the "PlayerBox" prefab in the Project tab.

Another good thing to do now is to place the camera in a better position to see the player boxes moving around. We suggest the following values to its transform component:

camera transform in game scene
Camera transform in game scene.

Testing

Please, make sure both scenes (Tutorial/Menu and Tutorial/Game) are included into the build settings, and that Tutorial/Menu is the first one (the one Unity will load at start up).

Create a build of your choice and run two copies of it, or one copy and run the Menu scene from inside the Unity editor.

You should now see both clients connecting to Photon, and then loading the game scene, where each client controls its own box.

Now let's move on to Part 3 to learn how to synchronously shoot projectiles.

Back to top