> For the complete documentation index, see [llms.txt](/llms.txt)


# Tutorial: Sync Player Characters

Steps to sync each player's character position and RPCs in Unity without writing rules



In this tutorial, you build a small room in which each player who joins moves their own character, and it appears the same way on the other players' screens.

- When you connect, one character of your own is spawned.
- Moving your character with the arrow keys (WASD) moves it smoothly on the other players' screens too.
- Pressing the Space key makes that character briefly grow larger on everyone's screen (RPC).

You use the `sync` server type. You do not need to write room rules. For the differences between server types, see [Server Types and How to Choose]().

## Workflow

| Step | Where you work | What you build |
| --- | --- | --- |
| [1. Configure the namespace](#namespace) | GS2 | The Realtime namespace settings |
| [2. Create the character Prefab](#prefab) | Unity Editor | A Prefab with movement and RPC scripts |
| [3. Connect to the room](#connect) | Unity Editor | A Prefab Catalog, and a scene that connects and spawns your character |
| [4. Verify the behavior](#verify) | Unity (2 clients) | — |
| [5. Restrict before release](#catalog) | GS2 | The object catalog |

You work in a Unity project (Unity 2021.3 or later) with the GS2 SDK for Unity. The sync components are included in the SDK, so no additional packages are required.

## 1. Configure the Namespace {#namespace}

In the GS2-Realtime namespace, specify the server type.

| Setting | Field | Value |
| --- | --- | --- |
| Server type | `serverType` | `sync` |

Preparing the room (GS2-Matchmaking and `RoomWant`) is the same as for the other server types.

## 2. Create the Character Prefab {#prefab}

### Prepare the Scripts

This script moves your own character. You only move the Transform, and NetworkTransform sends the position to the other participants. The Space key sends an RPC to everyone.

```csharp
using Gs2.Realtime.Sync;
using UnityEngine;

// Registered in NetworkEntity's Controlled Behaviours so it runs only on your own character
public sealed class PlayerMover : MonoBehaviour
{
    [SerializeField] float speed = 3f;

    void Update()
    {
        var move = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        // Just move the Transform; NetworkTransform sends it to the other participants
        transform.position += move.normalized * (speed * Time.deltaTime);

        if (Input.GetKeyDown(KeyCode.Space))
            GetComponent<NetworkEntity>().Rpc(nameof(PlayerEmote.Wave), RpcTarget.All);
    }
}
```

Add `[NetworkRpc]` to methods called by RPC. A method that returns `IEnumerator` runs as a coroutine.

```csharp
using System.Collections;
using Gs2.Realtime.Sync;
using UnityEngine;

public sealed class PlayerEmote : MonoBehaviour
{
    // The character briefly grows larger on everyone's screen
    [NetworkRpc]
    public IEnumerator Wave(NetworkRpcInfo info)
    {
        Debug.Log("Wave from connection " + info.Sender);
        transform.localScale = Vector3.one * 1.5f;
        yield return new WaitForSeconds(0.3f);
        transform.localScale = Vector3.one;
    }
}
```

`info.Sender` is the connection ID of the player who sent the RPC. The server sets it, so the sender cannot fake it.

### Create the Prefab

1. Create a Prefab for the character's appearance (a Capsule, for example), and add the following components to its root.

   | Component | Setting |
   | --- | --- |
   | **NetworkEntity** | Leave **Schema** empty (only position is synced) |
   | **NetworkTransform** | **Position Axes** is `All`, **Rotation Axes** is `None` |
   | **PlayerMover** | The script above |
   | **PlayerEmote** | The script above |

2. Register `PlayerMover` in **NetworkEntity > Controlled Behaviours**. `PlayerMover` is then enabled only on characters you own. If you do not register it, your key input is also applied to the other players' characters on your screen.

The SDK has standard fields for position and rotation, so they can be synced without a sync schema asset. To sync custom values such as HP, see [Display Custom Values](#bindings).

## 3. Connect to the Room {#connect}

### Place a NetworkWorld

1. In the Project window, create a catalog with **Create > GS2 > Realtime > Prefab Catalog**.
2. In the catalog's **Prefabs**, register **Id** `1` and the Prefab from step 2. The Prefab ID is a number that represents the kind of object; other clients use this number to choose which Prefab to display.
3. Create an empty GameObject in the scene, add **NetworkWorld**, and specify the catalog for **Prefab Catalog**.
4. Place a Plane for the ground and a Camera looking down from above.

### Add the Connection Script

Add **NetworkSession** and the following script to the same GameObject as the NetworkWorld. Obtain the room's connection information (IP address, port, encryption key) and the access token as described in [Obtaining connection information for the game server](#obtaining-connection-information-for-the-game-server), and pass them in.

```csharp
using Gs2.Realtime.Sync;
using UnityEngine;

[RequireComponent(typeof(NetworkWorld), typeof(NetworkSession))]
public sealed class PlayerRoomConnection : MonoBehaviour
{
    const uint PlayerPrefab = 1; // The Id registered in the Prefab Catalog

    NetworkWorld world;
    NetworkSession session;

    // Subscribe once: subscribing on each connection repeats the handlers after a reconnect
    void Awake()
    {
        world = GetComponent<NetworkWorld>();
        session = GetComponent<NetworkSession>();
        // Once the room state is applied, spawn your character.
        // The character is removed on disconnect, so create it again on each reconnect
        session.Ready += () => world.Instantiate(
            PlayerPrefab,
            new Vector3(Random.Range(-3f, 3f), 0, Random.Range(-3f, 3f)),
            Quaternion.identity);
        session.Disconnected += reason => Debug.Log("Disconnected: " + reason);
    }

    public void Connect(string accessToken, string ipAddress, int port, string encryptionKey)
    {
        session.Connect(accessToken, ipAddress, port, encryptionKey);
    }
}
```

There are three key points.

- **Create your own character with `world.Instantiate`.** GameObjects created with Unity's `Instantiate` are not shared with the other participants. The return value of `world.Instantiate` can be moved right away as your own (`IsMine`). Movement and RPCs before the room confirms the spawn are held and sent after it is confirmed.
- **NetworkWorld creates the other players' characters.** When it receives a spawn from the room, it creates a GameObject from the Prefab in the catalog and interpolates it toward the received positions.
- **When you connect, you receive the current state.** Players who join later also see the characters of players already in the room at their current positions. `Ready` is raised once it has been applied.

## 4. Verify the Behavior {#verify}

Connect to the same room from two clients. Connecting with the same userId replaces the earlier connection, so use access tokens for different userIds.

1. Confirm that both screens show two characters.
2. Move a character on one client, and confirm that the same character moves smoothly on the other screen too. Key input on the other client moves only its own character.
3. Press the Space key on one client, and confirm that the character briefly grows larger on both screens.
4. Disconnect one client, and confirm that its character disappears from the other screen. When it reconnects, a new character is spawned.

## 5. Restrict Before Release {#catalog}

With the settings so far, the room does not check the Prefabs players spawn or the RPCs they call. To restrict spawns and RPCs from modified clients, set an [object catalog](#catalog) in the namespace setting `syncSetting.objectCatalog`.

1. Right-click the Prefab Catalog asset and select **Copy Object Catalog JSON**. The synced fields and RPC IDs of the registered Prefabs are copied to the clipboard as JSON.
2. Set that JSON in `syncSetting.objectCatalog`.

Once it is set, only registered Prefabs can be spawned, and owners can call only the RPCs registered in `methods`. RPC IDs are derived from method names, so regenerate the JSON when you rename a method.

## What to Read Next

- [Unity Client](): lending control, room events, custom properties, the master client, Interest Groups, UDP, and handling reconnection.
- [Migration Guide from Photon](): a mapping table to the PUN2 API and the migration steps.
- [Tutorial: Build an Enemy Whose HP Is Managed by the Server](): how to use the `stateMachine` server type, where rules spawn and update objects.
- [Tutorial: Move Characters with Rollback Sync](): how the server calculates positions while you move your own character with prediction.



