はじめに
前回から間が空いてしまいましたが、「いますぐ始めるアプリ内課金」の連載を再開いたします。
前回は、「アプリ内課金を含むブロック崩し」という企画をもとに、どのようにすれば実現できるかを具体的に設計しました。
今回は、前回の設計をもとにいよいよ Unity でブロック崩しの開発を進めていきたいと思います。連動している Qiita 記事では、より詳しい説明を掲載しているので併せて参考にしてください。
この連載では、ブロック崩しの Unity プロジェクトを教材として公開しています。
ダウンロードした教材プロジェクトを Unity で開き、プログラムや素材を実際に見ることで、記事の内容がよりわかりやすくなると思います。
教材は github からダウンロードしてください
https://github.com/sakura-crowd/sample_blockkuzushi
次のように Title シーンを開いて実行ボタンを押すことで、ブロック崩しをエディタ上でプレイできます。
Unityでの基本的な作業の進め方
簡単なチュートリアル
Unityでのゲーム開発に不慣れな方もいらっしゃるかと思いますので、まずは、Unity でゲームを作るイメージをつかんでいただくために、ボールが落下するシーンを作る工程をチュートリアルとして紹介します。
シーンを作る
最初にボールを配置するシーンを作りましょう。
Unity でプロジェクトを新規作成した場合は、最初から用意されています。追加で新しく作りたい場合はメニュー[File]→[New Scene]から作成できます。
シーンを作っただけだと、上の図のようにカメラだけが配置されています。誰もいない舞台をカメラが映し出しているようなものです。
シーンにゲームオブジェクトを配置する
次に、空っぽのシーンにボールのゲームオブジェクトを配置してみましょう。
画像はあらかじめプロジェクトの Assets フォルダ以下に用意しておいた Ball.png を使います。
Ball.png をシーンにドラッグ&ドロップすると、ボールの絵を表示するゲームオブジェクトがシーンに追加されました。
この状態で、再生マークのボタンを押すと、 Unity 上で作成しているゲームを実行することができます。
しかし、この状態では、ボールが表示されているだけで、まったく動きません。
ゲームオブジェクトにコンポーネントを付加する
そこで、次に物理挙動の振る舞いを与えるコンポーネントを追加します。
Ball ゲームオブジェクトの設定が表示されている Inspector タブで Add Component ボタンを押すと、さまざまなコンポーネントをゲームオブジェクトに追加できます。
今回はコンポーネントの中から [Physics 2D]→[Rigidbody 2D] を選択します。
これで、ボールに物理挙動の振る舞いが追加されました。再び再生マークのボタンを押してゲームを実行してみましょう。
さっきは空中で止まっていたボールが重力に従い落下するようになりました。
チュートリアルのまとめ
まだまだこれだけではゲームとは呼べませんが、今のチュートリアルのように
- シーンを作り
- ゲームオブジェクトを配置し
- 各ゲームオブジェクトごとに必要な機能(コンポーネント)を付加していく
ことを積み重ねていくことで、少しずつゲームを作ることができます。
シーンとゲームオブジェクト
前述のチュートリアルでは、1つのシーンに1つのゲームオブジェクト(ボール)だけでした。
実際は、タイトル画面や、ブロック崩しをプレイするステージ画面など、画面の数に応じてシーンを作ります。それらのシーンを切り替えたり、重ねたりすることで、ゲームを進行させていきます。
複数が連携することは、ゲームオブジェクトも同じです。
ゲームオブジェクトはシーンに複数配置し、お互いに影響を与えながらゲームを実現しています。
教材のブロック崩しでも、ボールだけではなく、バー、ブロック、壁などさまざまなゲームオブジェクトが1つのシーンに配置されることでブロック崩しとして動作しています。
コンポーネント
ゲームオブジェクトをシーンに配置しただけでは、ただそこに存在するだけです。表示もされなければ、移動もしません。
そこで必要になるのが、ゲームオブジェクトに機能を付加できるコンポーネントです。
コンポーネントは、機能ごとにさまざまなものが標準で用意されていて、足りなければ独自のものを作成することもできます。
ひとつのゲームオブジェクトの中に複数のコンポーネントを付加することができ、コンポーネント同士が協調してゲームオブジェクトの振る舞いを実現しています。
例えば下の図は、ブロック崩しのボールを配置したステージ画面のシーンです。
この配置されたゲームオブジェクトの「Ball」を選択すると、コンポーネントごとにその詳細な設定が右側の [Inspector] タブに表示されます。
ボールの振る舞いを実現するために付加しているコンポーネントには、
- ボールの絵を表示する「Sprite Renderer」
- ボールの物理的な動きを計算する「Rigidbody2D」
- 壁などとの衝突を検知する「Collider2D」
- ブロック崩し独自のボールの処理を行う「Ball.cs」
があり、Ball というひとつのゲームオブジェクトの中でコンポーネント同士が協調してボールの振る舞いを実現しています。
その他、今回作成するブロック崩しで使用しているコンポーネントについては、連動している Qiita 記事でより詳しく説明していますので、併せてご覧ください。
Unity でブロック崩しを作る際に使用する標準コンポーネント - Qiita
Unity でブロック崩しを作る際に使用する独自コンポーネント - Qiita
各パーツの実装
それでは、実際にブロック崩しの実装を進めていきたいと思います。はじめに、ブロック崩しには欠かせない以下のパーツの機能や実装イメージを紹介します。
- 壁
- バー
- ボール
- ブロック
- ゲームオーバー領域
壁を配置する
まずは、壁を配置しましょう。
これがないとボールを動かしたときに跳ね返ることなく画面の外に飛び出してしまいます。
壁の役目はボールを跳ね返すことです。そのためには、 RigidBody2D と BoxCollider2D というコンポーネントを付加し、物理的な衝突を行えるようにしておきます。
教材サンプルでは、ボールとぶつかったら効果音を出すために Wall.cs というスクリプトをコンポーネントとして付加しています。
Wall.cs スクリプトで使用するデータは図のように [Inspector] タブで設定ができます。この場合は音声ファイルをドラッグ&ドロップして効果音のフィールドに設定しています。
ボールとぶつかったことは、イベントで教えてもらえます。Collider2D 系のコンポーネントを付加すると、 OnCollisionEnter2D などのイベント関数が衝突時に呼び出されます。あとは、そのイベントの中に音を出す処理を追加すれば Wall.cs スクリプトの完成です。
バーを動かす
バーは、ブロック崩しでプレイヤーが操作する対象となります。
用意しておいたバーの画像をシーンに表示するところから、横移動し、ボールをはじき返すまでを説明します。
バーのスプライトをシーンに追加する
まずは、バーのゲームオブジェクトをシーンに配置してみましょう。
Bar.png を [project] タブから [Scene] タブへドラッグ&ドロップするだけで、バーの画像を表示するゲームオブジェクトがシーンに配置できます。
バーに付加するコンポーネント
バーは移動したり、ボールをはじき返す必要があるので、物理演算の Rigidbody2D、衝突判定の BoxCollider2D コンポーネントを付加しておきます。
独自の Bar.cs コンポーネントは、タッチされた場所に向かって横移動したり、ボールの方向を変更する処理を実装しています。
入力された位置に移動させる
バーの位置とタッチされた位置は座標系が違うので、どちらかに合わせる必要があります。今回はゲームオブジェクト中心で処理をするため、タッチされた位置(スクリーン座標系)をワールド座標系に変換してから、位置関係を比較します。
参照:UnityのCameraが使う3つの座標系 - テラシュールブログ
移動も、いきなり瞬間移動するのではなく、決めておいたバーの最大速度の範囲で少しずつ入力地点に移動するようにします。
ぶつかったバーの位置で角度を変える
ボールはぶつかったバーの位置で角度を変えます。
これは物理的に不自然なので、 Unity の物理エンジンまかせにはできません。
そのため、 Ball.cs の衝突イベントで、ボールの方向を強制的に変更しています。衝突イベントでは、どの位置に衝突したかも知ることができます。それをもとに、どの程度の角度に修正するかを計算し、ボールに設定しています。
ボールを動かす
ボールは反射しながら一定速度で進みます。
Unity の物理エンジンを利用しつつ、ゲームならではの物理的に不自然な処理は独自のコンポーネントで制御します。
ボールにつけるコンポーネント
ボールは壁などで反射させるため、Rigidbody2D と CircleCollider2D コンポーネントを付加します。衝突を検知する Collider2D には Box の他にさまざまな形があるので、表示する形状に適したものを選びましょう。
独自の Ball.cs では、ボールを等速で移動させる処理や、移動方向の制御を行っています。
最初はタッチした方向へ移動させる
ボールは最初止まっています。今回はフラグによって、止まっている状態と動いている状態を制御しています。
プレイ開始と同時に、他のスクリプトからボールに動くように指示が送られます。このときに、最初の移動方向も設定されます。
設定する移動方向は、ボールとタッチされた座標との成す角度になります。三角関数の苦手な方でも、 Mathf.Atan2 や Vector2.Angle という便利な関数に座標を入れるだけで角度を算出できます。
反射は Unity の物理演算にまかせる
壁などにぶつかったら、角度をどのように変えればよいでしょうか。
Unity ならば Rigidbody2D と Collider2D により、自動的に衝突したあとの角度を計算し、方向を変えてくれます。
等速移動をさせる
ボールは一定の速度で動きます。今回はボールの移動を Rigidbody2D の velocity という変数の操作で制御します。
velocity は移動するための加速度のベクトルです。角度は変更せず、毎回同じ移動量にすることで、一定の速度を保ちます。具体的には、 Vector2.Normailze で移動量が1になる単位ベクトルを作り、一定の移動量をかけてあげます。
ブロックを配置する
今回作るブロック崩しでは、ブロックにレベルを持たせることで、崩せるまでの回数にパターンを設けています。レベルごとに初期の HPが異なっており、ボールがブロックにぶつかる度にHP が 1 減少し、HPが0 になったらそのブロックは消滅します。
ブロックに付加するコンポーネント
ブロックは、ボールと衝突する必要があるので、 Rigidbody2D と BoxCollider2D コンポーネントを付加します。
独自の Block.cs コンポーネントでは、 HP を持ち、ボールがぶつかる度に HP を減らしていき、HP が 0 になったら自身のゲームオブジェクトを削除します。また、壁と同様にボールがぶつかったら、効果音を再生します。
プレハブを用意する
ブロックはたくさん配置します。1つ1つ Block.png をドラッグ&ドロップして、コンポーネントを設定するのはとても面倒です。それに、何か修正する場合、配置したブロックの数だけ修正しなければいけなくなります。
そんなときに便利な機能がプレハブです。プレハブがあれば簡単に複製が作れるだけではなく、修正も簡単に反映できます。
プレハブの作り方は簡単で、[Scene] タブの作った Blocks ゲームオブジェクトを [Project] タブにドラッグ&ドロップするだけです。
Snap Settings で簡単にきれいに配置する
作成したブロックのプレハブを Scene タブにドラッグ&ドロップすると、簡単に複製がシーンに配置されます。
このとき、ブロックを一定間隔できっちり並べたくなります。そんなときは、メニュー[Edit]→[Snap Settings…]を選択して、 [Snap Settings] ダイアログを表示しましょう。
Move X, Move Y に、整列させたいグリッドの間隔を指定します。スクリーンショットで設定されている値は、ブロック1個分の幅をワールド座標系に置き換えたものです。画面解像度とカメラのサイズから、1ピクセル分のワールド座標系の幅を求めれば、ブロックのピクセル幅をかけるだけで求めることが出来ます。
あとは、整列させたいブロックを選択して、 [Snap Settings] ダイアログの [X]、[Y]ボタンをクリックすれば、指定されたグリッド幅で整列します。
また、 ctrl キーを押しながらブロックをマウスで移動させると、設定した幅を単位として移動させることができます。
Snap Settings は、教材サンプルからオリジナルのステージを作る際にも便利な機能なので覚えておくと良いでしょう。
ブロックは1つのゲームオブジェクトの下にまとめておく
Snap Settings で紹介した画像の [Hierarchy] タブを見ると、 Blocks というゲームオブジェクトの下に複数のブロックが連なっているのがわかります。
これは、ゲームクリアの判定処理のための配置です。Blocks というゲームオブジェクトは空のゲームオブジェクトで、Hierarchy 上のポップアップメニューの [Create Empty] で作ることができます。
ゲームの進行を管理するスクリプトの中で、 Blocks の子要素の数を常にチェックし、子要素が 0 になった瞬間にゲームクリアの処理を呼び出すようにしています。
ゲームオーバー領域を配置する
ゲームオーバー領域はこれまでのゲームオブジェクトと少し違います。ゲームオーバー領域は壁やブロックと違いボールと衝突することはありません。あくまで、ボールが画面の下に落ちたことを判定するためのものです。
ゲームオーバー領域につけるコンポーネント
ブロックなどと同じく、 Rigidbody2D と BoxCollider2D コンポーネントを付加します。これは、衝突を起こすためではなく、当たり判定のイベントを呼び出してもらうためです。そのため、ブロックなどとは異なる Collider2D の設定をします。
[Is Trigger] というフィールドにチェックをいれます。そうすると、衝突は検知せず、Rigidbody2D でも衝突の物理演算をしません。しかし、領域に接触したことは OnTriggerEnter2D イベントの呼び出しで通知されます。
また、独自の GameoverArea.cs コンポーネントでは、 OnTriggerEnter2D でボールと接触したことを知ったら、ゲームの進行役に対してゲームオーバーになったことを伝えます。
次回予告
かなり長くなってきましたので、今回はここまでです。
次回はゲームの進行に必要となるさまざまな画面や機能について説明しながら、ひとまずアプリ内課金のないブロック崩しを完成させたいと思います。
間を空けず公開する予定ですので、少しお待ちください。
それでは、次回をお楽しみに。
文・開発:さくらくらうど(@SakuraCrowd)
個人でゲームを開発しています。アプリ内課金については初心者ですが、itemstoreを利用することで簡単に課金ゲームアプリが作れることを、実際の開発を通して伝えられたら良いなと思っています。
Blog : http://sakuracrowd.blogspot.jp/
Twitter : https://twitter.com/SakuraCrowd