前回の第6回の記事では同じ色のボールをなぞって消す処理を作りました。
前回の記事↓
今回の第7回と次回の第8回では、第6回で作成した処理を改良します。
つながっている3つ以上の同じ色のボールをなぞると消える処理を作っていきます。
記事前半では選択したボールのカウント部分を作ります。すなわち、選択したボールの数を数え、3つ以上であれば削除する処理ですね。
記事後半のボールの種類の設定では、次の3項目に分けて段階的に説明していきます。
- 種類の追加
- 近くのボールを検出する処理(第8回で解説)
- 違うボールをなぞれないようにする処理(第8回で解説)
選択したボールのカウント
前回までのコードでは、1個や2個のような少ない数でも選択したボールが消えてしまいました。そこで、3個以上選択したときだけボールが消えるようにする処理に書き換えていきます。
まずは事前準備として、BallObject.csを次のように書き換えていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class BallObject : MonoBehaviour { [SerializeField] public Renderer renderer; [SerializeField] public bool isTouch = false; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (isTouch) { GetComponent<BallObject>().renderer.material.SetColor("_EmissionColor", new Color(0.5f, 0.5f, 0f)); } else { GetComponent<BallObject>().renderer.material.SetColor("_EmissionColor", new Color(0.0f, 0.0f, 0f)); } } void OnCollisionEnter(Collision other) { //ボールにぶつかったとき if (other.gameObject.tag == "Ball") { Debug.Log("ボールにぶつかった!"); } else { Debug.Log("ボールじゃないところにぶつかった!"); } } } |
isTouchは選択しているかどうかを意味する変数として定義しています。
これまでは、選択中のボールでも再度タッチすれば選択したボールの数を誤って増やしてしまっていました。
isTouch変数を使ってこの数えすぎ問題を防止します(また、Update関数内でisTouchによりEmissionの色が自動で変わるように変えました)。
それでは、ボールの数をカウントする処理を書いていきましょう。TouchManager.csのUpdate関数を次のように書き換えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
void Update() { var mousePos = Input.mousePosition; // Debug.Log(mousePos); if(Input.GetMouseButtonDown(0)) { touchBallList = new List<GameObject>(); Ray ray = Camera.main.ScreenPointToRay(mousePos); var h = Physics.RaycastAll(ray, 100.0f); if(h.Length > 0) { //タッチしたボールが選択状態でないとき if (h[0].collider.tag == "Ball" && !h[0].collider.GetComponent<BallObject>().isTouch) { h[0].collider.GetComponent<BallObject>().isTouch = true; touchBallList.Add(h[0].collider.gameObject); } } } if(Input.GetMouseButton(0)) { //選択しているボールが0個でないとき→タッチした場合 if(touchBallList.Count!=0) { Ray ray = Camera.main.ScreenPointToRay(mousePos); var h = Physics.RaycastAll(ray, 100.0f); if(h.Length > 0) { if(h[0].collider.tag=="Ball" && !h[0].collider.GetComponent<BallObject>().isTouch) { h[0].collider.GetComponent<BallObject>().isTouch = true; touchBallList.Add(h[0].collider.gameObject); } } } } if(Input.GetMouseButtonUp(0)) { ReleaseObject(); } } |
if文の条件判定式で先ほど定義したisTouch変数を組み合わせています。
(関連記事:Unity C#プログラミング入門講座 第6回 制御文の使い方1 if文・ switch文での条件分岐 )
以下のようにすると、タッチされていないボールの時、if文の中身が実行されるようになります。
1 |
if(h[0].collider.tag=="Ball" && !h[0].collider.GetComponent().isTouch) |
そして、このif文の中身は次のようになっています。
1 2 3 4 5 |
if(h[0].collider.tag=="Ball" && !h[0].collider.GetComponent().isTouch) { h[0].collider.GetComponent().isTouch = true; touchBallList.Add(h[0].collider.gameObject); } |
一行目でisTouchをtrueにしているため、次から選択されているボールがif分岐に入ることがなくなります。これでカウント数を正しくすることができました。
さらに、TouchManager.csのReleaseobject()を次のコードに書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public void ReleaseObject() { var cnt = touchBallList.Count; //離したらマテリアルの色を戻す foreach(GameObject go in touchBallList) { //選択状態を解除 go.GetComponent<BallObject>().isTouch = false; //3個以上なら消す if(cnt>=3) { Destroy(go); } } touchBallList.Clear(); } |
コードの流れを解説します。
まず、選択したボールを記憶している配列touchBallListの要素数をtouchBallList.Countで取得しcntに代入しています。
そして、cntが3以上であれば選択したボールを削除するようにif文を使って分岐させています。
以上のコード追加により、選択したボールが3つより少ない場合、選択しても消えなくなっていることが確認できます。一度ご自身でUnityを再生して確認してみてください。
ボールの種類の設定
マテリアルの色を変えてボールの種類を作っていきましょう。
まずは、ボールの種類を列挙型を使って定義します。
(関連記事:Unity C#プログラミング入門講座 第11回 列挙型の使い方)
GameResources.csという名前の新しいスクリプトを作成してください。
ここで注意ですが、GameResourcesクラスは、Start関数やUpdate関数などがあるMonobehaviorを継承したクラスとしては利用しません。
一度自動で生成されるコードを全消去して、次のコードだけになるように書き換えてください。
1 2 3 4 5 6 7 8 9 10 |
public class GameResources { public enum BallColor { red, blue, green, purple, } } |
今回は、red、blue、green、purpleの4種類のボールを設定します。
ボールの種類を追加する方法
このゲームでは、ボールが生成されるタイミングに種類が決まるようにします。
BallGenerator.csのボールが生成される処理にそのための処理を加えていくことになります。
その前に、BallObject.csを次のように書き換えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class BallObject : MonoBehaviour { [SerializeField] public Renderer renderer; [SerializeField] public bool isTouch = false; [SerializeField] public GameResources.BallColor color; // Start is called before the first frame update void Start() { ChangeColor(); } // Update is called once per frame void Update() { //省略 } //変数colorによってAlbedoの色を変更する関数 public void ChangeColor() { switch (color) { case GameResources.BallColor.red: GetComponent<BallObject>().renderer.material.SetColor("_Color", Color.red); break; case GameResources.BallColor.blue: GetComponent<BallObject>().renderer.material.SetColor("_Color", Color.blue); break; case GameResources.BallColor.green: GetComponent<BallObject>().renderer.material.SetColor("_Color", Color.green); break; case GameResources.BallColor.purple: GetComponent<BallObject>().renderer.material.SetColor("_Color", new Color(1, 0, 1)); break; } } } |
まずは追加したGameResources.BallColor型の変数colorを定義し、ボールの種類を設定できるようにしました。そして、ChangeColor関数により、colorでAlbedoの色が変わるようにしています。
さらに、BallGenerator.csを次のように書き換えましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; //Enumを利用するため using System.Linq; public class BallGenerator : MonoBehaviour { [SerializeField] GameObject ballObj; int cnt = 0; const int MAXCNT = 60; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { cnt++; cnt%=MAXCNT; if(cnt==0) { GameObject gameObject = Instantiate(ballObj); gameObject.transform.parent = this.transform; gameObject.transform.localPosition = Vector3.zero; // カラーをランダムに設定 gameObject.GetComponent<BallObject>().color = Enum.GetValues(typeof(GameResources.BallColor)).Cast<GameResources.BallColor>().ToList()[UnityEngine.Random.Range(0, 4)]; // 角度を付けてボールを落とす gameObject.GetComponent<Rigidbody>().AddForce(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-60.0f, 60.0f)) * Vector3.down * 10f, ForceMode.Impulse); } } } |
このコードではnamespace System.Linq;を使用しています。これはLinqと呼ばれるC#の言語で少し難しい処理を扱うときに使用する宣言文です。この記事では使い方のみ解説していきます。
追加した部分は、Update関数の最終行の次のコードです。
1 |
gameObject.GetComponent<BallObject>().color = Enum.GetValues(typeof(GameResources.BallColor)).Cast<GameResources.BallColor>().ToList()[UnityEngine.Random.RandomRange(0,4)]; } |
gameObject.GetComponent<BallObject>().colorは先ほど設定したボールの種類を設定する部分です。
Enum.GetValues(typeof(GameResources.BallColor)).Cast<GameResources.BallColor>().ToList()[UnityEngine.Random.Range(0,4)]; }がとてもややこしいことになっていますが、それぞれ次のような処理を行っています。
- Enum.GetValues(typeof(T)):Tの列挙型を配列にする処理
- Cast<T>():T型に変換する処理(Linq式を使える型に変更)
- ToList():リストに変換する処理
- UnityEngine.Random.Range(a,b):a~(b-1)の範囲のランダムな値を返す関数
つまり、列挙型を配列に→配列からリストに→リストの添え字をランダムに1つ選んで要素を取得という処理を一行にまとめたコードとなります。
また、Random.RangeメソッドはUnityとC#それぞれで同一名のものがライブラリとして定義されてしまっています。そのため、UnityEngineのものを利用することを明示しています。
この書き方は特別覚える必要はないですが、「指定した列挙型のランダムな値を返す処理」と考えてもらえればよいです。
関連記事1:UnityC# Listの使い方
関連記事2:UnityC#のLinq・属性・拡張メソッド・クラスの部分定義の使い方
では、この状態で実行してみましょう。色の違う生成されるボールが生成されていく様子が確認できます。
処理の流れとしては、
- GameResourses.csで色情報を格納した列挙体を作成
- BallGenerator.csでボールオブジェクトを生成し、列挙体のcolorパラメータに値を乱数で設定。
- BallObject.csのstart関数の中のChangeColor.cs関数が呼び出され、実際に色が変わる。
という形になっています。
おさらいと次回予告
今回は、ボールの設定の3項目の中の
- ボールの種類追加
を解説してきました。
さて、段々とゲームが完成に近づいてきました。メイン部分で残っている実装部分は次の2つです。
- 種類の違うボールを選択しない処理
- 近くのボールの検出
これらを実装しないと、次のような結果となります。
現状、
- 違うボールでも選択した状態になってしまうためパズル要素がない状態
- 遠くのボールをなぞっても選択した状態
という課題が残っています。これらを第8回で解決していきます。
次回の記事↓
パズルゲーム講座が動画になりました。
動画も合わせてチェック!
コメント