【第08日目】UnityとPUN2でカードの同期と表示のマルチプレイヤー対応

こんにちは。今日はPhotonマルチプレイでのカードの同期と表示が正しく行われるようにしたいと思います。

現状のビルドには以下の問題があります。

 

1.手札が落ちる。

2.引いたカードが場に残ってしまう。

3.対戦相手のカードのマテリアルがデフォルトの状態になってしまう。

4.位置の同期がされない。

 

これを一つずつ潰していこうと思います。

その後で、山札をマルチプレイヤーで処理することを考えたいと思います。

それから、ドローする時のOnMouseDragが検知できない時があり、

それの対応もしたので、それについても多少触れようかと思います。

 

では、まずデバッグから。

UnityとPhotonでカードの同期と表示のマルチプレイヤー対応

1.手札が落ちる

まずは、手札が落ちるという現象があります。

これは手札がインスタンス化した状態で、デフォルトでrigidbodyが付いているために重力で落ちています。

 

デフォルトの状態ではrigidbodyを外してしまい、スクリプトで付けるようにしようと思います。

具体的にはインスタンス化した時に下記のスクリプトを追加してあげるという事になります。

 

//rigidbody有効化
       Rigidbody rigidbody = cardCopy.AddComponent<Rigidbody>();
       rigidbody.transform.rotation = Quaternion.Euler(0, 0, 0);
       rigidbody.constraints = RigidbodyConstraints.FreezeRotation;

2.引いたカードが場に残ってしまう

これは簡単な事でした。Destroy()を使っていたのですが、これでは、オーナー側のカードしか破壊されず、

対戦相手に描画されているカードが破壊されていなかったのです。

これはPhotonNetwork.Destroy()に書き換える事で解決しました。

3.対戦相手のカードのマテリアルがデフォルトの状態になってしまう

オーナーの場札はきちんとマテリアルが表示されているのに、対戦相手側のカードは表示がデフォルトのままになってしまっています。

恐らくこれは、マテリアルインデックスが、対戦相手にわたっていないという事が原因にあるような気がしています。

そこで、PUN2に詳しいここのサイトを参考にしてスクリプトを追加してみました。

 

手を加えたのはカードについている、マテリアルをスイッチするCardSwitcherです。

同期の処理と、マテリアルの反映の一文を入れています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class CardSwitcher : MonoBehaviourPunCallbacks, IPunObservable
{
    public int cardIndex;


    [SerializeField] Material[] CardMat;
    public void SwitchMaterial(int x )
    {
        cardIndex = x;
        gameObject.GetComponent<MeshRenderer>().material = CardMat[cardIndex];
    }

    //ここから同期の処理
    void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
          if (stream.IsWriting)
        {
            stream.SendNext(cardIndex); //cardIndexの情報を送る
        }
        else
        {
            cardIndex = (int)stream.ReceiveNext(); //cardIndexの情報を受信
            SwitchMaterial(cardIndex);//materialに反映
        } 
    }
}

 

これによってマテリアルの反映がされるようになりました。

UnityとPhotonで対面マルチプレイヤー対応の山札とカードの取得

今のところ、1人のプレイヤーしかカードを引くことを想定していません。

しかし、TCGなどのゲームではお互いの山札が存在し、自分の山札からカードを引くのが通例です。

それを実装するために、山札をプレファブ化して、自分の山札と、相手の山札を別々に扱う必要がでてきます。

 

山札からカードを引くポジションを相対的にする

現在はカードがスポーンされる場所をCubeのトランスフォームで与えてあげていますが、山札に対して相対的にした方が汎用性が高くなります。

よって、まずは、山札の左側にカードが配られるようにしたいと思います。

必要な部分だけ抜粋すると、以下のように書きました。

    Vector3 cardSpawnPoint;

    private void Awake()
    {
        //現在のポジションをposに格納
        Transform pos = transform;

        //posのpositionをフロートで取得
        float x = pos.transform.position.x;
        float y = pos.transform.position.y;
        float z = pos.transform.position.z;

        //取得したフロートに位置をずらす分足す
        cardSpawnPoint = new Vector3(x, y + 0.02f, z - 0.1f);
    }

    public void click()
    {
        //カードをインスタンス化する
        GameObject cardCopy = (GameObject)PhotonNetwork.Instantiate("NewCard", cardSpawnPoint, Quaternion.Euler(0, 0, 0), 0);
  }

 

これで、ライブラリをプレファブ化してもカードが配られる位置は相対的になるようになりました。

山札をプレファブ化してSphereに衝突判定した際にインスタンス化する

これは、文字通り、山札をヒエラルキービューからプレファブ化してリソースフォルダにコピー。

Sphereについているスクリプトにインスタンス化の一文を追記。

それから、山札の位置にキューブを置いて非表示にして、Transformだけインスペクター上でドラッグアンドドロップしておきます。

下にSphere02についているスクリプトを貼っておきます。libraryPositionというのがそのキューブです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class NewCameraCollision2 : MonoBehaviour
{
    [SerializeField] GameObject Camera01;
    [SerializeField] GameObject Camera02;
    [SerializeField] GameObject position01;
    [SerializeField] Transform libraryPosition;
    PhotonView PV;
   
    private void Awake()
    {
        PV = GetComponent<PhotonView>();
    }

    private void Start()
    {
        if (!PV.IsMine)
        {
            Destroy(gameObject);
        }

    }
    private void OnCollisionEnter(Collision collision)
    {
        Debug.Log("collision");

        if (collision.gameObject.tag == "Player")
        {
            Destroy(position01);

            collision.gameObject.SetActive(false);

            Camera02.SetActive(true);
            Camera01.SetActive(false);

            GameObject library = (GameObject)PhotonNetwork.Instantiate("Library", libraryPosition.position , libraryPosition.rotation, 0);
        }
    }
}

Sphere01 とSphere02では、入っているTransformやカメラが逆転しています。

手札ホルダーを対戦相手の分も用意する

最後に、手札を宙に浮かすホルダーを対戦相手の分も用意します。ドローの時のコライダーはSphereの中に含める事で、

同じ名前のオブジェクトとしてGameObject.Findで取得できるようにしました。

その他、細かい調整がありましたが、主なものはこのくらいです。

これで、対面でカードをライブラリから引いたり、場に出したりすることが出来ました。

ドローがうまくできない問題点の解消

結論から言うと、コライダーが被ってただけなのですが、気づくのにかなりの時間を要したので、

一応ここに解決のプロセスを残しておきます。

 

引いたカードがつかめないエラーが出ていて、何に起因するものか分かりませんでした。

有識者に相談したところ、まず、OnMouseDragが呼ばれているかDebug.Logで確認するとのことでしたので、

確認したところ、つかめない時は呼ばれていないという事が判明しました。

 

という事は、コライダーが被っているか、何かに遮蔽されている可能性を指摘されたので、

Window>Analysis>Physics Debuggerというものがある事を教えて頂き、カメラアングルと合わせてみてみました。

すると、手札のスポナーが丁度被っていまして、しかもコライダーが付いているという事が判明。

そのコライダーを無効化することでエラーの解消が出来ました。

こういったエラーはなかなか気づくことが難しいので、後々のために書き残しておきます。

おわりに

今回はほぼ、バグ潰しでした。

やはり、対面マルチプレイヤーは頭を使いますね。

思っても見ないことが問題になったり、何故か解決したりします。

 

なんとか、カードを引いて使う、という事が対面でできるようになったので、

これから、ゲームの仕様を順次反映させていければいいなと思います。

 

それでは、また!

 

おすすめの記事