Photon からの移行ガイド

Photon(PUN2)で作ったマルチプレイを GS2-Realtime の sync / stateMachine へ移す手順と対応表

このページは、Photon PUN2 で作ったマルチプレイを、GS2-Realtime のオブジェクト同期(sync / stateMachine)へ移すための手順と対応表です。各 API の詳細は Unity クライアントを参照してください。

考え方の違い

観点Photon PUN2GS2-Realtime
ルームを探すPhoton のロビーとマッチメイキングGS2-Matchmaking でメンバーを集め、GS2-Realtime のルームを作る
ルームへの接続PhotonNetwork.ConnectUsingSettings / JoinRoom発行された IP アドレス・ポート・暗号鍵へ RealtimeSession で接続する
誰が値を決めるか各クライアント(マスタークライアントがまとめ役)ルームのサーバーが、書き込みの権限と RPC の許可を検証する。stateMachine では、ゲームの進行もサーバーのルールが決める
同期するデータOnPhotonSerializeView で自由なデータを送るオブジェクトごとに最大 32 個の整数フィールド。位置・回転は刻み幅に丸めて送る
通信UDP(reliable / unreliable)WebSocket(確実に届けるもの)と UDP(定期的な状態の更新)
課金同時接続数ルームのサーバーの起動時間と通信量

いちばん大きな違いは、ルームのサーバーが権限を検証できることです。ほかのプレイヤーのオブジェクトの値は書き換えられません。さらにオブジェクトカタログを設定すると、登録した Prefab と RPC だけを通すように検査させられます。マスタークライアントに任せていたゲームの進行(敵の HP、得点、勝敗)は、stateMachine のルールに移すと、クライアントの改造の影響を受けなくなります。

対応表

コンポーネント

Photon PUN2GS2-Realtime
PhotonViewNetworkEntity
PhotonTransformViewNetworkTransform
IPunObservable / OnPhotonSerializeViewINetworkObservable(OnNetworkWrite / OnNetworkRead)
PhotonNetwork.PrefabPool / Resources の PrefabPrefab Catalog と NetworkWorld
シーンに置いた PhotonView、InstantiateRoomObjectルームが作る共有オブジェクト(stateMachine のルール、または操作権を貸し出すオブジェクト)

オブジェクト

Photon PUN2GS2-Realtime
PhotonNetwork.Instantiate(name, pos, rot)world.Instantiate(prefabId, pos, rot)
PhotonNetwork.Destroy(go)entity.Destroy()
photonView.IsMineentity.IsMine
photonView.Ownerentity.OwnerId(接続 ID)
photonView.TransferOwnership(player)entity.TransferOwnership(connectionId)
photonView.RequestOwnership()(OwnershipOption.Takeover)entity.RequestOwnership()(オブジェクトカタログを設定する場合は "ownership": "takeover")
PhotonNetwork.CleanupCacheOnLeave = falseカタログで "keepOnLeave": true(マスタークライアントへ引き継ぐ)
photonView.Group / PhotonNetwork.SetInterestGroupsentity.SetGroup / world.SetInterestGroups、SubscribeGroup

RPC とイベント

Photon PUN2GS2-Realtime
[PunRPC][NetworkRpc]
photonView.RPC(name, RpcTarget.All, args)entity.Rpc(name, RpcTarget.All, args)
RpcTarget.Others / AllBuffered / OthersBuffered同名の RpcTarget
RpcTarget.AllViaServerRpcTarget.All(常にサーバーを経由する)
RpcTarget.MasterCliententity.RpcTo(name, world.MasterClientId, args)
photonView.RPC(name, player, args)entity.RpcTo(name, connectionId, args)
PhotonMessageInfoNetworkRpcInfo(Sender は呼び出し元の接続 ID)
PhotonNetwork.RaiseEventworld.RaiseEvent / RaiseEventTo(ハンドラーは [NetworkEvent])
IOnEventCallback.OnEventworld.AddEventTarget で登録した [NetworkEvent] メソッド、または world.EventReceived

ルームとプレイヤー

Photon PUN2GS2-Realtime
PhotonNetwork.LocalPlayer.ActorNumbersession.MyConnectionId
PhotonNetwork.PlayerList、OnPlayerEnteredRoom / OnPlayerLeftRoomRealtimeSession の OnJoinPlayer / OnLeavePlayer
PhotonNetwork.MasterClient / IsMasterClientworld.MasterClientId / IsMasterClient
PhotonNetwork.SetMasterClient / OnMasterClientSwitchedworld.SetMasterClient / MasterClientChanged
Room.SetCustomProperties / OnRoomPropertiesUpdateworld.SetRoomProperty / RoomPropertyChanged
Player.SetCustomProperties / OnPlayerPropertiesUpdateworld.SetPlayerProperty / PlayerPropertyChanged
PhotonNetwork.Timeworld.ServerTime
PhotonNetwork.SendRate / SerializationRateworld.SendRate
unreliable な送信RealtimeUdpChannel(定期的な状態の更新を UDP で送る)

移行の手順

  1. ルームの準備を置き換える: Photon のロビーとマッチメイキングを、GS2-Matchmaking と GS2-Realtime のルームの作成に置き換えます。Realtime のネームスペースは serverType を sync(ゲームの進行もサーバーで決めるなら stateMachine)にします。流れは GS2-Realtime の紹介とチュートリアルを参照してください。
  2. 接続を置き換える: RealtimeSession で接続し、NetworkWorld.Attach と world.Receive をつなぎます。ルームに接続するのとおりに書きます。
  3. Prefab を置き換える: PhotonView を NetworkEntity に、PhotonTransformView を NetworkTransform にします。Prefab Catalog を作り、Prefab ID を割り当てて登録します。
  4. 同期する値を置き換える: OnPhotonSerializeView で送っていた値は、同期スキーマのフィールドにして INetworkObservable で読み書きします。値は 32 個までの整数(float は刻み幅に丸める)なので、文字列や配列は RPC かカスタムプロパティで送ります。
  5. RPC を置き換える: [PunRPC] を [NetworkRpc] に、photonView.RPC を entity.Rpc にします。ほかのプレイヤーのオブジェクトに対して呼ぶ RPC には AnyCaller = true を付けます。引数の型が使える型に入っているかを確かめます。
  6. 生成と削除を置き換える: PhotonNetwork.Instantiate を world.Instantiate に、PhotonNetwork.Destroy を entity.Destroy() にします。シーンに置いていたオブジェクトは、ルームが作る共有オブジェクトにします。
  7. 検査したい場合はオブジェクトカタログを設定する: 生成や RPC をルームに検査させたい場合は、Prefab Catalog のアセットを右クリックし、Copy Object Catalog JSON で生成した JSON を、Realtime のネームスペースの syncSetting.objectCatalog に設定します。RPC の名前や Prefab を変えたら、生成し直します。
  8. イベント・プロパティ・マスタークライアントを置き換える: 対応表のとおりに置き換えます。
  9. UDP を使う: WebGL 以外では、RealtimeUdpChannel で定期的な状態の更新を UDP で送ります。

コードの書き換え例

RPC

// Photon PUN2
[PunRPC]
void Fire(Vector3 direction, PhotonMessageInfo info) { /* ... */ }
photonView.RPC(nameof(Fire), RpcTarget.Others, direction);

// GS2-Realtime
[NetworkRpc]
void Fire(Vector3 direction, NetworkRpcInfo info) { /* ... */ }
entity.Rpc(nameof(Fire), RpcTarget.Others, direction);

生成と所有者の判定

// Photon PUN2
var player = PhotonNetwork.Instantiate("Player", spawnPoint, Quaternion.identity);
if (player.GetComponent<PhotonView>().IsMine) { /* 入力を有効にする */ }

// GS2-Realtime(Prefab ID 1 を Prefab Catalog に登録しておく)
var player = world.Instantiate(1, spawnPoint, Quaternion.identity);
if (player.IsMine) { /* 入力を有効にする */ }

入力を受け付けるスクリプトは、NetworkEntity の Controlled Behaviours に登録すると、書き込める間だけ自動で有効になります。

値の同期

// Photon PUN2
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
    if (stream.IsWriting) stream.SendNext(health);
    else health = (float)stream.ReceiveNext();
}

// GS2-Realtime(同期スキーマに float の Health フィールドを宣言しておく)
public void OnNetworkWrite(NetworkEntity entity) => entity.SetFloat("Health", health);
public void OnNetworkRead(NetworkEntity entity, uint changed)
{
    if (entity.HasChanged("Health", changed)) health = entity.GetFloat("Health");
}

イベントとプロパティ

// Photon PUN2
PhotonNetwork.RaiseEvent(GoalCode, team, new RaiseEventOptions { Receivers = ReceiverGroup.All }, SendOptions.SendReliable);
PhotonNetwork.CurrentRoom.SetCustomProperties(new Hashtable { { "stage", "forest" } });

// GS2-Realtime
world.RaiseEvent("Goal", RpcTarget.All, team);   // [NetworkEvent] void Goal(int team) を登録しておく
world.SetRoomProperty("stage", "forest");

注意すること

  • 確定は非同期: world.Instantiate はすぐに使えますが、ルームが拒否すると SpawnFailed が呼ばれて削除されます。カスタムプロパティは、ルームを経由して全員に届いた時点で反映されます。
  • 値は丸められる: 位置は 0.01、回転は 360/4096 度の刻み幅で送ります。精度を変えるには、同期スキーマで指定します。
  • 権限はサーバーが検証する: 所有者以外の書き込みは拒否されます。オブジェクトカタログを設定すると、登録していない Prefab と RPC、許可していない所有権の取得も拒否されます。
  • RPC の引数の上限: 合計 4096 バイトまでです。大きなデータは分けて送ります。
  • 送信の順序: UDP で送る定期的な更新と、WebSocket で送る RPC やイベントの間の順序は保証しません。RPC の直前に書き換えた値は、RPC より先に届きます。
  • ルームの終了条件: 参加者全員から 1 分間通信がないとルームは終了します。ルームの終了条件との関係を参照してください。
  • WebGL: UDP を使えないので、すべて WebSocket で送ります。
  • Photon の別製品: Photon Voice や Photon Chat に当たる機能は、GS2-Realtime には含まれません。テキストチャットには GS2-Chat を使えます。