Bolt 102 - はじめに
このチュートリアルでは、Boltでマルチプレイヤーゲームを作成する方法を説明します。
Boltを使用してシンプルな画面を複数設定していきます。Boltの内部での変換の置換やオブジェクトのスポーンといった全ての基本タスクをいかに行うか、ステップ・バイ・ステップで見ていきましょう。チュートリアルとして明確に説明するため、自動化やテストツール内のBoltのビルドはできる限り使用せず、コードを記述します。
このチュートリアルは、BoltのDEBUG
ビルドを用いながら読み進めていくように作られております。それ以外のビルドに関する詳細については、Debug vs. Releaseのページを確認してください。Boltのアップグレード方法についてはUpgradingを参照してください。Boltのインストールとアップグレードを行うと、以下のようなフォルダ構造を得られます。
パッケージフォルダには、Bolt
用のアドオンがあります。
これらをインポートする場合には、はじめよう チュートリアルで、既存のBoltアセットを作成するよう確認されます。
この場合には、インポートを保留するか、または同じ名前で新規に作成する前に既存のBoltアセットを削除できます。
Bolt
が、Unity向けの他のネットワーキングソリューションと異なるのは、ネットワーキングコードを手動で書く必要がほとんどないという点です。シリアル化、補間、アニメーション複製などはすべてBolt
で自動的に処理されます。
ネットワーク上で 何 を複製したいのか、のみBolt
に伝える必要があります。
これは、Bolt/Assets
内の Bolt Assets ウィンドウで設定できます。
アセットは4つのカテゴリーに分けられます:
- ステート:これらは、Boltで構築されるほとんどのゲームの中核となります。ステートによって、名前、ヘルス、変換、アニメーションパラメータなどを定義できます。
- オブジェクト: オブジェクトは、C#クラスでおこなうのと同様に複数のプロパティを論理的にグループ化する方法です。たとえば、RPGゲームでプレイヤーにインベントリーを作成するとします。この場合、Item という名前の Object を作成し ItemId、Durability などのプロパティを設定することができます。その後、State 上に Item オブジェクトの配列を作成することが可能です。
- コマンド: これらは
Bolt
のオーソリテーティブな機能に特化して使用され、ユーザー入力の要約や、ユーザー入力をワールドでキャラクターに適用した際の結果を要約します。コマンド によって、クライアントサイドでの高度な予測の実装や、予測されたステートの自動修正をおこなえるようになります。 - イベント: これらは単純なメッセージです。イベントを使用すると、ゲーム内の様々な部分を分離することができます。これは、同時にアクティブな単一イベントに複数のリスナーを持てるためです。
Bolt Assetsウィンドウの最下部にBoltのバージョン番号、Debug / Release
モードのどちらで構築されたか、またBoltアセットをコンパイルおよび保存するための2つのボタンが表示されます。
Bolt Assetsウィンドウの空白部分で右クリックし、 New State
を選択してください。
Bolt はNewState
という名前のステートを新規作成し、そのステートをアクティブにしてBolt Editor
ウィンドウをポップアップします。
新しいステートを設定します:
- 最初のテキストフィールドで、ステートの名前を
CubeState
に変更します。 - このステートに新しいプロパティを作成するには、
New Property
をクリックします。 - プロパティの名前を
CubeTransform
と設定します。 - タイプを
Transform
と設定します。 - 他の設定はデフォルトのままにしてください。
必要のないプロパティやアセットを誤って加えてしまった場合にはCtrl
キーを押したままにしてください。小さな赤いX
がすべてのアセットおよびプロパティの隣にポップアップされ、アセットやプロパティを削除できるようになります。
コンパイルが必要なステートやプロパティをBolt
に認識させるには、Bolt/Compile Assembly
メニューオプションから、または Bolt Assets ウィンドウの小さな緑の矢印アイコンから設定をおこないます。どちらからも同じ内容が実行できます。
コンパイルすると、Boltは複数のメッセージをUnityエディターコンソール内に出力します。
Unity内のProject
タブに進んでTutorial
という名前のフォルダを作成し、Scripts
とPrefabs
という名前の2つのサブフォルダを作成してください。
まず始めに、Bolt
用にプレハブを作成します。これは標準的なUnityキューブとなります。
新しいキューブの作成はGameObject/3D Object/Cube
メニューから、またはHierarchyパネルで右クリックしておこなってください。キューブを作成したら、Hierarchyから、Projectタブにある Tutorial/Prefabs
フォルダにドラッグしてください。
ここまで完了したら、Hierarchyに残っているキューブを消去できます。
このCube
のプレハブをBolt
に認識させるには、プレハブにBolt Entity
コンポーネントを追加し、Add Component
をクリックして名前でコンポーネントを検索してください。
このCube
のプレハブをBoltが認識するのはこれが初めてであるため、Bolt Entity
のコンポーネントエディタには複数のエラーが発生します。
Prefab Id
フィールドの下にある2つのエラーは、Bolt用のコンパイラーを実行することで解決できます。Bolt/Compile Assembly
から、またはBolt Assets
ウィンドウにある緑の矢印アイコンからおこなってください。
State
欄の下にあるエラーを解決するには、ドロップダウンからICubeState
ステートを選択してください。現状ではNOT ASSIGNED
と表示されています。
Bolt Entity
のコンポーネントに関する設定の多くは変更可能です。ただし、ほぼすべてのプレハブやゲームのタイプでデフォルト設定が必要な条件を十分に満たしているため、設定を変更する必要はありません。これらの設定は、このドキュメントのさらに高度なセクションで説明されているような特殊な場合に用いられます。
実際のC#コードについて進めていく前に、キューブをスポーンする単純なシーンをUnityに設定しましょう。新たにUnityシーンを作成し、Tutorial/Tutorial1
に保存してください。今回のシーンのために、GameObject/3D Object/Plane
から接地面を作成します。GameObject/3D Object/Plane
は基盤として、また接地面用の新しいマテリアルとして使用されます。接地面をTutorial/Material_Ground
に保存してください。キューブ以外の色が使用可能となります。
続いてメインカメラ
、接地
面、Directional Light
をシーンに位置づけるための設定を説明します。これによって、類似したシーンの設定が可能になります。
それでは実際にコードを書いてみましょう。まず、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
に保存してください。
先に進んでメニュー用のコードを書く前に、Tutorial1
とTutorial1_Menu
の両方のシーンが、Unity内のBuild Settingsに追加されていることを確認してください。
重要: 両方のシーンを追加したら、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点は、メソッドBoltStartDone
とSessionListUpdated
d内にあります。
サーバーはBoltLauncher.StartServer
を呼ぶことで起動され、これによってこのピアはゲームのホスト
として設定されます。
サーバーを起動後に(BoltStartDone
が呼ばれたら)、1つの重要なメソッドを呼び出します。
BoltMatchmaking.CreateSession
:新しいピアがこのルームに接続してゲームに参加することを予期して、このメソッドはPhoton Server上にPhoton Cloudルームをセットアップします。
ルームの名前は、この関数のsessionID
引数を使用して設定できます。例として、ルームにランダムな名前を作成します。sceneToLoad
引数を使用し、Bolt
にTutorial1
シーンをロードするよう伝えます。
クライアント上の処理はサーバー上での処理に似ています。このピアをクライアントとして設定して他のゲームに参加できるようにするため、BoltLauncher.StartClient
を呼び出します。
主な違いは、クライアントがHost
ピアに接続するには、Photon Server上でゲームサーバーによって作成されたルームについての情報が必要である点です。
Bolt
は自動でこの処理をおこなうため、設定は不要で利便性が高まっています。
クライアントが開始すると、クライアントはPhoton Serverに接続し、クラウド上に登録され表示されているすべてのルームについての情報を取得します。
この情報を取得するには、SessionListUpdated
コールバックを使用します。このコールバックは、新しいルームが作成された場合、ルームが削除された場合、ルームのプロパティが変更された場合など、ルームリストがアップデートされるたびにBolt
によって呼び出されます。
SessionListUpdated
の実装では、利用可能なすべてのセッションを対象に Photon Session を検索し、最初に見つかったものに接続します。
クライアントは Host
でNATパンチスルー処理を開始し、ゲームに参加します。
Menu
スクリプトをTutorial1_Menu
シーン内のMainCamera
ゲームオブジェクトに添付してください。
重要: Menu
スクリプトの添付先が正しいシーンであることを確認してください。
ゲームを開始する前に、Unityの'Player Settings'で'Run In Background'を有効化してください。
ゲームのスタンドアロンなデスクトップバージョンを作成し、その2つのインスタンスを開始してください。ほとんどの場合、ウィンドウにはWindows Firewall
のポップアップが表示されるので、 Allow access
をクリックしてください。Mac
のマシン上では、ピアが接続を試行すると類似したウィンドウが表示されるためAllow
をクリックします。
Windowed
モードで実行している点を確認し、 16:9
の比率の解像度を選択してください。
ゲームが開始したら最初のStart Server
と2つ目のStart Client
をクリックしてください。ゲームの2つのインスタンスが実行中である点と、2つのキューブがスポーンしている点を確認できます(ゲームの各インスタンスに対するものです)
Debug
モードで実行した場合にBolt
がデフォルトで追加するConsole
と、小さなBolt Performance
ディスプレイが最下部に表示されます。
次章 >>に続きます。
Back to top