【初心者Unity】懐かしのゲームを作ってみよう!⑧
▶
【Unity】3Dアクションゲームを作ろう!#7 ステージの作成(Skybox・落下判定)
▶
【Unity】3Dアクションゲームを作ろう!#8 ステージの作成(スイッチ・扉)
▶
【Unity】3Dアクションゲームを作ろう!#9 プレイヤーのHP管理
▶
【初心者Unity】JsonUtilityクラスでJSONを扱う方法
▶
【初心者Unity】スクリプトからコンポーネントを追加する方法
はじめに
今回は前回に引き続き、↓のゲームを作っていきます。
前回はサクッとBlockを作成しました。
今回はいよいよBallがBlockに衝突した時にBlockが壊れる(消える)という処理を実装します。
前回の最後に、この様な処理をどの様に実装すればよいか各自で考えましょう!といった軽い宿題じみた話を持ちかけたわけですが皆さんいかがでしょう、考えてみましたか?(笑)
考えた方も(嬉)考えてない方も(悲)ぜひこれまで通り楽しみながら取り組んでください!
それではLet’s get started!!
第4段階:Blockを作成しよう!(2)
まずは、今回実装したいことを言語化してみましょう。
「Blockが消える。BallがBlockに衝突した時に。」
何やら文章の並びが不自然ですね。。。
不自然な書き方をしたのは、文学的な言い回しがしたかったのではなく、この書き方の方が設計の流れが伝わりやすいからです(笑)
今回のメインの文は最初に書かれている「Blockが消える」です。
主語が「Blockが」となっているので、今回のスクリプトはBlockに貼り付けます。そこでこれまでの命名の流れ的にBlockScriptというスクリプト名にしましょうか(笑)
次に、述語が「消える」となっているので、BlockScriptの中で「消える」処理を実装します。ここで重要なのがどのメソッド内でその処理を実行するかです。
基本的なメソッドとしてStart()とUpdate()がありますが、例えばStart()内にその処理を書いてしまうとどうでしょう?そう!ゲーム開始と同時にBlockが消えてしまいますね(笑)
それではUpdate()内はどうでしょう?これも。。。毎フレームBlockが消えるという何やらよくわからんことになりますね(笑)
ということで重要なのは消えるタイミングです。そこで上の文を再度確認すると、そのタイミングを確認することができます。そう、「BallがBlockに衝突した時に」です。
「衝突した時」に動く(呼び出される)メソッドありましたよね。。。?そうです!BallとPaddleの衝突を検知していたOnTriggerEnter2D()です!
「衝突」という言葉に厳密に行くのであれば本当はOnTriggerEnter2D()ではなくOnCollisionEnter2D()なのですが、後者を使うには、ガチで衝突させなければならず、各オブジェクトがもろにその影響を受けて吹っ飛んでしまったりするので、今回は実際に衝突させる必要がなく、重なったことを検知するだけの前者を使います。
まとめると以下の様な処理を加えることになります。
①BlockScriptを作成し、Blockに貼り付ける。
②OnTriggerEnter2D()の中にBlockを消す処理を書く。
①の処理はこれまでBallとPaddleにそれぞれ施してきたのを思い出していただければ同様にできるかと思います。
まずHierarchyタブのBlockをクリックします。するとInspectorタブにBlockの詳細が表示されるので、一番下のAdd Componentをクリックします。するとまた一番下にNew Scriptが現れるのでそれをクリックし、スクリプト名を決めます。今回は「BlockScript」と名付けました。最後に作成されたスクリプトをダブルクリックすればスクリプトがVisual Studioで開かれます。
次に②の処理ですね。これはまぁとりあえず分かっていることだけ書いてしまいましょう。
今分かっていることの中でこれまでの知識で書けるものはOnTriggerEnter2D()ですよね?これを書き加えると↓の様になります。
public class BlockScript : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void OnTriggerEnter2D(Collider2D collision) { //Blockを消す処理 } }
↑を見ていただけると分かるように後は「Blockを消す処理」を書けばBlockScriptは終わりですね!
実は消す処理ですが、2つ程方法があります。
1つ目はDestroy()メソッドを使う方法です。このメソッドはその重々しい名前の通りオブジェクトを破壊し(消し)ます。
このメソッドの特徴は、対象のオブジェクトをHierarchyから完全に消し去ることができることです。
つまり、このメソッドを使ってBlockを消すと、そのBlockは二度と復活しなくなるということです。今後、消したBlockを再利用しないというのであればこのメソッドは非常に有効です。
※どうしても復活させたければInstantiate()メソッドで同じパラメーターを持ったオブジェクトを生成することも可能です。
2つ目はSetActive()メソッドを使う方法です。このメソッドはその軽々しい(?)名前の通り、オブジェクトをActive(有効)にしたり、逆に無効にしたりすることができます。
このメソッドの特徴は、対象のオブジェクトをHierarchyに残しつつゲームの画面上から消したり、反対に出現させたりすることができることです。
つまり、このメソッドを使ってBlockを消しても、そのBlockは再度出現させることができるということです。今後、消したBlockを再利用するのであればこのメソッドは非常に有効です。
さあこの2つのメソッドの内どちらを使うのかというお話になりますが、ここで争点となるのは、この2つのメソッドの相違点である、Hierarchyに残すか残さないかという点です。
言い換えれば、消したBlockを再利用するか否かという点です。
これを決定するにあたって、現時点の「消したい」という願望のみに依存するのであれば、自然とDestroy()が選択されるかと思います。なぜなら消しゃーいいから。復活させる意味なくない?といったところです。
が、しかし、先見の明を持ってこの分岐点に立つとSetActive()が選ばれます。
その理由は、完成したこのゲームを皆さんがプレイするところをイメージしていただくと見えてくるかと思います。
Ballが動きます。BallがBlockを壊します。PaddleがBallを跳ね返します。。。これを最後のBlockが破壊されるまで繰り返しますね?まあ途中でゲームオーバーする可能性もありますが今はおいておきましょう(笑)
さあ、最後のBlockを破壊し、画面に「Game Clear」の文字が現れました。これで終わり!ではないんです!!!更にイメージを推し進めてください。
これでイメージが終わるとこのゲームは1度遊んだだけでもう2度とプレイできなくなってしまいます。なぜならその後の処理がイメージされていないから。
それではどうすれば頑張って作ったこのゲームを1度ならず何度もプレイすることができるでしょうか?
そうですね、例えばもう一度遊ぶための「Continue」ボタンでも作ればいいかもしれませんね?
他にも、ステージを複数作ったのであれば、ステージ選択のページへ飛んでも良いかもしれませんね?
というように、一度破壊されたBlockは、Continueボタンが押される度に何度も出現しなければなりません。
このときにDestroy()をもってして破壊してしまうと、Continueボタンが押された時に新たにオブジェクトを「再生成」することになり、多少処理が重くなります。
に、対してSetActive()であればBlockを「破壊する」というよりは、「出現」「非出現(?)」をさながら照明器具のオン・オフの如く切り替えるに留まるため、Continueボタンが押された時の再出現を比較的軽い処理で済ませることができます。
さあ、長くなってしまいましたが要するに今回は、Blockは一度消えてもContinueボタンが押された時再出現するためにSetActive()を使うということになります。
それでは実際のSetActive()の使い方ですが、割と簡単です。引数にtrueを取ればそのオブジェクト(またはコンポーネント)が有効になります。反対に、引数がfalseであれば無効になります。↓みたいな感じですね。
・SetActive(true) :オブジェクトやコンポーネントが有効になる
・SetActive(false):オブジェクトやコンポーネントが無効になる
早速BlockScriptに書き加えると↓の様になります。
public class BlockScript : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void OnTriggerEnter2D(Collider2D collision) { this.gameObject.SetActive(false); } }
よっしゃ!これで動くぞ!と思いきや。。。
スタートボタンを押して、BallとBlockがぶつかってもすり抜けてしまうのではないでしょうか。。。?
そうなんです、残念ながらまだ完成ではないのです。。。てかこのくだりBallとPaddleの時にもやりましたね(笑)
そうです、プログラム的には完成なのですが、BallとBlockそれぞれに衝突を判定させるためのあるコンポーネントを追加しなければなりません。
それは「Rigidbody 2D」と「Collider 2D」でしたね。
「Rigidbody 2D」はオブジェクトに物理演算を加えるもので、「衝突」という物理的な事象もこのコンポーネントがあることによって検知されます。
「Collider 2D」はオブジェクトの当たり判定の範囲を定義することができます。これがついていないと、そもそもの当たり所が無くなってしまい、存在は見えているけれども触れられない。。。まるで雲?幽霊?の様な存在になってしまいます。
BallはPaddleのくだりの時に付けたので、今回はBlockにだけ付けましょう。
まずは「Rigidbody 2D」ですが、前回はBall、Paddleの両方につけていましたが、ごめんなさい、実は衝突するオブジェクトの片方のみRigidbodyを持っていれば問題なく動きます。
なので、今回は既にBallがRigidbodyを持っているのでBlockにはつける必要がありません。もしもPaddleのRigidbodyを消したい方がいれば消していただいてかまいません。むしろ、消した方が処理が軽くなって良いです。
消し方は、「Paddle」の「Inspector」内の「Box Collider 2D」と書いてある右の方の点が3つ縦に並んでいるものをクリックして、「Remove Component」を選択すればオッケー(無理に消さなくても良いです)
「Rigidbody」は必要ありませんが「Collider 2D」は必要です。「Hierarchy」上の「Block」をクリックした後、「Add Component」をクリックし、検索ボックスで「co」辺りまで入力します。次に、「Box Collider 2D」をクリックすれば完成です。
さあ、ようやく完成か。。。と思いきや、最後にもう一息です。
今回は衝突と言えども、実際に衝突させて物理的な処理を加えるというよりも、衝突を「検知」して、数字上で変化させるという処理を取っています。
前者ではない理由としては、先程も言及した通り、実際に物理的に衝突させると、その反動でBlockが吹っ飛ばされたりするからです(笑)
そこで、今回はあくまで「検知」だけですよーということを示すべく、BlockのColliderの「is Trigger」チェックボックスにチェックを入れてようやく完成です。お疲れ様でした!
実際にゲームを実行すると。。。いかがでしょうか?無事、BallがぶつかるとBlockが消えるのでは無いでしょうか??
ただまあ動きとしては若干納得いかないところもありますよね。。。BlockにぶつかってもBallが跳ね返らない。。。この世界には反作用がないのか、と(笑)
まあここは次回の課題にしておきましょう。
そしてここで重要なことですが、BlockがGameView上は消えたものの、Hierarchyには残っているのが確認できるのではないでしょうか?(まあ文字が薄くはなっていますが。。。)
これがSetActive()の力です。これを仮にDestroy()にするともはやHierarchy上から消えます。
また、この薄文字のBlockをクリックしていただき、Inspectorタブを見てみてください。よく見ると「Block」の名前の横のチェックボックスが外れているのではないでしょうか?
そう、実は先ほどから言っていたオブジェクトの有効・無効とは、このチェックボックスを使うことで手動で操作することもできるのです。
おわりに
今回は2回の記事でBlockが完成しました。
これまでBallにしてもPaddleにしても3回掛かっていたのに早かったですね(笑)
と、思いきや実はまだ課題が残っています。
そう、Ballに衝突した後のBallの動きです。
いくらBlockが消えるとはいえ、突き抜けてしまうのはおかしいですよね(笑)
それに、ゲームの難易度としても格段に簡単になってしまいます。なぜならBallは実質壁でしか跳ね返らないから。
Blockでも跳ね返ることで、Ballの動きが変則的になり、より難易度が上がるのです。
と、いうことで次回はBallがBlockで跳ね返る処理を付け加えます。
まあいじるのはBallだけなのですが、これでようやくBlockの章が終わります。結局3回かかりますね(笑)
乞うご期待!