インスタンス化
どのようなゲームでも、プレイヤーごとに1つ以上のプレイヤーオブジェクトをインスタンス化する必要があります。
ネットワーク化されたゲームでオブジェクトを同期するには、特別なワークフローを使用します。
PhotonNetwork.Instantiate
PUN ではPhotonNetwork.Instantiate
メソッドに開始位置と回転およびプレハブの名前を渡すことで、ネットワークオブジェクトの生成を自動的に管理することができます。
要件:プレハブは「Resources」フォルダの直下にあり (実行時に読み込むため)、PhotonView
コンポーネントを持つ必要があります。
ウェブプレイヤーに注意してください:デフォルトではResourcesフォルダのすべてのものが最初のシーンでストリーミングされます。
ウェブプレイヤーの設定で、「First streamed level」を用いることで、Resourcesフォルダのどのアセットを最初のレベルで使用するかを指定することができます。
最初のゲームシーンでこれを設定し、Resourcesフォルダのアセットを使わないようにすればプリローダーとメインメニューの速度は低下しません。
C#
void SpawnMyPlayerEverywhere()
{
PhotonNetwork.Instantiate("MyPrefabName", new Vector3(0, 0, 0), Quaternion.identity, 0);
//The last argument is an optional group number, feel free to ignore it for now.
}
新しいゲームオブジェクトのインスタンス化に設定が必要な場合、OnPhotonInstantiate(PhotonMessageInfo info)
をスクリプト内に実装できます。
このスクリプトは、インスタンス化をトリガーした情報(PhotonMessageInfo info)にコールされます。GameObjectをプレイヤーのタグオブジェクトとして設定することが可能です。
以下に例を示します:
C#
void OnPhotonInstantiate(PhotonMessageInfo info)
{
// e.g. store this gameobject as this player's character in PhotonPlayer.TagObject
info.sender.TagObject = this.GameObject;
}
ネットワークオブジェクトのライフタイム
PhotonNetwork.Instantiate
で作成されるゲームオブジェクトは通常、同じルーム内にいる限り存在し続けます。
Unityのシーンを切り替えるときと同様に、ルームを替えてもオブジェクトが移動することはありません。
クライアントがルームを退出するとき、そのプレイヤーが所有または作成したオブジェクトはすべて破壊されます。
この処理がゲームロジックに合致しない場合、このステップは省略してください。その場合、PhotonNetwork.autoCleanUpPlayerObjects
をfalseに設定して下さい。
または、マスタークライアントは PhotonNetwork.InstantiateSceneObject()
を使用して、ルームと同じライフタイムを持つGameObjectを作成することができます。
このオブジェクトはマスタークライアントではなく、ルームにひもづいています。
デフォルトではマスタークライアントがこれらのオブジェクトを管理しますが、photonView.TransferOwnership()
を用いてその管理を渡すことができます。
オーナーシップの移行については、デモを参照してください。
ネットワーク化されたシーンオブジェクト
シーン内のオブジェクトにPhotonViewsを設定できます。
PhotonViewsはデフォルトではマスタークライアントによって管理されますが、ルームに関連するRPCを送信するための「ニュートラル」なオブジェクトを作ると便利かもしれません。
重要:ルームに入る前に、ネットワーク化されたオブジェクトを持つシーンを読み込む場合、まだ使用できないPhotonView値がいくつかあります。
たとえば、ルーム内にいないとAwake()でisMine
を確認することはできません!
シーンの切り替え
シーンを読み込むとUnityは通常、階層内のすべてのGameObjectsを破壊します。
ネットワークオブジェクトも含まれるため、混乱を招く場合があります。
例:メニューシーンでは、ルームに参加して別のシーンを読み込みます。
ルームに早く到着してしまい、ルームの初期メッセージを取得するかもしれません。
PUNはネットワークオブジェクトのインスタンス化を始めますが、あなたのロジックは別のシーンを読み込むため、インスタンス化されたオブジェクトは失われてしまいます。
シーンの読み込みに関する問題を避けるにはPhotonNetwork.automaticallySyncScene
をtrueに設定し、PhotonNetwork.LoadLevel()
を用いてシーンを切り替えることができます。
RPCのタイミングと読み込みレベルを参照してください。
PrefabPoolを使用
多くのオブジェクトを高い頻度で再利用する場合には、メモリ割り当てと開放の繰り返しを避けるためプレハブプールを使用して、ゲームパフォーマンスへの悪影響を防止できます。
PUNのプレハブプールを使用するには、 IPunPrefabPool
インターフェースを実装する必要があります。インターフェースにはオブジェクトをプールに追加する機能と、プールから削除する機能があります。これらの機能は GameObject Instantiate(文字列prefabId、Vector3位置、Quaternion回転);
およびvoid Destroy(GameObject gameObject);
です。プール自体にはすべての種類の(動的)データ構造が可能です。以下の例ではキューを使用していますが、リストやHashsetの使用も可能です。GameObjectにはパブリックリファレンスも追加されており、後にインスペクターにも設定できます。プールと名づけられたクラスは、以下のとおりです。
C#
public class Pool : MonoBehaviour, IPunPrefabPool
{
private Queue<GameObject> pool;
public GameObject Prefab;
public new GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation)
{
}
public void Destroy(GameObject gameObject)
{
}
}
プール化システムを正常に動作させるため、UnityのAwake
機能を追加しています。この機能によって、キューがインスタンス化され、PUNに通知が送信されます。
C#
public void Awake()
{
pool = new Queue<GameObject>();
PhotonNetwork.PrefabPool = this;
}
PhotonNetwork.Instantiate
またはPhotonNetwork.Destroy
を使用する場合は常に、これらのコールは以前に実装したプール、特にInstantiate
とDestroy
機能を使用します。、現時点では、これらの機能はなにもおこなっていません。このため、これらの機能の挙動を調整します。
Instantiate
が呼ばれると、発生しうる状況として2つが考えられます。再び使用できるプールに少なくとも1つのオブジェクトがある場合と、新たなオブジェクトをインスタンス化する場合です。これを把握したうえで、この機能を以下のようにアップデートします。
C#
public new GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation)
{
if (pool.Count > 0)
{
GameObject go = pool.Dequeue();
go.transform.position = position;
go.transform.rotation = rotation;
go.SetActive(true);
return go;
}
return Instantiate(Prefab, position, rotation);
}
この例ではキューを使用しているため、オブジェクトを取得してプールからそのオブジェクトを削除するには1つのDequeue()
コールを使用します。リストを使用する場合には、同様の挙動を実装する必要があります。キューからオブジェクトを取得したら、変換データを設定して有効化します。プールが空の場合、新たなオブジェクトをInstantiate
します。
Destroy
が呼ばれると、オブジェクトを非有効化してプールに返す必要があります。この機能もアップデートが必要です。
C#
public void Destroy(GameObject gameObject)
{
gameObject.SetActive(false);
pool.Enqueue(gameObject);
}
注:パフォーマンスの観点からいって、GameObjectの非有効化は重要です。オブジェクトが不要な場合には、この処理をおこなってスクリプトの実行やオブジェクトのレンダリング、衝突の確認などを回避します。
シーン内のオブジェクトにこのスクリプトを必ず添付してください。
手動でインスタンス化
ネットワーク上でのオブジェクトのインスタンス化でResourcesフォルダに依存したくない場合には、
このセクションの末尾の例にあるように、 手動でオブジェクトをインスタンス化します。
手動でインスタンス化する主な理由は、 ウェブプレイヤーのストリーミング時にダウンロードされるものを管理するためです。
ストリーミングとUnityのResourcesフォルダに関する詳細は、こちらを
参照してください。
インスタンス化するオブジェクトにRPCを送ることができます。
もちろん、どのオブジェクトをインスタンス化するかをリモートクライアントに伝える方法が必要です。
単にGameObjectへの参照を送信することはできません。そのため、名前やそれに類するものを提供する必要があります。
オブジェクトの型と同様に重要なのが、オブジェクトのネットワークIDです。
PhotonView.viewID
はネットワークメッセージを正しいゲームオブジェクトやスクリプトに伝達するキーとなります。
手動で生成する場合、PhotonNetwork.AllocateViewID()
を使用してviewIDを割り当て、送信する必要があります。
ルームにいる全員が、新規オブジェクトに対して同じIDを持つ必要があります。
インスタンス化のためのRPCはバッファリングする必要があります。
後から接続するクライアントも、生成指示を受信しなければなりません。
C#
void SpawnPlayerEverywhere()
{
// You must be in a Room already
// Manually allocate PhotonViewID
int id1 = PhotonNetwork.AllocateViewID();
PhotonView photonView = this.GetComponent<PhotonView>();
photonView.RPC("SpawnOnNetwork", PhotonTargets.AllBuffered, transform.position, transform.rotation, id1);
}
public Transform playerPrefab; //set this in the inspector
[RPC]
void SpawnOnNetwork(Vector3 pos, Quaternion rot, int id1)
{
Transform newPlayer = Instantiate(playerPrefab, pos, rot) as Transform;
// Set player's PhotonView
PhotonView[] nViews = newPlayer.GetComponentsInChildren<PhotonView>();
nViews[0].viewID = id1;
}
アセットバンドルを使ってネットワークオブジェクトを読み込む場合、アセットバンドルの読み込みコードを追加して、サンプルの「playerPrefab」を自分のアセットバンドルのものと置き換えます。
Back to top