こんにちは、今回はマナトークンの管理と消費をtagと配列とisMineの組み合わせで処理します。
基本原則としては、以下です。
カードを使用するためにはマナトークンを消費します。
マナトークンが無い場合カードは使用できません。
マナトークンを得るためにはトークン皿からトークンを取る必要があります。
一個トークンを取ったら、山札の上にトークンが一個発生します。
発生したトークンは山札からカードをドローした際に相手のマナトークンになります。
結構複雑ですね、これのうちトークンが発生するところまでを今回は実装したいと思います。
では、まずはtagの整理から参りましょう。
目次
UnityとPhotonとC#でマナトークンを使用してカードを一枚使う
図のように、皿からマナトークンを取って、ライブラリ(山札)の上にトークンが置かれた状況で、皿の上のトークンと、この2つのトークンをバラバラに扱う必要があります。
よって、まずは、タグを新たに2つ追加します。
tagを使い分けて自分のマナトークンと相手に渡るマナトークンを分ける
皿に盛ってあるトークンを”Mana”とします。
”Mana"がColliderから出た時に(OnTriggerExit)”Cost"とというタグに付け替えます。
一方で、山札の上に出てくるトークンのタグは”PreMana”という名前に付け替えます。
これはManaPicker.csを下記のように書き換えて実装します。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class ManaPicker : MonoBehaviour { [SerializeField] Transform manaSpawnPoint; // Start is called before the first frame update private void OnTriggerExit(Collider other) { Debug.Log("collision"); if (other.gameObject.tag == "Mana") { other.tag = "Cost"; GameObject mana = (GameObject)PhotonNetwork.Instantiate("Mana", manaSpawnPoint.position, manaSpawnPoint.rotation, 0); mana.tag = "PreMana"; } } }
これで、あとはインスペクター上でタグを増やしてあげればOKです。
自分のマナトークンを一個消費して手札を使う
まず、手札のカードを使うための条件として一個自分のマナトークンがあるという事が上げられるので、それをif文にします。
ただし、カウントするのは自分のマナトークンだけなので、Tagで拾うと相手のもカウントしてしまいます。
よって、まずはTagで全てのマナトークンを取得して、次にそのPhotonViewを取得して、それがIsMineがTrueの時のみ、新たな配列に加えます。
そして、その新たな配列が自分のマナトークンの配列になるので、それのLengthでif文を作成すれば、正しく手札を使う条件分岐ができます。
カードを使用したら、一つ自分のマナトークン配列から一番目の要素を破壊します。
それを手札のHandRemover.csに反映すると下記のようになります。
ハンドの処理は今回の実装部分以外変えてないので、まとめてメソッドにしてしまっています。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class HandRemover : MonoBehaviour { //自分のマナトークン配列 public GameObject[] myCost; void OnMouseDrag() { //手札の時にしかこの処理を行わない if (tag == "Hands") { //"Cost"タグが付いているオブジェクトを配列で全取得 GameObject[] costs = GameObject.FindGameObjectsWithTag("Cost"); //配列の回数分下記の処理を繰り返す for (int i = 0; i < costs.Length; i++) { //i番目の配列のPhotonViewを取得する PhotonView c_PV = costs[i].GetComponent<PhotonView>(); //i番目の配列のPhotonViewが自分のである場合のみ if (c_PV.IsMine) { //自分のマナトークン配列にゲームオブジェクトを格納する List<GameObject> list = new List<GameObject>(myCost); list.Add(costs[i]); myCost = list.ToArray(); } } //自分のマナトークン配列が1より長い場合 if (myCost.Length >= 1) { //手札を場札とする処理を呼ぶ HandRemove(); //自分のマナトークン配列から一番目の要素を破壊する PhotonNetwork.Destroy(myCost[0]); } } } void HandRemove() { //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.47f, 4.425f, 11.276f); //回転を拘束 rigidbody.constraints = RigidbodyConstraints.FreezeRotation; } }
これで、実行すると、なんとマナトークン無しでは手札を使用できないのですが、手札を動かす事は出来てしまいます。
なぜなら、カードプレファブについているMouseDrag.csが有効だからです。
なので、MouseDrag.csの処理全てを、「手札でない場合」に処理をするに書き換えてあげます。
そして、マナトークンが無い場合にカードをクリックしたらOnMouseUpでブブーという音を鳴らそうと思います。
スクリプトは以下のようになります。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MouseDrag : MonoBehaviour { public AudioClip audioClip1; public AudioClip audioClip2; AudioSource audioSource; void OnMouseDrag() { if(tag != "Hands") { //cardの位置をワールド座標からスクリーン座標に変換して、objectPointに格納 Vector3 objectPoint = Camera.main.WorldToScreenPoint(transform.position); //cardの現在位置(マウス位置)を、pointScreenに格納 Vector3 pointScreen = new Vector3(Input.mousePosition.x, Input.mousePosition.y, objectPoint.z); //cardの現在位置を、スクリーン座標からワールド座標に変換して、pointWorldに格納 Vector3 pointWorld = Camera.main.ScreenToWorldPoint(pointScreen); //pointWorld.z = transform.position.z; pointWorld.y = 4.4117f; //cardの位置を、pointWorldにする transform.position = pointWorld; } } private void OnMouseDown() { if (tag != "Hands") { audioSource = gameObject.GetComponent<AudioSource>(); audioSource.clip = audioClip1; audioSource.Play(); } } private void OnMouseUp() { if (tag != "Hands") { audioSource = gameObject.GetComponent<AudioSource>(); audioSource.clip = audioClip1; audioSource.Play(); } else if (tag == "Hands") { audioSource = gameObject.GetComponent<AudioSource>(); audioSource.clip = audioClip2; audioSource.Play(); } } }
インスペクター上で新しいオーディオソース(ブザー音)をドラッグアンドドロップで登録します。
これで、マナが無い時に手札を使おうとするとブブーって音が鳴るようになりました。
UnityとPhotonとC#で相手にマナトークンの所有権を移譲する
まず、山札からカードをドローする際に、上に載っているトークンを触ってしまうので、触らないようにコライダーをオフにしておきます。
ドローする時に"PreMana"のコライダーをオフにしておく
コライダーをオフにすると、トークンがテーブルと衝突しないので、奈落の底に落ちていきます。
よって、ManaPicker.csを改変し、RigidBodyのConstraintsでFreezePositionしてあげます。
これによって、現状トークンが2つ以上あると被ってしまいますが、それは後に解決するとして、ドローの邪魔にはならないはずです。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class ManaPicker : MonoBehaviour { [SerializeField] Transform manaSpawnPoint; // Start is called before the first frame update private void OnTriggerExit(Collider other) { Debug.Log("collision"); if (other.gameObject.tag == "Mana") { other.tag = "Cost"; GameObject mana = (GameObject)PhotonNetwork.Instantiate("Mana", manaSpawnPoint.position, manaSpawnPoint.rotation, 0); mana.tag = "PreMana"; Rigidbody RB = mana.GetComponent<Rigidbody>(); RB.constraints = RigidbodyConstraints.FreezePosition; mana.GetComponent<BoxCollider>().enabled = false; } } }
ちょっと対戦相手のトークンがプルプル震えますが、これもいつか解決したいと思います。
皿とManaPickerコライダーを対戦相手と自分のものと分ける
plateコライダーをそれぞれのPositionSphereの中に入れて、自分の物しか使えないようにしておきます。
PositionSphereは片方着席時に反対側が破壊されるようになっているので、対戦相手と被らないはずです。
図ではPlateもPositionSphereに入っていますが、Plateは関係ないので入れない方がよかったです。
SphereコライダーをBoxColliderに変える
マナトークンのOnTriggerExit判定をSphereColliderでやっていたのですが、そのコライダーに邪魔されてオブジェクトをピック出来ないので、
ボックスコライダーにして、オブジェクトが上に突き出るようにしておきます。
これによって、SphereColliderに邪魔されてピックしにくいエラーを回避することが出来ました。
着席時にマナトークンを皿の上のランダムな位置にインスタンス化する
マナトークンがランダムに毎回出てきた方が面白いと思ったので、ランダムで出てくるように書きました。
たまに位置が被ると飛び出るのは、どうにかしたい所ですが、些細な問題なので現状は無視します。
スクリプトを参考までに張っておきます。
for (int i = 0; i < 7; i++) { float x = manaPosition.position.x - 0.025f + Random.Range(0, 0.05f); float y = manaPosition.position.y + Random.Range(0, 0.05f); float z = manaPosition.position.z - 0.025f + Random.Range(0, 0.05f); int xx = Random.Range(0, 180); int yy = Random.Range(0, 180); int zz = Random.Range(0, 180); GameObject mana = (GameObject)PhotonNetwork.Instantiate("Mana", new Vector3(x, y, z), Quaternion.Euler(xx, yy, zz), 0); }
ライブラリ(山札)の上にトークンが出てくる位置も同じくランダムにしてあげる事にしました。
同様のスクリプトですが、参考までに張っておきます。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; public class ManaPicker : MonoBehaviour { [SerializeField] Transform manaSpawnPoint; // Start is called before the first frame update private void OnTriggerExit(Collider other) { Debug.Log("collision"); if (other.gameObject.tag == "Mana") { other.tag = "Cost"; float x = manaSpawnPoint.position.x - 0.025f + Random.Range(0, 0.05f); float y = manaSpawnPoint.position.y + Random.Range(0, 0.05f); float z = manaSpawnPoint.position.z - 0.025f + Random.Range(0, 0.05f); int xx = Random.Range(0, 180); int yy = Random.Range(0, 180); int zz = Random.Range(0, 180); GameObject mana = (GameObject)PhotonNetwork.Instantiate("Mana", new Vector3(x, y, z), Quaternion.Euler(xx, yy, zz), 0); mana.tag = "PreMana"; Rigidbody RB = mana.GetComponent<Rigidbody>(); RB.constraints = RigidbodyConstraints.FreezePosition; mana.GetComponent<BoxCollider>().enabled = false; } } }
おわりに
今回は少し長かったでしょうか。
この先は複雑な内容になりそうなので、一度ここで記事を切ります。
対面でマナを使ったプレイができるようになりました。
次回は、使用したマナが相手に移るようにする所有権の移譲というのをやりたいと思っています。
Photonの美味しいところを使う予定です。
それでは、また!