第9回では、ボールを一気に消す爆弾を作成します。また、ボールが消える時のエフェクトも一緒に作成していきます。
前回の記事を読みたい方はこちら↓
爆弾の作成
爆弾は、スマホ系のパズルゲームでよく用いられるアイテムで、周囲のボールを一気に消したり特定のボールを消すといったパズルを有利にしてくれるアイテムです。
今回は、周囲のボールを一気に消す爆弾を作成していきます。
ボールの種類に爆弾を追加
まず、ボールの種類に爆弾を追加します。GameResources.csを以下のコードに書き換えましょう。
1 2 3 4 5 6 7 8 9 10 11 |
public class GameResources { public enum BallColor { red, blue, green, purple, bomb, } } |
そして、BallObject.csのChangeColor関数のコードも書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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; case GameResources.BallColor.bomb: GetComponent<BallObject>().renderer.material.SetColor("_Color",new Color(0,0,0)); break; } } |
さらに、爆弾のみ、10回生成されたら1個生成されるようにしましょう。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 41 42 43 44 45 46 47 48 49 50 51 52 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Linq; public class BallGenerator : MonoBehaviour { [SerializeField] GameObject ballObj; int cnt = 0; const int MAXCNT = 60; int generateCount = 0; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { cnt++; cnt%=MAXCNT; if(cnt==0) { generateCount++; generateCount%=10; GameObject gameObject = Instantiate(ballObj); gameObject.transform.parent = this.transform; gameObject.transform.localPosition = Vector3.zero; if(generateCount==0) { // カラーを爆弾用に設定 gameObject.GetComponent<BallObject>().color = Enum.GetValues(typeof(GameResources.BallColor)).Cast<GameResources.BallColor>().ToList()[4]; } else { // カラーをランダムに設定 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); } } } |
追加した変数generateCountは、生成したボールの数を記憶させるための変数です。そして「generateCount%=10」により、0~9の範囲で昇順に増えるようになります。
さらに、if分岐で「if(generateCount==0)」と記述することにより、generateCountが0のとき→つまり10回生成するごとに処理させるようにできます。この分岐内で「gameObject.GetComponent<BallObject>().color = Enum.GetValues(typeof(GameResources.BallColor)).Cast<GameResources.BallColor>().ToList()[4];」と記述し、爆弾を生成しています。
この状態で実行すると、爆弾(黒いボール)が10回に1回生成される様子が確認できます。
周囲のボールを消す処理
次に、爆弾をクリックしたら周囲のボールが消える処理を書いていきます。
BallObject.csに次のExplosion関数を追加してください(ちなみにExplosionは「爆発」という意味です)。
1 2 3 4 5 6 7 8 9 10 11 12 |
public void Explosion() { var h = Physics.SphereCastAll(transform.position,5.0f,Vector3.forward); Debug.Log(h.Length); foreach(var hit in h) { if(hit.collider.tag=="Ball") { Destroy(hit.collider.gameObject); } } } |
Explosion関数は、爆弾を中心として半径5mの範囲にあるタグがBallのゲームオブジェクトをすべて削除する処理になります。
Physics.SphereCastAll関数は、第一引数の座標を中心に、第二引数の半径の範囲で第三引数の方向へ移動したとき衝突したゲームオブジェクトをすべて取得する関数です。
ちなみに、Unityにおいて距離を扱う場合には、1.0fを1mと解釈します。また、Vector3.forwardはnew Vector3(0,0,1.0f)と全く同じ値です。
Physics.SphereCastAll関数の返り値をhに代入し、if分岐を使用して、hのゲームオブジェクトのうちタグがBallのもののみDestroy関数を使って消去しています。
さて、この関数は爆弾をタッチしたときに発生させたいため、TouchManager.csのGetMouseButtonDown関数内に記述していきます。次のコードに書き換えましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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) { if(h[0].collider.GetComponent<BallObject>().color==GameResources.BallColor.bomb) { //爆発! h[0].collider.GetComponent<BallObject>().Explosion(); } else { h[0].collider.GetComponent<BallObject>().isTouch = true; touchBallList.Add(h[0].collider.gameObject); } } } } |
ここでまず、タッチしたボールが爆弾かどうかで分岐させます。爆弾であれば、Explosion関数を使って爆発させるようにしています。
この状態で実行してみましょう。爆弾をタップすると周囲のボールが消える様子が確認できます。
【学歴不問・高卒、元ニートでも挑戦できる】
ボールが消える時のエフェクトの作成
ここまでで爆弾の作成は完了しましたが、アプリストアで公開されているパズルゲームよりもクオリティが低く感じた方も多いと思います。明確に違う点があるからです。
その点は、演出面です。今回の例でいえば、ボールが消えた時、ただボールが見えなくなるだけで何も演出がないため地味なゲームになってしまいます。
そのため、今回はAnimatorを使用して消去エフェクトを作成していきます。
Animatorの設定
Unity上でアニメーションを作成するためには次の2つの要素を知っておく必要があります。
- Animation
- Animator Controller
Animationは、ゲームオブジェクトを時間に伴い指定した位置へ動かす機能です。
Animator Controllerは、Animationを組み合わせて任意のタイミングで動かすことのできるコンポーネントです。今回は簡単なアニメーションの作り方を説明していきます。
Animation ClipとAnimatorを使うと、次のようなアニメーションが簡単に作れるようになります。
それでは、作成を始めてみましょう。まずは、ヒエラルキーに空のオブジェクトを作成しましょう。名前はDeleteEffectという名前にしました。
このDeleteEffectの子オブジェクトとしてSphereを生成してください。
今回はSphereを5つ使用します。同じようにSphereを生成してもいいですが、同じゲームオブジェクトをたくさん作成する場合には、Sphereを選択した状態でCtrl + Dを押すとすぐに同じゲームオブジェクトを生成することができます。
作成した5つのSphereのPositionは(0,0,0)に設定してください。これで消去アニメーションの素材は準備できました。
【注意】ここで、子オブジェクトのSphereすべてのSphere Colliderのチェックを外しておいてください。チェックを外さないとBallと衝突判定を起こし正しく動作しません。
それでは、Window→Animation→Animationと、Window→Animation→Animatorを選択してください。二つのウインドウが開かれます。
このウインドウを使いやすくするため、2つのウインドウのタブをクリックしたまま好きな位置にドラッグしましょう。ウインドウを固定することができます。
では、DeleteEffectにAnimator Controllerのコンポーネントを追加してください。
そして、次のようにProject内にAnimatorフォルダを作成して、DeleteEffectという名前でAnimator ControllerとAnimationを作成してください(下のDeleteEffectがAnimator Controllerです)。
DeleteEffectのインスペクターからAnimatorのControllerに作成したDeleteEffect Animator Controllerをドラッグ&ドロップしてください。これで、DeleteEffectにAnimator Controllerが追加できました。
一旦、この状態でDeleteEffectをプレハブに登録してください。
ここからの作業はプレハブのDeleteEffectで行ってください。
次に、具体的にAnimatorを設定していきます。Animatorウインドウを表示させてください。すると、フローチャートが表示されていることが分かります。ここに、DeleteEffect Animationをドラッグ&ドロップしてみてください。すると、フローチャートの中にDeleteEffectという名前の要素が追加されます。
次に、フローチャートの何もないところで右クリック→Create State→Emptyで空の要素を作成してください。そして、DeleteEffectの要素を右クリックしてMake TransitionしてNew Stateで左クリックをして遷移先を設定しましょう。これで、消去エフェクト後に空の要素に移動するようになります。
【重要】ここで、作成したNew Stateを選択してインスペクターから名前を「Empty」に変更してください。また、Write Defaultsのチェックを外しておいてください。
次に、DeleteEffect – Empty間の矢印を選択して、インスペクタ―を確認してください。すると、Animation用のインスペクタが表示されるのですが、ここで、Setting中のExit Timeを1.0に、Transition Durationを0に設定してください。
Exit Timeはアニメーションの何%を実行したら次の状態に遷移するかを表します。0~1の範囲で設定できるため、1に設定すると、全部のアニメーションが完了したら次のアニメーションに遷移することになります。
Transition Durationは次のアニメーションへ移動する際、どの程度ブレンドしながら遷移するかを指定できる機能です。歩いているアニメーションの途中で急に止まるアニメーションに切り替わるととても不自然だったりします。これを設定すると歩いているアニメーションからだんだんと止まるアニメーションに切り替わるようになるため自然にアニメーションを移動できることができます。今回はすぐに切り替えたいためTransition Durationを0に指定しています。
では、Animationウインドウに切り替えてください。今回はAnimationの詳細の解説は割愛しますが、大まかにこのような形で作成していきます。
まず、Previewの右側にある録音ボタンを押してください。
そして、動かしたいフレームまでラインを移動させます。
そして、動かしたい子オブジェクトを選択してシーン上で移動させます。すると、タイムライン上にマークがつきます。
また、移動だけでなく、Transition内のRotationやScaleなども変更することが可能です。
この作業をすべての子オブジェクトに対して行います。
これで、消去アニメーションが完成です。
消去エフェクト用のスクリプトの作成
それでは、消去エフェクト用のスクリプトを作成していきましょう。
初めに、DeleteEffectObject.csを作成して開いてください。
編集する箇所は、変数animatorの定義と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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DeleteEffectObject : MonoBehaviour { [SerializeField] Animator animator; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { var a = animator.GetCurrentAnimatorStateInfo(0); if(a.IsName("Empty")) { Destroy(this.gameObject); } } } |
Update関数内では、「Animatior Controllerの状態が”Empty”になった時に自動的に消える」処理を記述しています。
まず、animator.GetCurrentAnimatorStateInfo関数によりanimatorの状態を確認します。引数である0はレイヤーの番号を指しています。今回はBase Layor(0番目のレイヤー)と呼ばれる初期化時に自動的に作成されるレイヤーのみしかないため、0を指定しています。この関数の戻り値はAnimationState型が返されるため、変数aに格納させています。
次に、a.IsName関数で第一引数の文字列と状態名が一致しているか確認します。アニメーション終了後の状態「Empty」と一致していればtrueが返されるため、このif分岐はアニメーションが終了したとき実行されることが分かります。
最後に、Destory(this.gameObject)により自身を削除しています。
編集が終わったら、インスペクタのAnimatorにDeleteEffectを追加しましょう。
deleteEffectObjectの追加/設定
それでは、ボールが削除される場所に消去エフェクトを追加していきましょう。
TouchManager.csに次の行を加えます。
1 2 |
[SerializeField] GameObject deleteEffectObj; |
さらに、Releaseobject関数を次のコードに書き換えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public void ReleaseObject() { var cnt = touchBallList.Count; //離したらマテリアルの色を戻す foreach(GameObject go in touchBallList) { //選択状態を解除 go.GetComponent<BallObject>().isTouch = false; //3個以上なら消す if(cnt>=3) { GameObject delObj = Instantiate(deleteEffectObj); delObj.transform.position = go.transform.position; Destroy(go); } } touchBallList.Clear(); } |
追加した部分は、Destroy関数によりBallが削除される直前に、Ballの位置にdeleteEffectObjを生成する部分です。
また、BallObject.csのExplosion関数を次のように書き換えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void Explosion(GameObject deleteObject) { var h = Physics.SphereCastAll(transform.position,5.0f,Vector3.forward); Debug.Log(h.Length); foreach(var hit in h) { if(hit.collider.tag=="Ball") { var delObj = Instantiate(deleteObject); delObj.transform.position = hit.collider.gameObject.transform.position; Destroy(hit.collider.gameObject); } } } |
改良したExplosion関数では、第一引数の消去エフェクト用のゲームオブジェクトをInstantiate関数により複製することにより消去エフェクトを表現していきます。
Explosion関数に第一引数を設けたため、TouchManager.csのUpdate関数を以下のように書き換えてください。修正した場所は、Explosion関数の引数にdeleteEffectObjを追加した一行です。
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 |
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) { if(h[0].collider.GetComponent<BallObject>().color==GameResources.BallColor.bomb) { //爆発! h[0].collider.GetComponent<BallObject>().Explosion(deleteEffectObj); } else { h[0].collider.GetComponent<BallObject>().isTouch = true; touchBallList.Add(h[0].collider.gameObject); } } } } //以下のコードは省略 } |
最後に、TouchManagerのインスペクタ―のdeleteEffectObjにプレハブのDeleteEffectを追加します。
この状態で実行してみましょう。ボールが消去されるときに消去エフェクトが表示されるようになりました。
おさらいと次回予告
今回は爆弾の作成と消去エフェクトの作成を行いました。
次回は、得点の表示方法について説明していきます。
次回の記事↓
コメント