リアルタイム編:2人のキャラクターを同期する

自分が動かしたキャラクターを、相手の画面にも表示してみましょう。

二つのゲーム画面を同じルームにつなぎます。WASD キーでキャラクターを動かし、スペースキーを押すと両方の画面で同じキャラクターが一瞬大きくなります。

基本編で用意した GS2 の学習用プロジェクトと Unity プロジェクトを使います。このページでは新しい RealtimePractice シーンを作り、SDK から接続します。

1. リアルタイム用のネームスペースを作る

マネージメントコンソールで学習用プロジェクトの東京リージョンを開きます。

  1. Realtime → ネームスペースリスト → ネームスペースを新規作成を開きます。
  2. 次の値を入力して作成します。
項目値
名前adventure-sync
サーバの種類オブジェクト同期(sync)
サーバのスペックrealtime1.nano

2. ログインするシーンを作る

  1. Unity の File → New Sceneで空のシーンを作り、RealtimePractice として保存します。
  2. AdventureLogin.cs を Assets に保存します。同じファイルがすでにある場合は、そのファイルを使います。
  3. 空の Login オブジェクトを作り、AdventureLogin を追加します。
  4. マネージメントコンソールの Deploy → Default → アウトプットリストから、ApplicationClientId と ApplicationClientSecret を Client Id / Client Secret に入力します。
  5. Account Namespace は、Studio でアカウントを作った場合は Account、SDK コースの場合は adventure-account にします。Account Slot は realtime-a にします。
  6. Edit → Project Settings → Player → Other Settings → Active Input Handlingを Both にします。再起動を求められたら Unity を再起動します。
  7. 同じ Player 設定の Resolution and Presentation → Run In Backgroundをオンにします。別のウィンドウを操作している間も、ビルドしたアプリで同期を続けるための設定です。

3. 同期するキャラクターを作る

次の二つのファイルを Assets に保存します。

移動処理は Transform を動かします。位置を他の画面へ送る処理は、次の手順で追加する NetworkTransform が担当します。

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);

RPC は、指定した処理をほかの参加者の画面でも実行する仕組みです。ここでは [NetworkRpc] を付けた Wave が、キャラクターを0.3秒だけ大きくします。

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

キャラクターごとに操作権を持つプレイヤーを決めます。自分の入力で相手のキャラクターも動かしてしまわないよう、移動処理を Controlled Behaviours に登録します。この欄のスクリプトは、操作権を持つ画面でだけ動きます。

  1. GameObject → 3D Object → Capsuleを作り、名前を NetworkPlayer にします。
  2. 次のコンポーネントを追加します。
コンポーネント設定
Network EntitySchema は空欄。Controlled Behaviours に、このオブジェクトの AdventureRoomPlayer を1件追加
Network TransformPosition Axes は All、Rotation Axes は None
Adventure Room Player追加したまま使う
Adventure Room Emote追加したまま使う
  1. NetworkPlayer を Project ウィンドウにドラッグして Prefab にします。Hierarchy に残った Capsule は削除します。

4. 接続用のオブジェクトを作る

  1. Project ウィンドウで Create → GS2 → Realtime → Prefab Catalogを選び、PracticeCatalog を作ります。
  2. Prefabsに1件追加し、Idを 1、Prefabを NetworkPlayer にします。
  3. AdventureRoom.cs を Assets に保存します。
  4. 空の Room オブジェクトを作って AdventureRoom を追加します。NetworkWorld と NetworkSession も追加されます。
  5. Network World → Prefab Catalogに PracticeCatalog、Adventure Room → Loginに Login オブジェクトの AdventureLogin を割り当てます。
  6. GameObject → 3D Object → Planeで地面を作ります。
  7. GameObject → Cameraでカメラを作り、名前を Main Camera、Tag を MainCamera にします。位置を (0, 12, -10)、回転を (50, 0, 0) にします。
  8. GameObject → Light → Directional Lightでライトを作り、回転を (50, -30, 0) にします。

接続コードは、ルーム名からIPアドレス・ポート・暗号鍵を取得し、ログインしたプレイヤーのアクセストークンと一緒に渡します。

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());

接続が完了すると Ready が呼ばれ、Prefab ID 1 のキャラクターを生成します。

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

5. 二つの画面で動かす

  1. シーンを保存し、File → Build Profilesでデスクトップ向けのビルドを作ります。シーンリストに RealtimePractice を入れ、今回はほかのシーンのチェックを外して、RealtimePractice だけを有効にします。
  2. ビルドしたアプリを起動し、ログイン画面の保存先を realtime-a にして ログインを押します。Unity Editor でも再生し、こちらの保存先は realtime-b にしてログインします。
  3. マネージメントコンソールで adventure-sync の ルームリスト → ルームを作成を開き、ルーム名を practice にして作成します。通知先ユーザーIDリストは空のまま使います。
  4. 一覧を更新し、practice の情報にIPアドレスとポートが表示されたら、アプリと Unity Editor の両方で、ルーム名 practice の このルームに接続を押します。
  5. 両方に二体のキャラクターが表示されたら、片方のゲーム画面をクリックして WASD キーを押します。同じキャラクターが両方の画面で動きます。
  6. スペースキーを押し、両方の画面でそのキャラクターが一瞬大きくなることを確かめます。

6. 操作する人を入れ替える

  1. Bの画面に表示された 自分の接続 ID を、Aの 操作権を渡す相手の接続 ID 欄に入力します。
  2. Aが所有するオブジェクトの このオブジェクトの操作権を渡すを押します。オブジェクト番号を控えておきます。
  3. Aで WASD キーを押しても、そのキャラクターは動かなくなります。Bでは、元から持っていたキャラクターと、受け取ったキャラクターの両方が動きます。
  4. Bの入力欄にAの接続IDを入れ、控えたオブジェクト番号の操作権をAへ返します。再び、それぞれ一体ずつ操作できる状態になります。

操作権を渡す呼び出しは entity.TransferOwnership(相手の接続ID) です。所有者が変わると、Controlled Behaviours の移動スクリプトも、操作できる側で有効になります。

7. 切断して接続し直す

Aの 切断を押します。Bの画面からAのキャラクターが消えます。Aで同じルームへ接続し直すと、新しいキャラクターが現れ、Bのキャラクターは現在の位置に表示されます。

試し終わったら、両方の画面で切断します。マネージメントコンソールの adventure-sync のルームリストから、実習で作った practice を削除します。

サーバー側で敵のHPや勝敗を管理する実装は、GSLで敵を動かすチュートリアル(別タブ)に、ルール定義からUnityでの操作まで載っています。

チュートリアルのトップへ戻る