【第07日目】UnityとPUN2で手札と山札使用する配列の処理とマルチプレイヤー対応

 

こんにちは、今日は手札を使用した場合の手札配列の処理と、手札と場札の描画について考えようと思います。

手札のマテリアルインデックス値を使用して、

それと同じインデックスをもつ手札配列中の要素を取り除くという処理にしようかと思っています。

 

それでは、詳しく見ていきましょう。

UnityとPhotonで手札を使用する場合を配列で処理する

手札を使用した時というのは、ユーザーがマウスで手札をクリックやドラッグすることを考慮して、

カード自体にスクリプトを仕組む必要があります。

ただし、カードが手札にある場合のみ有効になるようにしたいので、タグを使用して、タグが”Hands"である場合のみ処理を呼ぶようにします。

手札の配列はHandHoldernに格納されているので、GameObject.Find("HandHolder")でオブジェクトを取得してからGetComponent<HandHolder>();でスクリプトにアクセスします。

これで例えばマテリアルインデックスが2のカードを使用した場合、Indexが2の配列要素を一つ削除すればよいという事になります。

使用した手札は場札になるので、タグを”Card”につけ直します。(そうしないと描画の際に消去されてしまうので)

場札はテーブルの上に置けるようにしたいので、RigidBodyを戻してあげて、回転をフリーズ、表向きに表示してあげるように180度ひっくり返します。

これで、カードを使用する事ができるようになりました。

カードにアタッチしたスクリプトは以下です。

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HandRemover : MonoBehaviour
{
    void OnMouseDrag()
    {
        if (tag == "Hands")
        {
            //HandHolderの取得
            GameObject handHolderObject = GameObject.Find("HandHolder");
            HandHolder handHolder = handHolderObject.GetComponent<HandHolder>();

            //CardSwitcherのIndexの取得
            CardSwitcher cardSwitcher = GetComponent<CardSwitcher>();
            var removeNum = cardSwitcher.cardIndex;

            //Indexと同じものをHandHolderの手札配列から一つ削除
            var list = new List<int>();
            list.AddRange(handHolder.hands);
            list.Remove(removeNum);
            handHolder.hands = list.ToArray();

            //手札タグをカードタグに付け替える(場に戻すため)
            tag = "Card";

            //rigidbodyを付け、ひっくり返す、
            Rigidbody rigidbody = gameObject.AddComponent<Rigidbody>();
            rigidbody.transform.rotation = Quaternion.Euler(0, 0, 180);
            rigidbody.transform.position = new Vector3(5, 5, 11);

            //回転を拘束
            rigidbody.constraints = RigidbodyConstraints.FreezeRotationX;
            rigidbody.constraints = RigidbodyConstraints.FreezeRotationY;
            rigidbody.constraints = RigidbodyConstraints.FreezeRotationZ;
        }
           
    } 
}

 

UnityとPhotonで山札を配列で処理する

山札も手札と同様に配列で処理しようと思います。

残り枚数と山札の実際のサイズが同じになり、かつ、枚数が表示できるようにしたいと思います。

山札の配列の制御

libraryという配列を作り、その中に、インスペクター上で予め山札に欲しいカードを入れておきます。

カードを一枚インスタンス化する時にランダムで要素を一つ選び、それを消去してあげます。

引いたカードにはその要素のインデックスを渡し、正しいマテリアルをレンダーします。

 

それと同時に、山札の縦サイズをカード一枚分縮小します。

最後の一枚が引かれて、配列の数が0になったら山札を破壊します。

 

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

public class CardDraw : MonoBehaviour
{
    public int[] library;

    [SerializeField] Transform spawnPoint;

    CardSwitcher cardSwitcher;
    public void click()
    {
        //カードをインスタンス化する
        GameObject cardCopy = (GameObject)PhotonNetwork.Instantiate("NewCard", spawnPoint.position, spawnPoint.rotation, 0);

        //山札の配列からランダムで一つ要素のインデックスを選ぶ
        int randomDraw = Random.Range(0, library.Length);

        //選んだ山札カードのマテリアルをあてがう
        cardSwitcher = cardCopy.GetComponent<CardSwitcher>();
        cardSwitcher.SwitchMaterial(library[randomDraw]);

        //山札配列からその要素を一つ削除
        var list = new List<int>();
        list.AddRange(library);
        list.Remove(library[randomDraw]);
        library = list.ToArray();

        //山札のサイズを変化させる
        if (library.Length == 0)
        {
             Destroy(gameObject);
        }
        else
        {
            transform.localScale = new Vector3(1, library.Length, 1);
        }
       
    }
}

 

UnityのTextMeshPro3dによる山札の枚数の表示

山札の枚数が分かりにくいので、上にTextMeshProで枚数を動的に表示することにしました。

ここのサイトを参考に作成しています。

 

初めてTextMeshProを使うと関連ファイルのインポートを求められるのでインポートします。

まず、using TMPro;が必要です。

TextMeshPro3Dの場合TextMeshProを宣言して、インスペクターでドラッグアンドドロップしておきます。

後は下に抜粋した通りにxxxxxx.textに代入すればOKです。

using TMPro;

public class CardDraw : MonoBehaviour
{
    
    [SerializeField] int[] library;
    [SerializeField] TextMeshPro libraryNum;

    private void Start()
    {
        libraryNum.text = System.Convert.ToString(library.Length);
    }
    public void click()
    {
        libraryNum.text = System.Convert.ToString(library.Length);
    }

 

現状のビルドをマルチプレイヤーに対応する

Collisionを検知してカメラを切り替えるこのSphereなのですが、これが悪さをしていまして、マルチプレイヤーでやってみると、

別のプレイヤーのカメラにすり替わったりして、色々と面倒が起こりました。

カメラは常に一つしかオンにならないようにする必要があるので、PhotonViewを付けてIsMineでなければ破壊してしまうようにしました。

それだけでは、逆側のSphereが対戦相手と衝突するとカメラがきり替わるので、ついでに、逆側のSphereも破壊してしまうようにしました。

 

要するに、互い違いにスクリプトを付けてどちらかが衝突したら逆側が破壊されるようにしています。

そもそもSphereが自分のSphereが2つ、対戦相手のSphereが2つの計4つあるので、それのうちのどれか一つしかトリガーしないようにする必要がありました。

ややこしい話ですね。

Shere01には下のスクリプトを付けました。

using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class NewCameraCollision : MonoBehaviour
{
    [SerializeField] GameObject Camera01;
    [SerializeField] GameObject Camera02;
    [SerializeField] GameObject position02;
    //OnCollisionEnter()

    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(position02);
            
            collision.gameObject.SetActive(false);

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

        }
    }
}

 

Shere02には下のスクリプトを付けました。

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;
    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);
        }
    
    }
}

そして、お互いをインスペクター上でドラッグアンドドロップして監視しています。

これによってカメラのスイッチの問題は解決しました。

しかし、これだけでは十分ではないのですね。

カードの表示と同期について、多々問題があり、それはおいおい解決していこうと思います。

おわりに

画像だけ見るとうまくいってそうに見えますが、これは1人プレイの状態なので、逆のプレイヤーから見ると全く違う風景が実は見えています。

カードの表示が例えばインデックスが対戦相手に伝わってないために正しく表示されない、

ポジションをロックしたカードがリジッドボディのせいで落ちる、

などなど、色々な問題が発生しています。

今後の課題として明日以降対応していこうと思います。

それではまた!

 

おすすめの記事