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


# Realtime: Synchronize two players

Show a character you control on another player’s screen.



Connect two game windows to the same room. Move a character with WASD, then press Space to briefly enlarge that character on both screens.

Use the GS2 tutorial project and Unity project from the basics. On this page, create a new `RealtimePractice` scene and connect through the SDK.

## 1. Create a Realtime namespace

In the management console, open the Tokyo region of your tutorial project.

1. Open **Realtime → Namespace list → Create namespace**.
2. Enter the following values and create it.

| Field | Value |
| --- | --- |
| Name | `adventure-sync` |
| Server type | Object synchronization (`sync`) |
| Server specification | `realtime1.nano` |

## 2. Create a scene with login

1. In Unity, create an empty scene with **File → New Scene** and save it as `RealtimePractice`.
2. Save <a href="/samples/adventure/en/AdventureLogin.cs" download>AdventureLogin.cs</a> in `Assets`. If you already have the file, use that copy.
3. Create an empty `Login` object and add `AdventureLogin`.
4. From **Deploy → Default → Output list** in the management console, copy `ApplicationClientId` and `ApplicationClientSecret` into **Client Id / Client Secret**.
5. Set **Account Namespace** to `Account` if you created accounts with Studio, or `adventure-account` for the SDK course. Set **Account Slot** to `realtime-a`.
6. Set **Edit → Project Settings → Player → Other Settings → Active Input Handling** to **Both**. Restart Unity if prompted.
7. In the same Player settings, enable **Resolution and Presentation → Run In Background**. This keeps synchronization running in the built app while you use another window.

## 3. Create the synchronized character

Save these two files in `Assets`.

- <a href="/samples/adventure/en/realtime/AdventureRoomPlayer.cs" download>AdventureRoomPlayer.cs</a>: movement and Space key input.
- <a href="/samples/adventure/en/realtime/AdventureRoomEmote.cs" download>AdventureRoomEmote.cs</a>: an action performed on everyone's screen.

The movement code changes the Transform. `NetworkTransform`, added in the next steps, sends the position to the other screens.

```csharp
var move = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
transform.position += move.normalized * (3f * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.Space))
    GetComponent<NetworkEntity>().Rpc(nameof(AdventureRoomEmote.Wave), RpcTarget.All);
```

An RPC runs a specified action on other participants' screens too. Here, `Wave`, marked with `[NetworkRpc]`, enlarges the character for 0.3 seconds.

```csharp
[NetworkRpc]
public IEnumerator Wave(NetworkRpcInfo info)
{
    transform.localScale = Vector3.one * 1.5f;
    yield return new WaitForSeconds(0.3f);
    transform.localScale = Vector3.one;
}
```

Each character has a player who owns its controls. Register the movement script under **Controlled Behaviours** so your input does not move the other player's character. Scripts in this list run only on the owner's screen.

1. Create **GameObject → 3D Object → Capsule** and name it `NetworkPlayer`.
2. Add the following components.

| Component | Settings |
| --- | --- |
| Network Entity | Leave Schema empty. Add this object's AdventureRoomPlayer to Controlled Behaviours |
| Network Transform | Position Axes: All; Rotation Axes: None |
| Adventure Room Player | Keep the defaults |
| Adventure Room Emote | Keep the defaults |

3. Drag `NetworkPlayer` into the Project window to create a Prefab. Delete the Capsule remaining in the Hierarchy.

## 4. Create the connection object

1. In the Project window, choose **Create → GS2 → Realtime → Prefab Catalog** and create `PracticeCatalog`.
2. Add one entry to **Prefabs**, with **Id** `1` and **Prefab** `NetworkPlayer`.
3. Save <a href="/samples/adventure/en/realtime/AdventureRoom.cs" download>AdventureRoom.cs</a> in `Assets`.
4. Create an empty `Room` object and add `AdventureRoom`. `NetworkWorld` and `NetworkSession` are added automatically.
5. Assign `PracticeCatalog` to **Network World → Prefab Catalog**, and the `Login` object's `AdventureLogin` to **Adventure Room → Login**.
6. Create the ground with **GameObject → 3D Object → Plane**.
7. Create **GameObject → Camera**, name it `Main Camera`, and set its Tag to `MainCamera`. Set position to `(0, 12, -10)` and rotation to `(50, 0, 0)`.
8. Create **GameObject → Light → Directional Light** and set its rotation to `(50, -30, 0)`.

The connection code looks up the room's IP address, port, and encryption key by name, then passes them along with the logged-in player's access token.

```csharp
session.Connect(login.Session.AccessToken.Token,
    get.IpAddress, get.Port, get.EncryptionKey);
await UniTask.WaitUntil(
    () => session.State == NetworkSessionState.Ready || session.State == NetworkSessionState.Disconnected,
    cancellationToken: this.GetCancellationTokenOnDestroy());
```

Once connected, `Ready` is called and spawns the character with Prefab ID `1`.

```csharp
world.Instantiate(1,
    new Vector3(Random.Range(-3f, 3f), 1, Random.Range(-3f, 3f)),
    Quaternion.identity);
```

## 5. Move characters in two windows

1. Save the scene and create a desktop build under **File → Build Profiles**. Add `RealtimePractice` to the scene list, uncheck the other scenes, and enable only `RealtimePractice` for this build.
2. Start the built app, set the login screen's account slot to `realtime-a`, and click **Log in**. Enter Play Mode in the Unity Editor too, set its slot to `realtime-b`, and log in.
3. In the management console, open **Room list → Create room** for `adventure-sync`. Create a room named `practice`. Leave the notification user ID list empty.
4. Refresh the list. When `practice` shows an IP address and port, click **Connect to this room** with room name `practice` in both the app and Unity Editor.
5. Once both windows show two characters, click one game window and press WASD. The same character moves in both windows.
6. Press Space and check that the character briefly grows on both screens.

## 6. Transfer control to the other player

1. Copy **My connection ID** from B's screen into A's **Connection ID to transfer control to** field.
2. For an object owned by A, click **Transfer control of this object**. Note its object number.
3. WASD on A no longer moves that character. On B, it moves both the original character and the transferred one.
4. Enter A's connection ID on B and return control of the noted object to A. Each player can control one character again.

Transfer control with `entity.TransferOwnership(otherConnectionId)`. When ownership changes, the movement script in `Controlled Behaviours` becomes enabled on the new owner's screen.

## 7. Disconnect and reconnect

Click **Disconnect** on A. A's character disappears from B's screen. Reconnect A to the same room: a new character appears, while B's character is shown at its current position.

When finished, disconnect both windows. In the management console, delete the `practice` room from `adventure-sync`'s room list.

To manage enemy HP and game results on the server, see the <a href='' target="_blank" rel="noopener">GSL enemy tutorial (new tab)</a>, from defining rules to trying them in Unity.

[Back to the tutorial]()




