こんにちは、今日は、山札のカードに何が含まれているかの配列を対戦相手と共有することを考えます。
現時点では、オーナーのオブジェクトの山札の配列のみ書き換えられる仕様となっているため、
その情報に他のプレイヤーがアクセスできないようになっています。
上図の通り、カードを引いた状態でも、初期状態の9枚が表示されている状況です。
これを、共有するために色々試行錯誤をしようと思っています。
目次
UnityとPUN2とC#で山札の配列を対戦相手と共有する
まずは情報の同期が出来れば良いと思ったので、カードのcardIndexの同期の時と同様の処理で書けないかと考えました。
IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)を使って試してみました。
PUN2のOnPhotonSerializeViewを利用して配列を送ろうとする
これが見事にうまくいきませんでした。
Photonでサポートされている型には、int配列が含まれていないようなので、そのせいかもしれません。
以下のコードをくっつけて、 MonoBehaviourPunCallbacks, IPunObservableを継承しています。
void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.IsWriting) { stream.SendNext(library); //libraryの情報を送る Debug.Log(library); } else { library = (int[])stream.ReceiveNext(); //libraryの情報を受信 } }
PUN2のOnPhotonSerializeViewについて色々と調べてみる
Photon(PUN 2)で任意データの同期処理を書くとか、
Photon PUN2のOnPhotonSerializeView()で送受信できる型一覧とか、
【Unity】僕もPhotonを使いたい #09 RPC() シリアライズ編を確認すると、
OnPhotonSerializeViewでの配列の共有は結構ややこしいという事がわかってきました。
そこで、カスタムプロパティを使ってはどうかと思うようになりました。
PUN2のカスタムプロパティについて色々と調べてみる
🔄 同期3 : カスタムプロパティによると、カスタムプロパティを使えばルームに特有のデータを扱えそうです。
オーナー側の配列を取得して、一度カスタムプロパティに配列の各要素をすべて渡して、オーナーとクライアントのlibraryを初期化後、カスタムプロパティから要素を受け取るという流れです。
void SendLibraryData() { var hashtable = new ExitGames.Client.Photon.Hashtable(); for (int i = 0; i < library.Length; i++) { hashtable["item" + i] = library[i]; } PhotonNetwork.LocalPlayer.SetCustomProperties(hashtable); } public override void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged) { library = new int[0]; foreach (var prop in propertiesThatChanged) { List<int> list = new List<int>(library); list.Add(library[(int)prop.Value]); library = list.ToArray(); } if (library.Length == 0) { Destroy(gameObject); } else { GameObject libraryMesh = GameObject.Find("card"); libraryMesh.transform.localScale = new Vector3(1, library.Length, 1); } }
これを実行してみると、見事に動きません。
デバッグログを仕込んでみると、OnRoomPropertiesUpdateが呼ばれていないという事が分かります。
色々調べたり試行錯誤してみたりしたのですが、やっとできました。
PUN2のOnRoomPropertiesUpdateは PhotonNetwork.CurrentRoom.SetCustomPropertiesで呼ぶ
OnRoomPropertiesUpdateが呼ばれていないという事ですが、
もしかして、PhotonNetwork.LocalPlayer.SetCustomProperties(hashtable);のLocalPlayerをCurrentRoomに変えたらできるのでは?
と思い、やってみたら、呼ばれるようになりました。
void SendLibraryData() { var hashtable = new ExitGames.Client.Photon.Hashtable(); for (int i = 0; i < library.Length; i++) { hashtable["item" + i] = library[i]; } PhotonNetwork.CurrentRoom.SetCustomProperties(hashtable); } public override void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged) { library = new int[0]; foreach (var prop in propertiesThatChanged) { List<int> list = new List<int>(library); list.Add((int)prop.Value); library = list.ToArray(); } if (library.Length == 0) { Destroy(gameObject); } else { GameObject libraryMesh = GameObject.Find("card"); libraryMesh.transform.localScale = new Vector3(1, library.Length, 1); } }
これでコード自体は正しく動くようになったのですが、全てのライブラリに対して、同じ値を与えてしまうので、うまくないという事に気づきました。
そもそも山札(ライブラリ)には違うデッキが入る事が想定されるので、分ける必要があるとは思ってはいたのですが、、、
なので、この部分は分離する必要がありそうな気がします。
UnityとPUN2とC#で同じ山札の配列だけ対戦相手と共有する準備
そこで、プレファブを1と2に分けて、別々の配列を入れる事を想定し、コードを書き直そうと思いました。
その前にやっておくべきこととして、山札の上に浮いている数字の表記は、プレイヤーに対して正対して、かつ正しく表示される必要があります。
まず、それを片付けてから、プレファブの分離を行いたいと思います。
TextMeshProの表示を変えるタイミングをOnRoomPropetiesUpdatesにする
まずは、ドローする際に呼んでいたTextMeshProの表示を変えるタイミングをカスタムプロパティの更新時に変えたいので、
OnRoomPropertiesUpdateの中で呼ぶことにしました。
これで、とりあえず、全ての数字が同じになるようになりました。
public override void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged) { library = new int[0]; foreach (var prop in propertiesThatChanged) { List<int> list = new List<int>(library); list.Add((int)prop.Value); library = list.ToArray(); } if (library.Length == 0) { Destroy(gameObject); } else { GameObject libraryMesh = GameObject.Find("card"); libraryMesh.transform.localScale = new Vector3(1, library.Length, 1); //これを今回追記 libraryNum.text = System.Convert.ToString(library.Length); } }
TextMeshProの表示を自分のオブジェクトではない場合裏返す
表示を一度だけひっくり返したいので、ひっくり返ったかどうかを試すbool値を作っておいて、それがfalseからtrueになったらもうひっくり返さないことにします。
TextMeshProの親オブジェクトについているPhotonViewを参照するのでGetComponentInParentを使っています。
あとは、IsMineの値false(自分のオブジェクトでない)場合のみ現在のポジションから180回転でひっくり返すことにします。
このスクリプトをTextMeshProにつけておきます。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class FlipText : MonoBehaviour { bool isFlipped = false; PhotonView PV; private void Awake() { PV = GetComponentInParent<PhotonView>(); } private void Update() { if(!isFlipped && !PV.IsMine) { float angle = transform.rotation.y; transform.rotation = Quaternion.Euler(0, angle +180, 0); isFlipped = true; } } }
これでは結果的にうまくいきませんでした。
親のトランスフォームを取得したり、カメラのトランスフォームを取得したり色々しましたが、撃沈。
有識者に相談したところ、Transform.LookAt(hoge)でhogeを向くというものがあるという事が分かりそれを使うことにしました。
LookAtを使ってカメラのトランスフォームの方向を向かせる
先ほどからごちゃごちゃ小手先で方向を修正しようとしていましたが、そもそもカメラを向くという機能が備わっているという事が分かりました。
できる人に聞くというのは凄く大切だなと思いました。
たった二行で済んでしまいました。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class FlipText : MonoBehaviour { private void Update() { Transform TF = Camera.main.GetComponent<Transform>(); transform.LookAt(TF); } }
ただ、これをTextMeshProに直接つけると全部が裏返ってしまったので、親オブジェクトをかませて180度回転してます。
なので、このスクリプトは親オブジェクトに付いています。
おわりに
今日はライブラリの分離まで行きたかったのですが、
それ以前の段階で割と詰まってしまったので、長くなってしまいました。
とりあえず、カスタムプロパティを使った同期と、TextMeshProの方向の修正ができたという事になります。
今後ライブラリを分離して別々のデッキを組めるようにしたいと思います。
それでは、また!