前回まででPhotonの接続からルームへ入室までの流れが、なんとなくですが掴めてきました。
今回はPhotonを使ってオブジェクトの生成に挑戦してみますよ。
この記事にはUnity5.4.1f1及びPUN1.75を使用しています。
- Photonのオブジェクト生成API
- PhotonViewが必要なのです
- PhotonNetwork.Instantiate()とPhotonNetwork.InstantiateSceneObject()
Photonのオブジェクト生成API
Unity上でオブジェクトを生成するにはGameObject.Instantiate()
を使用して引数で渡したオブジェクトをコピーして生成しますよね。
ただ、マルチゲームでこの関数を使用しても、自分のゲーム上にはオブジェクトが生成されるけど、他のプレイヤーのゲーム上にはオブジェクトが生成されません。
さてどうしましょうか。
とりあえずPhotonNetwork
クラスが何とかしてくれそうな気がするのでスクリプトリファレンスを見てみましょう。
PhotonNetwork.Instantiate()
とPhotonNetwork.InstantiateSceneObject()
という生成関数らしきものが見つかりました。
引数にGameObjectではなく、作成するオブジェクトのResourcesフォルダからのパス
を渡す必要があるようです。
GameObject.Instantiate()
とResources.Load()
を組み合わせた、GameObject.Instantiate( Resources.Load() )
みたいな形でしょうか。
早速使ってみますよ。
適当なオブジェクトをResourcesフォルダに作ってそれをPhotonNetwork.Instantiate()
を使ってゲーム上で生成してみます。
using UnityEngine; public class DemoNetwork : MonoBehaviour { [SerializeField] private string m_resourcePath = ""; [SerializeField] private float m_randomCircle = 4.0f; private const string ROOM_NAME = "RoomA"; void Start() { PhotonNetwork.ConnectUsingSettings( null ); } void OnJoinedLobby() { PhotonNetwork.JoinOrCreateRoom( ROOM_NAME, new RoomOptions(), TypedLobby.Default ); } public void SpawnObject() { PhotonNetwork.Instantiate( m_resourcePath, GetRandomPosition(), Quaternion.identity, 0 ); } private Vector3 GetRandomPosition() { var rand = Random.insideUnitCircle * m_randomCircle; return rand; } } // class DemoNetwork
エラーが出た!
どういうことだ。
Failed to Instantiate prefab:Cube. Prefab must have a PhotonView component.
PhotonView
がどうとか言っていますね。
うーん、どういうことでしょう。
PhotonViewが必要なのです
ドキュメントを読んでみましょう。というか最初から読めばよかった……。
うん、PhotonView
がいるって書いてますね。
PhotonView
については同期処理を行う際に、詳しく調べてみます。
ではPhotonView
を先ほどのオブジェクトに追加して、もう一度試してみましょう。
プレイヤー1とプレイヤー2の両方のゲームにオブジェクトが生成出来ました。
これで今回のお題であるオブジェクトの生成ができました。
ここで筆をおいてもいいのですが、確かPhotonにはもう一つオブジェクト生成関数がありましたね。そちらも使ってみましょう。
PhotonNetwork.Instantiate()とPhotonNetwork.InstantiateSceneObject()
PhotonNetwork.InstantiateSceneObject()
を使ってオブジェクトを生成してみましょう。
PhotonNetwork.Instantiate()
と何が違うんでしょうか。
using UnityEngine; public class DemoNetwork : MonoBehaviour { [SerializeField] private string m_resourcePath = ""; [SerializeField] private float m_randomCircle = 4.0f; private const string ROOM_NAME = "RoomA"; void Start() { PhotonNetwork.ConnectUsingSettings( null ); } void OnJoinedLobby() { PhotonNetwork.JoinOrCreateRoom( ROOM_NAME, new RoomOptions(), TypedLobby.Default ); } public void SpawnObject() { PhotonNetwork.Instantiate( m_resourcePath, GetRandomPosition(), Quaternion.identity, 0 ); } public void SpawnSceneObject() { PhotonNetwork.InstantiateSceneObject( m_resourcePath, GetRandomPosition(), Quaternion.identity, 0, null ); } private Vector3 GetRandomPosition() { var rand = Random.insideUnitCircle * m_randomCircle; return rand; } } // class DemoNetwork
ふむ、何が違うのかがわからない!
ただ、部屋の主(MasterClient)であるPlayer1でないとPhotonNetwork.InstantiateSceneObject()
は機能しませんでした。
もう一度ドキュメントを読んでみましょう。
ネットワークオブジェクトのライフタイムの項目に以下のような記述がありました。
PhotonNetwork.Instantiateで生成されるゲームオブジェクトは、通常、同じルーム内にいる限り存在し続けます。
ルームを替えても、Unityのシーンを切り替えるときと同様、オブジェクトが移動することはありません。
クライアントがルームを離れるとき、プレイヤーが生成した所有オブジェクトは全て破棄されます。マスタークライアントは、PhotonNeworkInstantiateSceneObject()を用いて、ルームと同じライフタイムを持つゲームオブジェクトを生成することができます。
このオブジェクトはマスタークライアントではなく、ルームに結びつけられます。
デフォルトの設定では、マスタークライアントがこれらのオブジェクトを管理しますが、PhotonView.TransferOwnership()を用いてその権限を渡すことができます。
ふぅむ、PhotonNetwork.Instantiate()
は生成したプレイヤーに紐づけられ、PhotonNetwork.InstantiateSceneObject()
はルームに紐づけられるわけですか。
これを確認するために、プレイヤーを途中で退室させて生成したオブジェクトがどうなるのかを見てみましょう。
ただ、現在のプロジェクトでは、どのオブジェクトが誰の作ったオブジェクトなのかがわかりにくいため、コンポーネントを追加して視覚的にわかりやすくしましょう。
using UnityEngine; public class DemoObject : MonoBehaviour { private PhotonView m_photonView = null; private Renderer m_render = null; private readonly Color[] MATERIAL_COLORS = new Color[] { Color.white, Color.red, Color.green, Color.blue, Color.green, }; void Awake() { m_photonView = GetComponent<PhotonView>(); m_render = GetComponent<Renderer>(); } void Update() { int ownerID = m_photonView.ownerId; m_render.material.color = MATERIAL_COLORS[ ownerID ]; } } // class DemoObject
このコンポーネントを生成されるオブジェクトにアタッチすることで、生成者によって色が変わります。
ではこのオブジェクトを生成した後に、各プレイヤーが退室したらどうなるのかを見てみましょう。
Player2が退室する場合
Player1が退室する場合
PhotonNetwork.Instantiate()
で生成したオブジェクトは、生成したプレイヤーが退室したら消えますが、PhotonNetwork.InstantiateSceneObject()
で生成したオブジェクトは残り続けていますね。
このプレイヤーが退室した際にオブジェクトが消えるのを抑制したい場合は、PhotonNetwork.autoCleanUpPlayerObjects
をfalse
を設定してあげればいいみたいです。
ちゃんと各自で消さないとゴミが残ってしまうんですけどね。
次回は位置などの同期について調べていきます。