Chapter 12

🌶 コールバックインターフェース

o8que
o8que
2021.04.17に更新

PUNのコールバックを受け取れるようにするためにはMonoBehaviourPunCallbacksを継承する方法が最も簡単ですが、MonoBehaviourを継承しないクラスなどではこの方法は使えません。任意のクラスでPUNのコールバックを受け取れるようにしたい場合は、PUN2で用意されているコールバックインターフェースを実装することになります。

https://doc.photonengine.com/ja-jp/pun/current/getting-started/dotnet-callbacks

MonoBehaviourPunCallbacksのコードを読む

MonoBehaviourPunCallbacksがどのような実装になっているかを確認してみましょう。

以下のコードは、上記のファイルからMonoBehaviourPunCallbacksに関連する部分を抜粋、簡略化してコメントを追加したものです。これらの実装を補うことで、どんなクラスでもPUNのコールバックを受け取れるようにすることができます。

public class MonoBehaviourPun : MonoBehaviour
{
    // photonViewプロパティの追加
    public PhotonView photonView { get; }
}
public class MonoBehaviourPunCallbacks : MonoBehaviourPun,
    IConnectionCallbacks,
    IMatchmakingCallbacks,
    IInRoomCallbacks,
    ILobbyCallbacks,
    IWebRpcCallback,
    IErrorInfoCallback
{
    public virtual void OnEnable() {
        // PUNのコールバック対象に登録する
        PhotonNetwork.AddCallbackTarget(this);
    }

    public virtual void OnDisable() {
        // PUNのコールバック対象の登録を解除する
        PhotonNetwork.RemoveCallbackTarget(this);
    }

    // 各コールバックインターフェースの空実装
    public virtual void OnConnected() {}
    public virtual void OnLeftRoom() {}
    public virtual void OnMasterClientSwitched(Player newMasterClient) {}
    public virtual void OnCreateRoomFailed(short returnCode, string message) {}
    public virtual void OnJoinRoomFailed(short returnCode, string message) {}
    public virtual void OnCreatedRoom() {}
    public virtual void OnJoinedLobby() {}
    public virtual void OnLeftLobby() {}
    public virtual void OnDisconnected(DisconnectCause cause) {}
    public virtual void OnRegionListReceived(RegionHandler regionHandler) {}
    public virtual void OnRoomListUpdate(List<RoomInfo> roomList) {}
    public virtual void OnJoinedRoom() {}
    public virtual void OnPlayerEnteredRoom(Player newPlayer) {}
    public virtual void OnPlayerLeftRoom(Player otherPlayer) {}
    public virtual void OnJoinRandomFailed(short returnCode, string message) {}
    public virtual void OnConnectedToMaster() {}
    public virtual void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) {}
    public virtual void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) {}
    public virtual void OnFriendListUpdate(List<FriendInfo> friendList) {}
    public virtual void OnCustomAuthenticationResponse(Dictionary<string, object> data) {}
    public virtual void OnCustomAuthenticationFailed(string debugMessage) {}
    public virtual void OnWebRpcResponse(OperationResponse response) {}
    public virtual void OnLobbyStatisticsUpdate(List<TypedLobbyInfo> lobbyStatistics) {}
    public virtual void OnErrorInfo(ErrorInfo errorInfo) {}
}

コールバックインターフェースの実装

サンプルプロジェクト(🎮)のSampleSceneスクリプトで、MonoBehaviourPunCallbacksを継承せずにコールバックインターフェースを実装してみましょう。ここで重要なポイントは以下の2点です。

  1. SampleSceneに必要なコールバックインターフェースを実装する
  2. SampleSceneをPUNのコールバック対象に登録する


SampleScene.cs
 using System.Collections.Generic;
 using Photon.Pun;
 using Photon.Realtime;
 using UnityEngine;

-public class SampleScene : MonoBehaviourPunCallbacks
+public class SampleScene : MonoBehaviour, IConnectionCallbacks, IMatchmakingCallbacks
 {
+    private void OnEnable() {
+        // PUNのコールバック対象に登録する
+        PhotonNetwork.AddCallbackTarget(this);
+    }

+    private void OnDisable() {
+        // PUNのコールバック対象の登録を解除する
+        PhotonNetwork.RemoveCallbackTarget(this);
+    }

     private void Start() {
         PhotonNetwork.NickName = "Player";
         PhotonNetwork.ConnectUsingSettings();
     }

-    public override void OnConnectedToMaster() {
+    void IConnectionCallbacks.OnConnectedToMaster() {
         PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
     }

-    public override void OnJoinedRoom() {
+    void IMatchmakingCallbacks.OnJoinedRoom() {
         var position = new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f));
         PhotonNetwork.Instantiate("Avatar", position, Quaternion.identity);

         if (PhotonNetwork.IsMasterClient) {
             PhotonNetwork.CurrentRoom.SetStartTime(PhotonNetwork.ServerTimestamp);
         }
     }

+    void IConnectionCallbacks.OnConnected() {}
+    void IConnectionCallbacks.OnDisconnected(DisconnectCause cause) {}
+    void IConnectionCallbacks.OnRegionListReceived(RegionHandler regionHandler) {}
+    void IConnectionCallbacks.OnCustomAuthenticationResponse(Dictionary<string, object> data) {}
+    void IConnectionCallbacks.OnCustomAuthenticationFailed(string debugMessage) {}

+    void IMatchmakingCallbacks.OnFriendListUpdate(List<FriendInfo> friendList) {}
+    void IMatchmakingCallbacks.OnCreatedRoom() {}
+    void IMatchmakingCallbacks.OnCreateRoomFailed(short returnCode, string message) {}
+    void IMatchmakingCallbacks.OnJoinRoomFailed(short returnCode, string message) {}
+    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message) {}
+    void IMatchmakingCallbacks.OnLeftRoom() {}
 }

PUNのコールバックは、用途に応じて複数のインターフェースに定義が分けられているので、必要なインターフェースを選択して実装するようにしましょう。

名前 主なコールバック 説明
IConnectionCallbacks OnConnectedToMaster()、OnDisconnected() など 接続関連のコールバック
IMatchmakingCallbacks OnCreatedRoom()、OnJoinedRoom()、OnLeftRoom() など マッチメイキング関連のコールバック
ILobbyCallbacks OnJoinedLobby()、OnLeftLobby()、OnRoomListUpdate() など ロビー関連のコールバック
IInRoomCallbacks OnPlayerEnterdRoom()、OnRoomPropertiesUpdate() など ルーム関連のコールバック

特別なコールバックインターフェース

MonoBehaviourPunCallbacksには実装されていないPUNのコールバックもいくつか存在します。これらを使用する場合には、かならずコールバックインターフェースを実装する必要があります。

ネットワークオブジェクトが生成された時のコールバック

ネットワークオブジェクトのスクリプトにIPunInstantiateMagicCallbackインターフェースを実装すると、ネットワークオブジェクトが生成された時のコールバックを受け取ることができます。このインターフェースに限り、コールバック対象の登録をしなくても動作することに注意してください。

using Photon.Pun;
using UnityEngine;

public class InstantiateCallbackSample : MonoBehaviour, IPunInstantiateMagicCallback
{
    // ネットワークオブジェクトが生成された時に呼ばれるコールバック
    void IPunInstantiateMagicCallback.OnPhotonInstantiate(PhotonMessageInfo info) {
        if (info.Sender.IsLocal) {
            Debug.Log("自身がネットワークオブジェクトを生成しました");
        } else {
            Debug.Log("他プレイヤーがネットワークオブジェクトを生成しました");
        }
    }
}

ネットワークオブジェクトが破棄される直前のコールバック

ネットワークオブジェクトのスクリプトにIOnPhotonViewPreNetDestroyインターフェースを実装すると、ネットワークオブジェクトが破棄される直前のコールバックを受け取ることができます。スクリプトは、PhotonNetwork.AddCallbackTarget()ではなくphotonView.AddCallbackTarget()で、PhotonViewのコールバック対象に登録します。

using Photon.Pun;
using UnityEngine;

public class DestroyCallbacksSample : MonoBehaviourPun, IOnPhotonViewPreNetDestroy
{
    private void OnEnable() {
        // PhotonViewのコールバック対象に登録する
        photonView.AddCallbackTarget(this);
    }

    private void OnDisable() {
        // PhotonViewのコールバック対象の登録を解除する
        photonView.RemoveCallbackTarget(this);
    }

    // ネットワークオブジェクトが破棄される直前に呼ばれるコールバック
    void IOnPhotonViewPreNetDestroy.OnPreNetDestroy(PhotonView rootView) {
        Debug.Log($"{rootView.name}({rootView.ViewID}) が破棄されます");
    }
}

ネットワークオブジェクトの所有者が変更された時のコールバック

ネットワークオブジェクトのスクリプトに、IOnPhotonViewOwnerChangeインターフェースを実装するとネットワークオブジェクトの所有者が変更された時のコールバックを、IOnPhotonViewControllerChangeインターフェースを実装するとネットワークオブジェクトの管理者が変更された時のコールバックを受け取ることができます。こちらもPhotonViewのコールバック対象に登録しましょう。

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

public class OwnerChangeCallbacksSample : MonoBehaviourPun, IOnPhotonViewOwnerChange, IOnPhotonViewControllerChange
{
    private void OnEnable() {
        // PhotonViewのコールバック対象に登録する
        photonView.AddCallbackTarget(this);
    }

    private void OnDisable() {
        // PhotonViewのコールバック対象の登録を解除する
        photonView.RemoveCallbackTarget(this);
    }

    // ネットワークオブジェクトの所有者が変更された時に呼ばれるコールバック
    void IOnPhotonViewOwnerChange.OnOwnerChange(Player newOwner, Player previousOwner) {
        string objectName = $"{photonView.name}({photonView.ViewID})";
        string oldName = previousOwner.NickName;
        string newName = newOwner.NickName;
        Debug.Log($"{objectName} の所有者が {oldName} から {newName} に変更されました");
    }

    // ネットワークオブジェクトの管理者が変更された時に呼ばれるコールバック
    void IOnPhotonViewControllerChange.OnControllerChange(Player newController, Player previousController) {
        string objectName = $"{photonView.name}({photonView.ViewID})";
        string oldName = previousController.NickName;
        string newName = newController.NickName;
        Debug.Log($"{objectName} の管理者が {oldName} から {newName} に変更されました");
    }
}