さて、今回は早速対戦プレイに持ち込みたいところです。まずはPhotonをAssetStoreよりImportします。そして、ホームページにログインして自分のAppIDを確認して、コピペします。
AppIDを入れたら、RegionをJPに、そしてAuto-Join Lobbyにチェックを入れます。
次にEmptyObjectを作成し、NetworkManagerと名前を付けます。
それから、同様にDeckというオブジェクトを作成します。それから、Textを配置し、Photonのコネクションの状態を監視できるようにします。
NetworkManager.csには最終的に下記のように記載しました。
カメラの設定はカメラをプレイヤー2のみ回転させることで対戦ゲームとしての逆から見た図を作ることを可能にしています。
カメラを2つ置いて切り替えるというアプローチも行ったのですが、2dRaycasterが複数あると、マウスオーバーをした際におかしな挙動をしてしまうので、上手くいきませんでした。
片方のカメラを回転するという結論に至るまでには実は長い道のりがあったのですが、それについては別記事で言及したいと思います。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //text 使うので public class NetworkManager : MonoBehaviour { Deck deck; //Deckクラスを参照します // ChipCalculator chipCalculator; [SerializeField] Text connectionText; [SerializeField] Transform[] spawnPoints; [SerializeField] Camera sceneCamera; public float cardOffset; //カードのずらし幅offsetの宣言 public Vector3 startfirst; public Vector3 startsecond; GameObject player; //Photonの設定 void Start() { PhotonNetwork.logLevel = PhotonLogLevel.Full; PhotonNetwork.ConnectUsingSettings("0.1"); //deck = GetComponent<Deck>(); //Deck.csの取得 deck = GameObject.Find("Deck").GetComponent<Deck>(); } void Update() { connectionText.text = PhotonNetwork.connectionStateDetailed.ToString(); } void OnJoinedLobby() { RoomOptions ro = new RoomOptions() { isVisible = true, maxPlayers = 2 }; PhotonNetwork.JoinOrCreateRoom("Tomo", ro, TypedLobby.Default); } //Photonの設定ここまで //カメラの設定 public void CameraSetting(float cameratime)//この引数は使用していない { if(PhotonNetwork.playerList.Length == 1)//一人目の場合 { sceneCamera.enabled = true; } else//二人目のプレイヤーの場合 { sceneCamera.transform.rotation = Quaternion.Euler(0, 0, 180);//プレイヤー2はカメラを180回転させて画面を反転する。 } } void OnJoinedRoom() { CameraSetting(0f);//カメラの設定を呼んでいる if (PhotonNetwork.playerList.Length == 1)//一人目の場合 { int index = 0; int cardCount = 0; //内部で使う値cardCountの宣言 deck.Shuffle(); Vector3 temp3 = new Vector3(0.031f, -4.137f, 0); foreach (int i in deck.GetCards()) { float co = cardOffset * cardCount; //オフセット幅の計算 Vector3 temp = startfirst + new Vector3(4.3f -co, -1.5f); GameObject cardCopy = (GameObject)PhotonNetwork.Instantiate("Koma", temp, spawnPoints[index].rotation, 0);//犬の駒をInstantiateする。 KomaModel cardModel = cardCopy.GetComponent<KomaModel>(); cardModel.cardIndex = i;//インデックスにiを代入 cardModel.ToggleFace(0); cardCount++; //cardCountをインクリメント } } else//プレイヤー2の場合 { int index = 1; int cardCount = 0; //内部で使う値cardCountの宣言 Vector3 temp2 = new Vector3(-6.61f, 3.18f, 0); foreach (int i in deck.GetCards()) { float co = cardOffset * cardCount; //オフセット幅の計算 Vector3 temp = startfirst + new Vector3(-3.1f -co, 1.5f); GameObject cardCopy = (GameObject)PhotonNetwork.Instantiate("neko", temp, spawnPoints[index].rotation, 0);//猫の駒をInstantiateする。 KomaModel cardModel = cardCopy.GetComponent<KomaModel>(); cardModel.cardIndex = i;//インデックスにiを代入 cardModel.ToggleFace(0); cardCount++; //cardCountをインクリメント } } } }
そして、インスペクター上のconnectionTextの部分にTextをヒエラルキーからドラッグ&ドロップします。 SpawnPointsにはCubeを一個作成しドラッグ&ドロップ。 そして、もう一個キューブを作成して180度回転させてからドラッグ&ドロップします。 これは駒の向きをそのキューブの回転角をもとに計算するようになっているからです。 CardOffsetの値は0.3が最終的には駒台にちょうど乗ったので良かったのではないかと思います。
そして、DeckにはDeck.csをアタッチし、下記のように記載します。 これは駒をシャッフルしてどの駒か順序によって悟られないようにするために、トランプのシャッフルスクリプトを流用しています。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Deck : MonoBehaviour { List<int> cards; //リストの宣言。 public IEnumerable<int> GetCards() //戻り値に列挙可能なリストを持つメソッド { foreach (int i in cards) //cardsの中の要素それぞれについて { yield return i; //要素を戻り値に返します } } public void Shuffle() //shuffleメソッド本体。 { if ( cards == null) //cardsが空であれば。 { cards = new List<int>(); //cardの初期化。 } else //その他の場合。 { cards.Clear(); //cardsを空にする。 } for (int i = 0; i < 5; i++) //初期値i=0から15以下の場合下記を繰り返しiをインクリメント。 { cards.Add(i); //リストにiを加える。 } int n = cards.Count; //整数nの初期値はカードの枚数とする。 while (n > 1) //nが1より大きい場合下記を繰り返す。 { n--; //nをディクリメント int k = Random.Range(0, n + 1);//kは0~n+1の間のランダムな数とする int temp = cards[k]; //k番目のカードをtempに代入 cards[k] = cards[n]; //k番目のインデックスにn番目のインデックスを代入 cards[n] = temp; //n番目のインデックスにtemp(k番目のインデックス)を代入 } } void Start () { Shuffle(); //shuffleメソッドを実行 } }
そして、忘れずに駒にPhotonView.csをつけて、Resourceフォルダに保存しプレファブ化します。 PhotonViewがついていないとゲームオブジェクトとしてお互いに認識されないので対戦相手に表示されません。
この時、犬の駒と、猫の駒をそれぞれ別々に作成し、名前をkoma, nekoと、NetworkManager.csに記載した通りにしておかないとInstantiateが正しくされないので注意が必要です。 また、グラフィックは犬の駒は犬のものを配列にいれ、猫の駒は猫のものを配列に入れて、それぞれ正しく表示されるようにしておきます。
それから、背景の上にきちんと駒がレンダーされるようにSortingLayerを設定しておきます。 この時、レンダーされる順序を決める所はLayerではなく、SortingLayerなので注意です。
SortingLayerの選択はLayerではなく、SpriteRendererのSorting Layerの部分で選択するようになっていますので注意です。 背景を作成してAssetフォルダにD&Dします。そして背景を配置し、SortingLayerをBackGroundにします。
ビルドして、対戦相手と両方部屋に入ると、正しく駒が配置されることが確認できます。 きちんと裏面が表示されて相手にどの駒が何かわからないようになっています。 自分の駒はマウスオーバーでひっくり返すことができるので確認できますが、相手にはそのグラフィックは見えないようになっています。 ダブルクリックすると駒を公開することができます。
続いて、駒の動きの同期をとります。 PlayerNetworkMover.csを駒にアタッチし、PhotonViewの監視対象をD&Dでそのスクリプトにします。
PlayerNetworkMover.csには下記のように記載します。 互いの駒を触れないように2dColliderはデフォルトでオフにしてあるので、それを自分の駒だけONにするようにしてあります。 そして、位置とインデックスの同期をとるようなスクリプトにしてあります。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerNetworkMover : Photon.MonoBehaviour { KomaModel card; DoubleClick doubleClick; Vector3 position; public int clickedNum; float smoothing = 1f; private int i = 0; void Awake() { card = GetComponent<KomaModel>(); doubleClick = GetComponent<DoubleClick>(); } void Update()//start から変更 { if (photonView.isMine) { GetComponent<BoxCollider2D>().enabled = true; } else { StartCoroutine("UpdateData"); } } IEnumerator UpdateData() { while (true) { transform.position = Vector3.Lerp(transform.position, position, Time.deltaTime * smoothing); yield return null; } } void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.isWriting) { stream.SendNext(transform.position); //現在のポジションを送る stream.SendNext(card.cardIndex); //cardIndexの情報を送る stream.SendNext(doubleClick.clickNum); //クリックされた回数を送る } else { KomaModel cardModel = GetComponent<KomaModel>(); //受信先のCardModelを取得 position = (Vector3)stream.ReceiveNext(); //現在のポジションを受信 cardModel.cardIndex = (int)stream.ReceiveNext(); //cardIndexの情報を受信 clickedNum = (int)stream.ReceiveNext(); //クリックされた回数を受信 if (clickedNum % 3 == 1) { cardModel.ToggleFace(0); //表をレンダー }else if(clickedNum % 3 == 2) { cardModel.ToggleFace(2); //裏をレンダー } else { cardModel.ToggleFace(1); } } } }
Prefab化した駒の2dBoxColliderは下記のようにチェックを外してあります。 スクリプトによってオンになるようになっています。
現状ではkomaBackIndexの値を指定していないので、成った状態では 、0番の桂馬がデフォルトで常に表示されるようになっています。 これは今後、修正していきたいと思います。
もしよろしければ、Photonについて私が知りえる限りの情報を徹底的にまとめてみたので、下記の記事も読んで下さいね!