オーナーシップとコントロール
PUNでは、ネットワーク化されたオブジェクトはPhotonView
コンポーネントを用いて確立されます。
それぞれの PhotonView
には、作成者(インスタンス化)、オーナー、コントローラがあります。
このドキュメントでは、PhotonView
の制御と所有権に関する定義と概念について説明します。
また、さまざまな状況で予想される動作と、PhotonView
の所有権を明示的に変更する方法をリストアップします。
定義
アクター
アクターはルームのクライアントを表します。
各アクター/クライアントには、ルームに参加する際に新しいIDがインクリメンタルに割り当てられます。
最初にルームに参加したアクターはアクター1、2番目はアクター2となります。
アクターはメッセージのターゲット、PhotonViewsのオーナー/コントローラーとなり、マスタークライアントとして割り当てられます。
Creator、Owner、Controllerはすべて俳優を指しています。
PhotonView Creator
PhotonView
または'instantiator' (または'spawner' )の作成者は、PhotonNetwork.Instantiate
を呼び出すアクターです。
このアクター番号は PhotonView.ViewID
の一部であり、ViewID / PhotonNetwork.MAX_VIEW_IDS を用いて決定されます。
PhotonView
のViewIDは変更されないので、作成者のIDも変更されません。
ネットワーク化されたルームのオブジェクトの場合、作成者がいないので(null/0)、ネットワーク化されたオブジェクトはアクターによって作成されず、代わりにルームに関連付けられます。
PhotonView Owner
PhotonView
のオーナーは、PhotonView
のデフォルトコントローラーを示します。
ネットワーク化されたルームのオブジェクトの場合、オブジェクトのオーナーはルームであってアクターではないので、オーナー(null)は存在しません。
オーナーがいる場合、後者が有効であればコントローラにもなります。
それ以外の場合は、マスタークライアントが制御します。
PhotonView Controller
PhotonView
を制御するアクター(状態の権限を持つ)。
次の場合以外、オーナーは常にコントローラーです。
- オーナーがnullの場合は。マスタークライアントがコントローラーになります。
- オーナーがルームから切断された場合。
PlayerTTLが0より大きい場合、ハード切断が発生する前にソフト切断としてアクターが退室する可能性があります。
オーナーがソフト切断されている間は、マスタークライアントがコントローラになり、オーナーが再参加すると、オーナーが制御を再開します。
photonView.IsMineはphotonNetwork.LocalPlayerがこのphotonView
の現在のコントローラであるかどうかを示します。
ネットワーク化されたオブジェクト
ネットワーク化されたオブジェクトとは、PhotonView
コンポーネントやその子を持つGameObjectのことです。
ネットワーク化されたオブジェクトは、そのルートの PhotonView
コンポーネント、つまりトップレベル(ルート)のGameObjectにアタッチされた PhotonView
に表されます。
ネストされたネットワーク化オブジェクト
ネットワーク化オブジェクトの階層内に子の¥GameObject があり、そのうちの 1 つ以上に PhotonView
がアタッチされている場合、それはネストしたネットワーク化オブジェクトとみなされます。
つまり、ネストされたネットワーク化オブジェクトは、別のネットワーク化オブジェクトの階層の一部のネットワークオブジェクトのことです。
通常、ネストされたネットワーク化オブジェクトを持つネットワーク化オブジェクトをインスタンス化するとき、すべての PhotonView
は同じインスタンスID (ルートの PhotonView
のViewID) を共有し、所有者やコントローラが異なるか、再ペアレント化されていない限り、同じライフタイムを持ちます。
ルームオブジェクト
ネットワーク化されたルームオブジェクトとは、アクターには属さないが、そのルームに属する「グローバルネットワーク化されたオブジェクト」です。
これはオーナー(null)もコントローラー(null)もなく、PhotonNetwork.Instantiate
呼び出しの結果でもありません(ただし、PhotonNetwork.InstantiateSceneObject
の結果である可能性はあります)。
シーンオブジェクト
シーンオブジェクトは PhotonNetwork.InstantiateSceneObject
を使ってランタイムインスタンス化されなかったルームオブジェクトです。
コンパイル時のUnityシーンの一部でした。
ソフト切断
ソフト切断とは、アクターがルームの中で無効になることです。
PlayerTTLが0と異なる場合と、以下の場合に発生します。
- クライアントが切断される。
- クライアントは、戻ってくるつもりで一時的にルームを出ます。
プレイヤーはPlayerTTLが期限切れになるまで非アクティブのままです。
PlayerTTL < 0
またはPlayerTTL == int.MaxValue
の場合、アクターは永遠にアクティブのままでいられます。
予期せぬソフト切断から回復するには、PhotonNetwork.ReconnectAndRejoin()
を使用するか、PhotonNetwork.RejoinRoom
を使用して同じルームに再接続します。
どちらの場合も、クライアントは同じ UserId を保持する必要があり、アクターは再入室時に同じアクター番号と以前のすべてのアクターのプロパティを再取得します。
ハード切断
ハード切断とは、あるアクターがルームのアクターリストから完全に削除された場合のことです。
PhotonNetwork.CurrentRoom.PlayerTtl == 0
の場合。
- クライアントがPhotonサーバーから切断。
- クライアントがルームから退室。
PhotonNetwork.CurrentRoom.PlayerTtl != 0
の場合:
- クライアントが完全にルームから退室
- 非アクティブなアクターのPlayerTTLはが期限切れになる
自動コントロール移行
ソフト切断時
ハード切断したローカルクライアント上で:
ルームが
roomOptions.CleanupCacheOnLeave == true
で作成された場合:ランタイムにインスタンス化されたネットワークオブジェクトからシーンオブジェクトの親を解除します(破壊を防ぐため)。
このアクターによって作成されなかったネスティングされたネットワークエンティティは、破棄する前にオブジェクトの親ではなくなります。
切断するアクターによって作成された、実行時にインスタンス化されたネットワークオブジェクトはすべて破棄されます。
その他のネットワーク接続されたオブジェクトはデフォルトにリセットされます。
If the room was created with
roomOptions.CleanupCacheOnLeave == false
:- 何も変わりません。手作業でクリーンアップする必要があります。
リモートクライアントの場合(ある場合)。
- ソフト切断したアクターが以前に所有していたPhotonViewの所有権は変更されません。
- マスタークライアントは、ソフト切断したアクターが所有する
PhotonView
sのコントローラになります。
ハード切断時
ハードディスコネクトされたローカルクライアント上:
ルームが
roomOptions.CleanupCacheOnLeave == true
で作成された場合:- 切断するアクターのインスタンス化されたネットワークシーンオブジェクトの親を解除する(破壊を防ぐために)
- 切断されたアクターによって作成された、ランタイムにインスタンス化されたネットワークオブジェクトは破棄されます。
- キャッシュされたインスタンス化イベントやバッファされたRPCは中継サーバから削除されます。
- シーンオブジェクトがリセットされます。
ルームが
roomOptions.CleanupCacheOnLeave == false
で作成された場合:- 何も変わりません。手作業でクリーンアップする必要があります。
リモートクライアント上(ある場合):
- 切断するアクターのネスティングされたネットワークシーンオブジェクトの親を解除する(破壊を防ぐために)
- 切断されたアクターによって作成された、ランタイムにインスタンス化されたネットワークオブジェクトは破棄されます。
- ハード切断されたアクターが以前に所有していた残りの
PhotonView
の所有権をリセットします(所有者がnullになります)。
それらは親のないネットワークオブジェクトになります。 - オーナーがnullの場合、マスタークライアントが常にコントローラであるため、マスタークライアントはこれらの親のない
PhotonView
のコントローラになります。
再入室の場合
Player.HasRejoined == true
クライアントが空室ではないルームに再入室した場合。
- 同じアクターが所有するネットワーク化されたオブジェクトの制御を再取得します。
- [任意] マスタークライアントは、owner != creatorのネットワークオブジェクトに対してOwnershipUpdate を再送します。
サーバー上にあるが空のルームにクライアントが再参加した場合(EmptyRoomTTLの有効期限がまだ切れていない):
- 同じアクターが所有するネットワーク化されたオブジェクトの制御を再取得します。
- マスタークライアントなので、他のすべてのネットワーク上のオブジェクトも制御します。
クライアントがルームを「復活」させて再入室した場合(外部ソースから状態を再読み込み):
- 同じアクターが所有するネットワーク化されたオブジェクトの制御を再取得します。
- マスタークライアントなので、他のすべてのネットワーク上のオブジェクトも制御します。
新規入室の場合
Player.HasRejoined == false
クライアントが空室ではないルームに再入室した場合。
- 通常通り
サーバー上にあるが空のルームにクライアントが再参加した場合(EmptyRoomTTLの有効期限がまだ切れていない):
- 同じアクターが所有するネットワーク化されたオブジェクトの制御を再取得します。
- マスタークライアントなので、他のすべてのネットワーク上のオブジェクトも制御します。
クライアントがルームを「復活」させて再入室した場合(外部ソースから状態を再読み込み):
- 同じアクターが所有するネットワーク化されたオブジェクトの制御を再取得します。
- マスタークライアントなので、他のすべてのネットワーク上のオブジェクトも制御します。
マスタークライアント変更の場合
新しいマスターが以下のコントローラーになります:
- "孤児" ネットワーク化されたオブジェクト:オーナーのいないネットワーク化されたオブジェクト。
- 所有者が非アクティブなネットワーク化されたオブジェクト。
注: 前のマスターがソフト切断した場合、所有権を主張したネットワークルームオブジェクトの所有権を保持します。
明示的なオーナーシップの移転
ネットワークオブジェクトの所有者を明示的に変更するには、それぞれのルートPhotonView
を使用します。
デフォルトでは、ネットワーク化されたオブジェクトの所有者は固定されていますが、これを変更することで、直接またはリクエストで所有者を変更することができます。
所有権の変更は通常、コントローラ(制御者)の変更を意味し、新しい所有者が非アクティブでない限り、その所有者がネットワークオブジェクトを制御します。
所有権移転オプション
PhotonView
の所有権移転の動作は、PhotonView.OwnershipTransfer
で設定されたOwnershipOption
で定義されます。
PhotonView.OwnershipTransfer
はネットワーク上では同期されないので、PhotonView
がインスタンス化されたら変更しないでください。
所有権移転オプションにはFixed
, Request
, Takeover
の3つのタイプがあります。
それぞれについて個別に説明しましょう。
Fixed
所有権は固定されています。
ルームオブジェクトの場合、所有者はありませんが、マスタークライアントがコントローラーとなります。
プレイヤーオブジェクトの場合は、作成者が常に所有者となります。
これがデフォルト値です。
Request
OwnershipTransfer
オプションが Request
に設定されている場合、アクターは現在の所有者(またはコントローラー)に PhotonView
の所有権を要求することができます。
これは2つのステップで行われます。最初にアクターがPhotonView
の所有者にリクエストを送信し、後者がそれを受け入れれば所有権の移転がオーナーによって明示的に行われ、リクエストしたアクターが新しい所有者になります。
リクエストはPhotonView.RequestOwnership()
を経由して行われます。
これは現在の所有者に対してIPunOwnershipCallbacks.OnOwnershipRequest(PhotonView targetView, Player requestingPlayer)
コールバックを発生させ、開発者は実際の所有権の変更を行うためにtargetView.TransferOwnership(requestingPlayer)
を呼び出す必要があります。これにより、開発者はコードの中でそのリクエストが受け入れられるかどうかを判断することができます。
ネットワークオブジェクトの現在の所有者か現在のコントローラーのみが所有権の移転要求を受け付けることができます。
Takeover
アクターはTakeover
をOwnershipTransfer
オプションとして指定したPhotonView
の所有権を変更することができます。
このオプションは、現在の所有者の同意なしにPhotonView
の所有権を直接主張したり、PhotonView
を他の誰かに帰属させたりすることを目的としています。
この場合、所有権を引き継ぐにはPhotonView.TransferOwnership(Player newOwner)
を呼び出すだけです。
アクターXはTakeover
に設定されたPhotonView
の所有者をアクターYからアクターZに変更することができます。
OwnershipTransfer
オプションにTakeover
を指定したPhotonView
に対してPhotonView.RequestOwnership()
をコールすると、コールバック処理を行わなくても自動的にリクエストが受け付けられます(他の誰かが引き継がない限り)。
しかし、Takeover
のオーナーシップオプションの場合は、PhotonView.TransferOwnership(Player newOwner)
を直接呼び出すことを推奨します。
所有権の放棄
PhotonView
の所有権が固定されていて変更されない限り、どのアクターも自分のPhotonView
の所有権を他のアクティブなアクターに譲渡することができます。
これは PhotonView.TransferOwnership(Player newOwner)
を使って行います。
PhotonView コールバック
所有権変更コールバック
PhotonView
の所有者が変わるたびに、それを実装したクラスと同じ PhotonView
に登録されているクラスでIonPhotonViewOwnerChange.OnOwnerChange(Player newOwner, Player previousOwner)
が発生します。
IOnPhotonViewOwnerChange
インターフェースを実装しているクラスは、PhotonView.AddCallbackTarget
で登録し、PhotonView.RemoveCallbackTarget
で登録を解除します。
明示的な所有権移転コールバック
同じIPunOwnershipCallbacks
インターフェースに2つの所有権変更コールバックがあります。
- 誰かがTargetViewに所有権を要求した時の
OnOwnershipRequest(PhotonView targetView, Player requestingPlayer)
。 - TargetViewの所有者が変わる際の
OnOwnershipTransfered(PhotonView targetView, Player previousOwner)
。
IPunOwnershipCallbacks
インターフェースを実装するクラスは、PhotonNetwork.AddCallbackTarget
で登録し、PhotonNetwork.RemoveCallbackTarget
で登録を解除する必要があります。
制御変更コールバック
PhotonView
のコントローラーが変更されるたびに、IonPhotonViewControllerChange.OnControllerChange(Player newController, Player newController)
がそれを実装したクラスと同じPhotonView
に登録されているクラスに対して発行されます。
IonPhotonViewControllerChange
インターフェースを実装したクラスはPhotonView.AddCallbackTarget
で登録し、PhotonView.RemoveCallbackTarget
で登録を解除する必要があります。
ネットワーク破棄コールバック
ネットワーク接続されたオブジェクトが破壊されそうになったときに通知を希望する場合もあります。
このために使用できるコールバックがあり、同じネットワークオブジェクトのすべての PhotonView
(メイン/ルートのPhotonView
とそれにネストされているものがあればすべて)に対しても発生します。
PhotonNetwork.Destroy
が呼ばれるたびに、ネットワークの破壊が終わる直前にIonPhotonViewPreNetDestroy.OnPreNetDestroy(PhotonView rootView)
が呼び出され、それを実装したクラスと同じPhotonViewに登録されているクラスに対して IonPhotonViewPreNetDestroy.OnPreNetDestroy(PhotonView rootView)
が発生します。
IOnPhotonViewPreNetDestroy
インターフェースを実装するクラスは PhotonView.AddCallbackTarget
で登録し、PhotonView.RemoveCallbackTarget
で登録を解除する必要があります。