LoginSignup
5
2

More than 1 year has passed since last update.

Photon PUN2で動的にネットワークオブジェクトを生成する

Posted at

PUN2(Photon Unity Networking 2)とは

Unityでサーバー構築などなしに、マルチプレイを実現できるアセットです。
詳しくは公式ドキュメントや、PUN2(Photon Unity Networking 2)で始めるオンラインゲーム開発入門をご覧ください。

開発環境

  • Windows 11
  • Unity 2020.3.21f
  • PUN 2 - FREE v2.40
    (その他)
  • MRTK 2.7.3

通常のネットワークオブジェクトの生成

通常は表示するモデルのPrefabにPhotonViewをアタッチしておき、これを下記のように生成するのが一般的です。

PhotonNetwork.Instantiate("MyPrefabName", new Vector3(0, 0, 0), Quaternion.identity, 0);

しかしこの方法だとPrefabで使用していたモデルを変更したり、新規にモデルを追加する時に、毎回PhotonViewを追加しなくてはいけません。
これが煩わしいので今回は動的にネットワークオブジェクトを生成します。

動的にネットワークスオブジェクトを生成

public class ModelCreator : MonoBehaviour, IOnEventCallback
{
    private const byte CustomInstantiateEventCode = 1;

    [SerializeField]
    private GameObject prefab; // PhotonViewをアタッチしていないPrefab

    [SerializeField]
    public Camera camera;

    [SerializeField]
    public float offset = 2f;

    private void OnEnable()
    {
        PhotonNetwork.AddCallbackTarget(this);
    }

    private void OnDisable()
    {
        PhotonNetwork.RemoveCallbackTarget(this);
    }

    public void CreateModel()
    {
        CreateModel(prefab);
    }

    private GameObject CreateModel(GameObject prefab)
    {
        var obj = Instantiate(prefab, camera.transform.position + camera.transform.forward * offset, Quaternion.identity);

        // MRTK の設定(Prefabを操作できるようにする)
        var objManipulator = obj.AddComponent<ObjectManipulator>();
        objManipulator.TwoHandedManipulationType = TransformFlags.Move | TransformFlags.Rotate;
        obj.AddComponent<NearInteractionGrabbable>();

        // Photon
        var photonView = obj.AddComponent<PhotonView>();
        var photonTransformView = obj.AddComponent<PhotonTransformView>();

        // 初期化を行う
        photonView.ObservedComponents = new List<Component>();

        // Synchronizeするものを設定
        photonTransformView.m_SynchronizePosition = true;
        photonTransformView.m_SynchronizeRotation = true;
        photonTransformView.m_SynchronizeScale = false;
        photonTransformView.m_UseLocal = true;

        // PhotonViewに紐付ける
        photonView.ObservedComponents.Add(photonTransformView);

        // 同期にはViewIDが必要なため取得する
        if (PhotonNetwork.AllocateViewID(photonView))
        {
            // PrefabのtransformとViewIDを通知する準備をする
            var data = new object[]
            {
                obj.transform.position,
                obj.transform.rotation,
                photonView.ViewID
            };

            // 同じRoomの自分以外に通知
            var raiseEventOptions = new RaiseEventOptions
            {
                Receivers = ReceiverGroup.Others,
                CachingOption = EventCaching.AddToRoomCache
            };

            var sendOptions = new SendOptions
            {
                Reliability = true
            };

            // 同じRoom内の他のユーザーへ通知
            PhotonNetwork.RaiseEvent(CustomInstantiateEventCode, data, raiseEventOptions, sendOptions);
        }
        else
        {
            Debug.LogError("Failed to allocate a ViewId");
            Destroy(obj);
        }
    }

    // RaiseEventを受け取る
    public void OnEvent(EventData photonEvent)
    {
        var eventCode = photonEvent.Code;

        if (eventCode != CustomInstantiateEventCode)
        {
            return;
        }

        var data = (object[])photonEvent.CustomData;

        // 受信したtransformを設定
        var obj = Instantiate(prefab, (Vector3)data[0], (Quaternion)data[1]);

        // MRTK の設定(Prefabを操作できるようにする)
        var objManipulator = obj.AddComponent<ObjectManipulator>();
        objManipulator.TwoHandedManipulationType = TransformFlags.Move | TransformFlags.Rotate;
        obj.AddComponent<NearInteractionGrabbable>();

        // Photon
        var photonView = obj.AddComponent<PhotonView>();
        var photonTransformView = obj.AddComponent<PhotonTransformView>();

        // 初期化を行う
        photonView.ObservedComponents = new List<Component>();

        // Synchronizeするものを設定
        photonTransformView.m_SynchronizePosition = true;
        photonTransformView.m_SynchronizeRotation = true;
        photonTransformView.m_SynchronizeScale = false;
        photonTransformView.m_UseLocal = true;

        // PhotonViewに紐付ける
        photonView.ObservedComponents.Add(photonTransformView);

        // 受信したViewIDを用いて同期する
        photonView.ViewID = (int)data[2];
    }
}

ポイント

// 初期化を行う
photonView.ObservedComponents = new List<Component>();

// PhotonViewに紐付ける
photonView.ObservedComponents.Add(photonTransformView);

PhotonView.ObservedComponentsを初期化してから、PhotonTransformViewをAddしないと追加されません(追加されませんでした)。

// 同期にはViewIDが必要なため取得する
if (PhotonNetwork.AllocateViewID(photonView))

同期にはViewIDが大事な要素になるため、ViewIDを作成します。

// PrefabのtransformとViewIDを通知する準備をする
var data = new object[]
{
    obj.transform.position,
    obj.transform.rotation,
    photonView.ViewID
};

// 同じRoom内の他のユーザーへ通知
PhotonNetwork.RaiseEvent(CustomInstantiateEventCode, data, raiseEventOptions, sendOptions);

ViewIDをPhotonNetwork.RaiseEventを用いて、同じRoom内の他のユーザーへ通知を行います。

// 受信したViewIDを用いて同期する
photonView.ViewID = (int)data[2];

OnEventで受信したViewIDを設定します。
これで同じViewIDを持つオブジェクト同士が同期されます。

実行

HoloLens2とUnityのEditorで同期した時の動画です。
HoloLens2側でオブジェクトを表示して、動かしているところを、UnityのEditorから見ている動画です。

参考サイト

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2