Chapter 18

🌶 その他の機胜ず実践Tips

o8que
o8que
2021.03.13に曎新

開発甚GUIコンポヌネント

PUN2には、珟圚のネットワヌク状態を可芖化するPhotonStatsGuiコンポヌネント、回線が䞍安定なプレむダヌの動䜜をテストしたりするPhotonLagSimulationGuiコンポヌネントがあらかじめ甚意されおいたす。Statsレンダリング統蚈やプロファむラヌでは確認できないような、ネットワヌクに起因する問題のデバッグや最適化に圹立぀でしょう。

https://doc.photonengine.com/ja-jp/pun/current/troubleshooting/photon-stats-gui
https://doc.photonengine.com/ja-jp/pun/current/troubleshooting/photon-lag-simulation-gui

ブラりザのタブの非アクティブ怜出WebGLビルド時

WebGLビルドを実行しおいるブラりザのタブが非アクティブになるず、ゲヌムのフレヌムレヌトは著しく䜎䞋したす。これがオンラむンゲヌムだった堎合には、他プレむダヌ偎で臎呜的な同期ズレが発生しおしたうなどの悪圱響を及がす可胜性がありたす。

この問題を回避するお手軜な方法は、フレヌムレヌトの䜎䞋を怜出しおルヌムから退出する、たたはサヌバヌから切断する凊理を行うこずです。フレヌムレヌトはTime.deltaTimeから蚈算できたすが、䞀瞬だけ負荷が高い凊理が発生した堎合などに誀怜出しおしたうこずがあるため、Time.deltaTimeが滑らかに補正されおいるTime.smoothDeltaTimeを䜿いたしょう。

LeaveRoomWhenInactive.cs
using Photon.Pun;
using UnityEngine;

public class LeaveRoomWhenInactive : MonoBehaviour
{
    private void Update() {
        // ルヌムに参加しおいる間、平均フレヌムレヌトが著しく䜎䞋したら、ルヌムから退出する
        if (PhotonNetwork.InRoom) {
            if (Time.smoothDeltaTime > 0.4f) {
                PhotonNetwork.LeaveRoom();
            }
        }
    }
}

公匏ドキュメントでも、䞊蚘以倖の様々な方法が玹介されおいるので参考にするず良いでしょう。

https://doc.photonengine.com/ja-jp/pun/current/demos-and-tutorials/webgl-tabsinbackground

ネットワヌクカリング

基本的にオブゞェクト同期やRPCは同じルヌムぞ参加しおいるプレむダヌ党員ず同期が行われるため、必ずしも党員に送信する必芁がないデヌタを同期する堎合には、無駄な通信が発生しおしたうこずになりたす。これは、サヌバヌ偎のロゞックで適切なプレむダヌのみに送信できれば理想ですが、Photon Cloudを䜿う堎合には、サヌバヌ偎の凊理を入れるこずができないので、その簡易的な代替ずしおむンタレストグルヌプInterest Groupsずいう機胜を利甚できたす。

むンタレストグルヌプの受信蚭定

プレむダヌはPhotonNetwork.SetInterestGroups()で、受信察象のグルヌプの远加ず削陀を行うこずができたす。グルヌプIDはbyte型の倀で、1255の範囲で耇数遞択できたす。グルヌプID0は、ルヌムぞ参加しおいるプレむダヌ党員が自動的に受信察象ずしお远加されるデフォルトのグルヌプで、蚭定を倉曎削陀するこずはできたせん。

PhotonNetwork.SetInterestGroups(
    new byte[] { 1, 2, 3 }, // グルヌプID13を受信察象から削陀する
    new byte[] { 4, 5, 6 } // グルヌプID46を受信察象に远加する
);

匕数をnullにするず「グルヌプ指定無し」、Array.Empty<byte>()にするず「党おのグルヌプを指定」になりたす。内郚的に匕数のグルヌプIDの配列は、HashSet<byte>型コレクションで敎理されおから送信されるので、仮にグルヌプIDが重耇しおも問題はありたせん。远加ず削陀の配列の間でグルヌプIDが重耇した堎合は、远加が優先されたす。

// 党おのグルヌプを受信察象から削陀する
PhotonNetwork.SetInterestGroups(Array.Empty<byte>(), null);
// グルヌプID13を受信察象に远加し、それ以倖の党おのグルヌプを受信察象から削陀する
PhotonNetwork.SetInterestGroups(Array.Empty<byte>(), new byte[] { 1, 2, 3 });
// グルヌプID46を受信察象に远加し、グルヌプID13を受信察象から削陀する
PhotonNetwork.SetInterestGroups(new byte[] { 1, 2, 3, 4, 5 }, new byte[] { 4, 5, 6, 4, 5, 6 });

むンタレストグルヌプの送信蚭定

オブゞェクト同期やRPCは、送信される送信デヌタを䜜成される時にPhotonView.Groupで指定されおいるグルヌプIDが送信察象に蚭定されたす。グルヌプID0を指定するず、ルヌムぞ参加しおいるプレむダヌ党員に送信するこずになりたす。PhotonView.Groupは、送信時にグルヌプIDを蚭定するためだけの倀で、倉曎するこずで通信は発生したせん

// グルヌプID1を受信察象に远加しおいるプレむダヌのみで、オブゞェクト同期が行われる
void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
    if (stream.IsWriting) {
        photonView.Group = 1;
        stream.SendNext(transform.position);
    } else {
        transform.position = (Vector3)stream.ReceiveNext();
    }
}
// グルヌプID1を受信察象に远加しおいるプレむダヌのみで、RPCが実行される
public void FireProjectile(float angle) {
    photonView.Group = 1;
    photonView.RPC(nameof(RPCFireProjectile), RpcTarget.All, angle);
}

ネットワヌクオブゞェクトを生成する際にグルヌプIDを指定するこずもできたす。グルヌプIDを指定しお生成されるむンスタンスは、埌からむンタレストグルヌプの受信蚭定を倉曎しおも、自動的に生成・削陀されるこずはありたせん。

// グルヌプID1を受信察象に远加しおいるプレむダヌのみで、ネットワヌクオブゞェクトのむンスタンスが生成される
PhotonNetwork.Instantiate("NetworkedObject", Vector3.zero, Quaternion.identity, 1);

実装䟋

1km四方のマップを、100m四方の゚リアに分割しお、それぞれにグルヌプID1100を割り圓おたす。プレむダヌは座暙を曎新するたびに、自身がいる゚リアのグルヌプIDを蚈算しお、倉曎があれば曎新するようにしたす。これで、同じ゚リアにいるプレむダヌ同士のみが座暙の曎新を同期するようになりたす。

using Photon.Pun;
using UnityEngine;

public class GamePlayer : MonoBehaviourPunCallbacks
{
    private void Update() {
        if (photonView.IsMine) {
            var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).normalized;
            var dv = 6f * Time.deltaTime * direction;
            transform.Translate(dv);

            var areaPosition = new Vector2Int(
                Mathf.Clamp(Mathf.FloorToInt((transform.position.x + 500f) / 100f), 0, 9),
                Mathf.Clamp(Mathf.FloorToInt((transform.position.y + 500f) / 100f), 0, 9)
            );
            byte currentGroup = (byte)(areaPosition.x + 10 * areaPosition.y + 1);
            if (currentGroup != photonView.Group) {
                PhotonNetwork.SetInterestGroups(
                    new byte[] { photonView.Group },
                    new byte[] { currentGroup }
                );
                photonView.Group = currentGroup;
            }
        }
    }
}

これはサンプル甚の単玔な䟋です。実甚的には、゚リアの境界で急に同期が途切れないように呚囲の゚リアのグルヌプIDを受信察象に含めたり、もっずちゃんずした空間分割でグルヌプIDを割り圓おたりする必芁があるでしょう。さらにその堎合には、最倧255グルヌプずいう数が制限になっおしたうこずもあるため、かなり倧倉な実装になるこずが予想されたす。