やりたいこと => PhotonObjectをスクリプトから生成する
PhotonObjectをスクリプトから生成するやり方、あまり記事がひっかかりません。
みんなPhotonNetwork.Instantiate()でResourcesから引っ張ってきてるんでしょうか?
とりあえず以下の先人の記事を読んでください。
Photon Unity Networkingで動的に同期オブジェクトを生成する方法
この記事はそれにPhotonAnimationViewを追加して使い回せるようそれっぽくメソッド化したものです。
無茶苦茶やってるので解説読んでから使ってください。
コード
public static GameObject DressAsPhotonObject(GameObject plainObject, int viewId, int ownerId)
{
// PhotonView.
PhotonView photonView = plainObject.AddComponent<PhotonView>();
photonView.viewID = viewId;
photonView.ownerId = ownerId;
photonView.synchronization = ViewSynchronization.UnreliableOnChange;
photonView.ObservedComponents = new List<Component>();
// Transform.
PhotonTransformView photonTransformView = plainObject.AddComponent<PhotonTransformView>();
photonTransformView.m_PositionModel.SynchronizeEnabled = true;
photonTransformView.m_RotationModel.SynchronizeEnabled = true;
photonTransformView.m_ScaleModel.SynchronizeEnabled = true;
// Animator.
PhotonAnimatorView photonAnimatorView = plainObject.AddComponent<PhotonAnimatorView>();
Animator animator = plainObject.GetComponent<Animator>();
// Animator layer
for (var count = 0; count < animator.layerCount; count++)
{
photonAnimatorView.SetLayerSynchronized(count, PhotonAnimatorView.SynchronizeType.Discrete);
}
// Animator Parameter
foreach (AnimatorControllerParameter animatorControllerParameter in animator.parameters)
{
photonAnimatorView.SetParameterSynchronized(animatorControllerParameter.name,
(PhotonAnimatorView.ParameterType) animatorControllerParameter.type,
PhotonAnimatorView.SynchronizeType.Discrete);
}
// add list
photonView.ObservedComponents.Add(photonTransformView);
photonView.ObservedComponents.Add(photonAnimatorView);
return plainObject;
}
こんな感じで使う
[SerializeField] private GameObject _prefab;
public enum RaiseEventType : byte
{
Create = 1,
}
void Start()
{
PhotonNetwork.OnEventCall += CreatePhoton;
var sendData = new int[2];
sendData[0] = PhotonNetwork.AllocateViewID();
sendData[1] = PhotonNetwork.player.ID;
var option = new RaiseEventOptions
{
CachingOption = EventCaching.AddToRoomCache,
Receivers = ReceiverGroup.All,
};
PhotonNetwork.RaiseEvent((byte) RaiseEventType.Create, sendData, true, option);
}
private void CreatePhoton(byte eventCode, object content, int senderId)
{
if (eventCode == (byte) RaiseEventType.Create)
{
var receiveData = content as int[];
var plainObjects = Instantiate(_prefab);
PhotonDresser.DressAsPhotonObject(plainObjects, receiveData[0], receiveData[1]);
}
}
解説
PhotonView
// まずは大本のPhotonView.
PhotonView photonView = plainObject.AddComponent<PhotonView>();
// PhotonNetwork.AllocateViewID()で用意してもらったviewID
photonView.viewID = viewId;
// とりあえず作ったplayerのIDぶちこんどく
photonView.ownerId = ownerId;
// デフォルトがoff.わたしはこれでしにました
photonView.synchronization = ViewSynchronization.UnreliableOnChange;
// newしとかないとしぬ
photonView.ObservedComponents = new List<Component>();
PhotonTransFormView
// Transform.
// インスペクタ上で見られる設定は対応する~Modelに設定できるメソッドやプロパティがある.
// 見ればわかるが各Modelクラスはただのデータクラス.あまりクセがないので特に言うことがない。
PhotonTransformView photonTransformView = plainObject.AddComponent<PhotonTransformView>();
photonTransformView.m_PositionModel.SynchronizeEnabled = true;
photonTransformView.m_RotationModel.SynchronizeEnabled = true;
photonTransformView.m_ScaleModel.SynchronizeEnabled = true;
PhotonAnimatorView
// Animator.
PhotonAnimatorView photonAnimatorView = plainObject.AddComponent<PhotonAnimatorView>();
Animator animator = plainObject.GetComponent<Animator>();
// 特定のレイヤーだけ同期する、みたいな用途はあまり考えづらいが……。普通は全部同期する気がする.
// もちろん実際に使う場合は「layerIndexはどうせ0,1,2...なんだろ? 死ねぇ!」とループでブチ回すのではなく、
// ちゃんとAnimator.GetLayerIndex(レイヤー名)で取るべき.
// しかしそうなるとこのメソッドの汎用性がなくなる…….
for (var count = 0; count < animator.layerCount; count++)
{
photonAnimatorView.SetLayerSynchronized(count, PhotonAnimatorView.SynchronizeType.Discrete);
}
// パラメータなんざ全部同期しときゃいいんだよ! オラァ!
foreach (AnimatorControllerParameter animatorControllerParameter in animator.parameters)
{
// AnimatorControllerParameterType = PhotonAnimatorView.ParameterType
// 別のenumだが両方 int enum で定義値が一緒なので直にキャストしても動く.
// 変更することがあるとも思えないのでそのまま使う.
// そのままAnimatorControllerParameterType使ったらよかったのでは?
photonAnimatorView.SetParameterSynchronized(animatorControllerParameter.name,
(PhotonAnimatorView.ParameterType) animatorControllerParameter.type,
PhotonAnimatorView.SynchronizeType.Discrete);
}
return
// PhotonViewに渡しとかないと同期されない.
photonView.ObservedComponents.Add(photonTransformView);
photonView.ObservedComponents.Add(photonAnimatorView);
// 余談だが動的に生成したPhotonObjectはリストとかに入れて管理するより
// タグつけて適宜GameObject.FindGameObjectsWithTag(TAG)で取得したほうがいいような気がする.
// リストに死んだの入ってたらめんどくさいし.
return plainObject;
結論
適宜よいように改変して使ってください。
Photonは公式ドキュメントが__絶妙にふわふわ__しているので、ある程度それらしいことを検索してわからなかったらコード読んだほうが早いです。PhotonViewとかPhotonNetworkとか。
また、こうして動的にPhotonObjectを生成するには各クライアントで実行する必要があります。[PunRPC]はあまり使わないでサンプルで書いたみたいにRaiseEventでやったほうがいいです。
慣れるとRaiseEventばっかり使うようになってきます。キャッシュ管理とかいろいろべんり。
CustomPropertiesにブチ込んでそのコールバックで……みたいな手もなくは……ないんですが……?
一応動いてはいるんですが、なんかやばそうな気がするのでやめたほうがいいです、多分。
参考資料
Photon Unity Networkingで動的に同期オブジェクトを生成する方法
Photonでオブジェクトをスポーンする方法の調査およびIPunPrefabPoolとUniRx.Toolkit.ObjectPoolを使ったオブジェクトプールのサンプル
【Unity】僕もPhotonを使いたい #10 RaiseEvent
ほんとうにいいたかったこと
あんちゃん! なんか2出てるけど何が変わったのかよくわからんから調べといて!