PUN Classic (v1)、PUN 2、Boltはメンテナンスモードとなっております。Unity2022についてはPUN 2でサポートいたしますが、新機能が追加されることはありません。お客様のPUNプロジェクトおよびBoltプロジェクトが停止することはなく、将来にわたってパフォーマンス性能が落ちることはありません。 今後の新しいプロジェクトについては、Photon FusionまたはQuantumへ切り替えていただくようよろしくお願いいたします。

Bolt 102 - はじめに

このチュートリアルでは、Boltでマルチプレイヤーゲームを作成する方法を説明します。 Boltを使用してシンプルな画面を複数設定していきます。Boltの内部での変換の置換やオブジェクトのスポーンといった全ての基本タスクをいかに行うか、ステップ・バイ・ステップで見ていきましょう。チュートリアルとして明確に説明するため、自動化やテストツール内のBoltのビルドはできる限り使用せず、コードを記述します。

このチュートリアルは、BoltのDEBUGビルドを用いながら読み進めていくように作られております。それ以外のビルドに関する詳細については、Debug vs. Releaseのページを確認してください。Boltのアップグレード方法についてはUpgradingを参照してください。Boltのインストールとアップグレードを行うと、以下のようなフォルダ構造を得られます。

bolt folder on unity assets
Unityアセット上のBoltフォルダ

パッケージフォルダには、Bolt用のアドオンがあります。 これらをインポートする場合には、はじめよう チュートリアルで、既存のBoltアセットを作成するよう確認されます。 この場合には、インポートを保留するか、または同じ名前で新規に作成する前に既存のBoltアセットを削除できます。

Boltが、Unity向けの他のネットワーキングソリューションと異なるのは、ネットワーキングコードを手動で書く必要がほとんどないという点です。シリアル化、補間、アニメーション複製などはすべてBoltで自動的に処理されます。

ネットワーク上で を複製したいのか、のみBoltに伝える必要があります。 これは、Bolt/Assets内の Bolt Assets ウィンドウで設定できます。

access bolt assets from window menu
WindowメニューからBolt Assetsにアクセス

アセットは4つのカテゴリーに分けられます:

  • ステート:これらは、Boltで構築されるほとんどのゲームの中核となります。ステートによって、名前、ヘルス、変換、アニメーションパラメータなどを定義できます。
  • オブジェクト: オブジェクトは、C#クラスでおこなうのと同様に複数のプロパティを論理的にグループ化する方法です。たとえば、RPGゲームでプレイヤーにインベントリーを作成するとします。この場合、Item という名前の Object を作成し ItemIdDurability などのプロパティを設定することができます。その後、State 上に Item オブジェクトの配列を作成することが可能です。
  • コマンド: これらは Bolt のオーソリテーティブな機能に特化して使用され、ユーザー入力の要約や、ユーザー入力をワールドでキャラクターに適用した際の結果を要約します。コマンド によって、クライアントサイドでの高度な予測の実装や、予測されたステートの自動修正をおこなえるようになります。
  • イベント: これらは単純なメッセージです。イベントを使用すると、ゲーム内の様々な部分を分離することができます。これは、同時にアクティブな単一イベントに複数のリスナーを持てるためです。

Bolt Assetsウィンドウの最下部にBoltのバージョン番号、Debug / Releaseモードのどちらで構築されたか、またBoltアセットをコンパイルおよび保存するための2つのボタンが表示されます。

Bolt Assetsウィンドウの空白部分で右クリックし、 New Stateを選択してください。

bolt assets window - create a new state
Bolt Assetウィンドウ - ステートを新規作成

Bolt はNewStateという名前のステートを新規作成し、そのステートをアクティブにしてBolt Editorウィンドウをポップアップします。

bolt assets window - new state
Bolt Assetウィンドウ - 新しいステート

新しいステートを設定します:

  1. 最初のテキストフィールドで、ステートの名前をCubeStateに変更します。
  2. このステートに新しいプロパティを作成するには、New Propertyをクリックします。
  3. プロパティの名前をCubeTransformと設定します。
  4. タイプをTransformと設定します。
  5. 他の設定はデフォルトのままにしてください。
bolt assets window - edit new state
Bolt Assetウィンドウ - 新しいステートを編集

必要のないプロパティやアセットを誤って加えてしまった場合にはCtrlキーを押したままにしてください。小さな赤いXがすべてのアセットおよびプロパティの隣にポップアップされ、アセットやプロパティを削除できるようになります。

コンパイルが必要なステートやプロパティをBoltに認識させるには、Bolt/Compile Assemblyメニューオプションから、または Bolt Assets ウィンドウの小さな緑の矢印アイコンから設定をおこないます。どちらからも同じ内容が実行できます。

bolt compile menu
Boltのコンパイルメニュー

コンパイルすると、Boltは複数のメッセージをUnityエディターコンソール内に出力します。

bolt compile output
Boltコンパイルの出力

Unity内のProjectタブに進んでTutorialという名前のフォルダを作成し、ScriptsPrefabsという名前の2つのサブフォルダを作成してください。

new tutorial folder
新しいチュートリアルフォルダ

まず始めに、Bolt用にプレハブを作成します。これは標準的なUnityキューブとなります。 新しいキューブの作成はGameObject/3D Object/Cubeメニューから、またはHierarchyパネルで右クリックしておこなってください。キューブを作成したら、Hierarchyから、Projectタブにある Tutorial/Prefabsフォルダにドラッグしてください。 ここまで完了したら、Hierarchyに残っているキューブを消去できます。

new cube prefab
新しいキューブプレハブ

このCubeのプレハブをBoltに認識させるには、プレハブにBolt Entityコンポーネントを追加し、Add Componentをクリックして名前でコンポーネントを検索してください。

bolt entity creation
Boltエンティティの作成

このCubeのプレハブをBoltが認識するのはこれが初めてであるため、Bolt Entityのコンポーネントエディタには複数のエラーが発生します。 Prefab Idフィールドの下にある2つのエラーは、Bolt用のコンパイラーを実行することで解決できます。Bolt/Compile Assemblyから、またはBolt Assetsウィンドウにある緑の矢印アイコンからおこなってください。 State欄の下にあるエラーを解決するには、ドロップダウンからICubeStateステートを選択してください。現状ではNOT ASSIGNEDと表示されています。

bolt entity setup
Boltエンティティのセットアップ

Bolt Entityのコンポーネントに関する設定の多くは変更可能です。ただし、ほぼすべてのプレハブやゲームのタイプでデフォルト設定が必要な条件を十分に満たしているため、設定を変更する必要はありません。これらの設定は、このドキュメントのさらに高度なセクションで説明されているような特殊な場合に用いられます。

実際のC#コードについて進めていく前に、キューブをスポーンする単純なシーンをUnityに設定しましょう。新たにUnityシーンを作成し、Tutorial/Tutorial1に保存してください。今回のシーンのために、GameObject/3D Object/Planeから接地面を作成します。GameObject/3D Object/Planeは基盤として、また接地面用の新しいマテリアルとして使用されます。接地面をTutorial/Material_Groundに保存してください。キューブ以外の色が使用可能となります。

tutorial scene creation
チュートリアルシーンの作成

続いてメインカメラ接地面、Directional Lightをシーンに位置づけるための設定を説明します。これによって、類似したシーンの設定が可能になります。

scene configuration for each game object
各ゲームオブジェクトのシーン設定

それでは実際にコードを書いてみましょう。まず、Tutorial/Scriptsフォルダ内にNetworkCallbacksという名前の新たなスクリプトを作成します。それを任意のテキストエディタで開き、デフォルトのUnityメソッドを削除してください。すると、ファイルは以下のようになります。

C#

using UnityEngine;
using System.Collections;

public class NetworkCallbacks : MonoBehaviour
{

}

ベースクラスをMonoBehaviourからGlobalEventListenerに変更します。GlobalEventListenerクラス自体がMonoBehaviourに由来するものです。このため、全て通常のUnityと同様の処理が可能ですが、最上部にはBolt特有のメソッドが複数追加されています。

C#

using UnityEngine;
using System.Collections;
using Bolt;

public class NetworkCallbacks : GlobalEventListener
{

}

シーンがロードしているとき(今回はTutorial1のシーンがロードしているとき)にコールバックを受信する必要があります。これは、Cubeのプレハブをインスタンス化できるタイミングを把握するためです。Boltは自動でネットワーク化されたUnityシーンのロードをサポートしており、これにフックしたいコールバックは、SceneLoadLocalDoneと呼ばれます。

Bolt は、メソッドの実装にpublic override標準のC#を使用する点に留意してください。これはメソッドをオーバーライドとして指定しないUnityの方法と異なります。

C#

using UnityEngine;
using System.Collections;
using Bolt;

public class NetworkCallbacks : GlobalEventListener
{
    public override void SceneLoadLocalDone(string scene)
    {
        // Your code here...
    }
}

Boltでプレハブのインスタンスを作成するには、BoltNetwork.Instantiateメソッドを使用します。 通常、これはUnityでの組み込みGameObject.Instantiateと全く同様に動作します。 このメソッドには様々な負荷やパラメータが存在しますが、ここでは比較的シンプルなものを用いていきます。Boltプレハブリファレンス、ポジション、ローテーションを使用します。

C#

using UnityEngine;
using System.Collections;
using Bolt;

public class NetworkCallbacks : GlobalEventListener
{
    public override void SceneLoadLocalDone(string scene)
    {
        // randomize a position
        var spawnPosition = new Vector3(Random.Range(-8, 8), 0, Random.Range(-8, 8));

        // instantiate cube
        BoltNetwork.Instantiate(BoltPrefabs.Cube, spawnPosition, Quaternion.identity);
    }
}

XZ 接地面での位置をランダム化し、全員が同じ場所にスポーンしないようにします。その後、BoltNetwork.Instantiateをランダムポジション、デフォルトローテーション、Cubeへのリファレンスと共に呼び出します。

BoltPrefabs.Cubeの使用は、Boltに慣れていないうちは難しいかもしれません。 BoltPrefabsは静的なクラスで、Bolt/Compile Assemblyを実行した際にコンパイルおよびアップデートされます。 BoltPrefabsには各プレハブへの一意のリファレンスとそれにおけるBolt Entityが含まれます。 また、任意でプレハブに通常のGameObjectリファレンスを渡すことも可能です。

NetworkCallbacksスクリプトについて完了する前に、[BoltGlobalBehaviour]と呼ばれる属性を追加する必要があります。 public class NetworkCallbacks ...定義の上の行にこの属性を追加してください。

C#

using UnityEngine;
using System.Collections;

[BoltGlobalBehaviour]
public class NetworkCallbacks : Bolt.GlobalEventListener
{
    public override void SceneLoadLocalDone(string scene)
    {
        // randomize a position
        var spawnPosition = new Vector3(Random.Range(-16, 16), 0, Random.Range(-16, 16));

        // instantiate cube
        BoltNetwork.Instantiate(BoltPrefabs.Cube, spawnPosition, Quaternion.identity);
    }
}

この属性によって、Boltは自動的にこのスクリプトを検知し、そのインスタンスを作成できるようになります。これはBoltと共生し、Boltがシャットダウンされると破壊されます。

重要: このスクリプトのインスタンスは、Unity内のどのGameObjectにも手動で添付しないでください。添付はBoltが自動で行います。 GlobalEventListenerを手動でどこかに添付したい場合には、[BoltGlobalBehaviour] の属性を追加しては いけません。

キューブのスポーンをテストする前の最後のステップは、Boltを開始するメインメニューのスクリプトの設定です。 新たなシーンを作成し、それをTutorial/Tutorial1_Menuに保存してください。

先に進んでメニュー用のコードを書く前に、Tutorial1Tutorial1_Menuの両方のシーンが、Unity内のBuild Settingsに追加されていることを確認してください。

configure scenes to build
  ビルドするシーンを設定

重要: 両方のシーンを追加したら、Bolt/Compile Assemblyを再度実行し、Boltにシーンを認識させてください。 また、Tutorial1_Menuシーンが最初のシーンで(インデックス0)、ゲームが始まったらこの読み込まれるようになっていることを確認してください。

非常にシンプルなメニュースクリプトを作成し、それをTutorial1_Menuシーン内のMainCameraに添付します。 以下に示したように、Tutorial/Scripts内に、Menuという名前のスクリプトを作成してください。

C#

using System;
using UnityEngine;
using Bolt;
using Bolt.Matchmaking;
using UdpKit;

public class Menu : GlobalEventListener
{
    void OnGUI()
    {
        // Your code here...
    }
}

デフォルトのUnityメソッドを削除し、 OnGUIメソッドを追加してください。 画面のほぼ全体に渡るボタンを2つ作成します。1つはサーバーの起動用、もう一つはクライアントの起動用です。 通常どおり、このクラスはGlobalEventListenerから拡張するように設定する必要があります。これは、Boltの挙動を使用できるようにするためです。

C#

using System;
using UnityEngine;
using Bolt;
using Bolt.Matchmaking;
using UdpKit;

public class Menu : GlobalEventListener {

    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(10, 10, Screen.width - 20, Screen.height - 20));

        if (GUILayout.Button("Start Server", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)))
        {
            // START SERVER
            BoltLauncher.StartServer();
        }

        if (GUILayout.Button("Start Client", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)))
        {
            // START CLIENT
            BoltLauncher.StartClient();
        }

        GUILayout.EndArea();
    }

    public override void BoltStartDone()
    {
        if (BoltNetwork.IsServer)
        {
            string matchName = Guid.NewGuid().ToString();

            BoltMatchmaking.CreateSession(
                sessionID: matchName,
                sceneToLoad: "Tutorial1"
            );
        }
    }

    public override void SessionListUpdated(Map<Guid, UdpSession> sessionList)
    {
        Debug.LogFormat("Session list updated: {0} total sessions", sessionList.Count);

        foreach (var session in sessionList)
        {
            UdpSession photonSession = session.Value as UdpSession;

            if (photonSession.Source == UdpSessionSource.Photon)
            {
                BoltMatchmaking.JoinSession(photonSession);
            }
        }
    }
}

すべてのGUILayout.* コールは、標準的なUnityの仕様となっていますので割愛します。コードで特に重要な2点は、メソッドBoltStartDoneSessionListUpdatedd内にあります。

サーバーはBoltLauncher.StartServerを呼ぶことで起動され、これによってこのピアはゲームのホストとして設定されます。 サーバーを起動後に(BoltStartDoneが呼ばれたら)、1つの重要なメソッドを呼び出します。

  • BoltMatchmaking.CreateSession:新しいピアがこのルームに接続してゲームに参加することを予期して、このメソッドはPhoton Server上にPhoton Cloudルームをセットアップします。 ルームの名前は、この関数のsessionID引数を使用して設定できます。例として、ルームにランダムな名前を作成します。sceneToLoad引数を使用し、BoltTutorial1シーンをロードするよう伝えます。

クライアント上の処理はサーバー上での処理に似ています。このピアをクライアントとして設定して他のゲームに参加できるようにするため、BoltLauncher.StartClientを呼び出します。 主な違いは、クライアントがHostピアに接続するには、Photon Server上でゲームサーバーによって作成されたルームについての情報が必要である点です。

Boltは自動でこの処理をおこなうため、設定は不要で利便性が高まっています。 クライアントが開始すると、クライアントはPhoton Serverに接続し、クラウド上に登録され表示されているすべてのルームについての情報を取得します。 この情報を取得するには、SessionListUpdatedコールバックを使用します。このコールバックは、新しいルームが作成された場合、ルームが削除された場合、ルームのプロパティが変更された場合など、ルームリストがアップデートされるたびにBoltによって呼び出されます。

SessionListUpdatedの実装では、利用可能なすべてのセッションを対象に Photon Session を検索し、最初に見つかったものに接続します。 クライアントは Host でNATパンチスルー処理を開始し、ゲームに参加します。

MenuスクリプトをTutorial1_Menuシーン内のMainCameraゲームオブジェクトに添付してください。

tutorial menu scene setup
Tutorial Menuシーンのセットアップ

重要: Menuスクリプトの添付先が正しいシーンであることを確認してください。

ゲームを開始する前に、Unityの'Player Settings'で'Run In Background'を有効化してください。

configure game to run in background
Run in Backgroundするよう、ゲームを設定

ゲームのスタンドアロンなデスクトップバージョンを作成し、その2つのインスタンスを開始してください。ほとんどの場合、ウィンドウにはWindows Firewallのポップアップが表示されるので、 Allow accessをクリックしてください。Macのマシン上では、ピアが接続を試行すると類似したウィンドウが表示されるためAllowをクリックします。

Windowedモードで実行している点を確認し、 16:9の比率の解像度を選択してください。

start game configuration
ゲームの設定を開始

ゲームが開始したら最初のStart Serverと2つ目のStart Clientをクリックしてください。ゲームの2つのインスタンスが実行中である点と、2つのキューブがスポーンしている点を確認できます(ゲームの各インスタンスに対するものです)

game running.
実行中のゲーム

Debugモードで実行した場合にBoltがデフォルトで追加するConsoleと、小さなBolt Performanceディスプレイが最下部に表示されます。

次章 >>に続きます。

Back to top