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から見ている動画です。