Unityで2Dブロック崩しを作ろう 1

ブロック崩しを作ろう

Unityの勉強のためのゲーム制作第2弾を進めます。作るのはブロック崩しです。自機のバーを動かしてボールを弾き、ブロックを全て崩したらクリア、みたいなゲームです。

作りとしてはシンプルですが、ボールを跳ね返す処理などを物理演算で行うので、色々と学べます。

参考サイト

上記サイトがまさに2Dのブロック崩しを扱っています。色々と詳しく書いているので、かなり参考になりました。基本的な部分は上記サイトの手順に倣っています。

完成予定のゲームとソース

完成するゲームは以下のページで遊べます。

Unity WebGL Player | BlockBreak_dev_rx

ソースはGitHub上にあります。

gamegame-game/BlockBreak
Unity Game, BlockBreak. Contribute to gamegame-game/BlockBreak development by creating an account on GitHub.

ブロック崩しゲーム

ブロック崩しゲームですが、ざっくりとですが以下のように作ります。

  • 横640x縦960の画面サイズ
  • ボールの跳ね返りに物理演算(Rigidbody2D)を使う
  • UniRxを使う
  • ブロックやボール、壁はUIのImageを使う

処理シーケンス

以下の図のような処理シーケンスで開発します。

GameManagerクラスが中心となってゲーム全体の処理を進めることとし、他のクラスからGameManagerクラスを参照したりせず、できるだけ処理の責務を切り分けられるようにしたいと思います。

「ブロックが壊されたらスコアを加算して表示を更新する」という処理を考えるとき、Blockクラスのイベントでスコアを加算したりせず、GameManagerクラスがBlockクラスを監視して、壊れた時にGameManagerクラスがスコアを更新するようにします。

そのほか、すべてのブロックが壊れたらクリア画面に遷移しますが、これも同様にブロックがすべて壊れる状態になるまで監視し、処理を行います。

あとはゲームオーバーの画面も用意するので、地面にボールがぶつかるという状態になるまで監視し、そのタイミングでゲームオーバー画面に遷移するようにします。

これら値やイベントを監視して処理を行うためにUniRxを使用します。

UniRxとは

UniRxというオープンソースのライブラリを使用して開発します。UniRxは、Reactive Extensions(Rx) という非同期処理向けのライブラリのUnity版です。デザインパターンの一つ、Observerパターンを中心に作られています。

いうまでもなく、ゲームは非同期処理の塊です。したがってRxとの相性も抜群です。

これを使うと、時間やタイミングが重要な処理(つまり色々な条件下でのイベント処理)が簡潔に記述できます。

もちろん使わなくても作れますが、規模が大きくなったりするとコードの収拾がつかなくなるので、できる限り簡潔に実装できるようにUniRxを利用します。

だいたいはざっと以下のページで予習しました。

UniRx入門 その1

目標

出来るだけ処理の実装がややこしくならないように心がけます。

プロジェクト作成とディレクトリ構成

今回は2Dゲームなので、2Dプロジェクトとして作成します。

まずはファイルを整理するためのディレクトリ構成を作ってしまいます。以下のようにします。

  • PhysicsMaterials
  • Prefabs
  • Scenes
  • Scripts

それぞれディレクトリ名通りのファイルを配置するようにします。

シーンの作成と保存

ひとまずここでシーンを保存しておきます。名前を GameScene とします。ゲームを遊ぶためのシーンです。保存場所はもちろん /Scenes です。

そのほかのシーンもひとまず作成しておきます。ゲームクリア用のシーンとゲームオーバー用のシーンです。名前をそれぞれ GameClearScene, GameOverScene とします。

新しいシーンは File -> New Scene から作成できます。

ビルド設定等もろもろ

File -> Build Settings からビルド設定画面を開きます。

Scenes In Build

作成したシーン3つを Scenes In Build へD&Dで追加してください。先頭に GameScene が来るように並べ替えます。これで起動時にロードされるシーンとなります。

適当な Platform を選択して Switch Platform ボタンをクリックしてください。

Player Settings

Player Settings ボタンをクリックして設定画面を表示します。基本的に .NET 4.6 で開発したいので、その設定をします。設定方法は以下のページから。

Unity2017 で .NET 4.6 (C# 6.0) を使う方法

画面レイアウト、Prefabの作成

まずはGameSceneの構成要素を洗い出します。

  • 壁(上、右、左に配置。ボールが跳ね返る)
  • ボール(壁、ブロックに当たると跳ね返る。ブロックを壊す)
  • ブロック(ボールがぶつかると壊れる)
  • 自機(ユーザーが操作する棒。ボールを跳ね返す)
  • 地面(画面外の判定に使う見えないオブジェクト)
  • スコア表示用テキスト

上記の項目を作成していきます。まずは以下のような画面を作ってみましょう。

  • 黄色が壁
  • 青色がスコア
  • 赤色が自機のバー
  • 緑色がボール
  • 見えない地面を画面下に配置

各色に意味はありませんので適宜変更して構いません。

Canvasの作成

ブロックや壁は全てuGUIで作成するのでまずはそれらを配置するためのCanvasを作成します。

Hierarchy -> Create -> UI -> Canvas

作成したらインスペクターから表示サイズの設定を行います。横640x縦960で表示させるために、UIを縦横比に合わせてスケールするようにします。

ここでは画面の高さをに合わせて画面がスケールするように次のようにします。

UI Scale Mode

UI Scale Mode の設定値を Scale Width Screen Size とします。これでスクリーンサイズに合わせてUIをスケールさせることができます。

Reference Resolution に画面サイズを設定します。このサイズが基準となります。

Match を Height 1 として画面の高さに合わせてスケールするようにします。

このあたりの設定値の意味は公式マニュアルを参考にするとよいでしょう。そのほか参考URLを以下にまとめておきます。

ゲームエリアと壁の作成

まずは各種ゲーム用のオブジェクト(UI)を配置するための空のオブジェクトを作成し、名前を GameArea とします。

左右と上に位置する壁を作成します。以下画面の要素を作成していきますが、基準となるアンカーを設定し、相対位置で要素のポジションを決定しているという点が重要です。

まずは Walls という名前の空のオブジェクトを GameArea の子として作成します。Walls の子として Top Left Right という名前の Image(Hierarchy -> Create -> UI -> Image) をそれぞれ作成します。

GameArea

GameAreaのサイズを640*960に設定します。

Walls

Walls は空のオブジェクトですが、これは親のGameAreaいっぱいとなるように Anchor Presets を Stretch に設定します。こうすることで親のサイズに合わせて相対的にサイズが設定されます。

Top

上辺の壁を作成します。Anchor Presets を Top – Stretch とすることで Walls の上部からの相対位置を設定できます。さらに 横のサイズを Stretch としているので、Walls の横に目一杯伸びて表示されるはずです。

壁の幅は40とします。PosY=0でとすると上半分がはみ出るので-20としています。

あと色を見やすく変えておくといいかもしれません。

LeftとRight

左右の壁もTopと同じように設定します。Anchor Presets をきっちり設定していると、Walls のサイズが変わってもはみ出たり崩れず対応されます。

以下のようになっていればOKです。色は適当です

Box Cllider 2D の追加

壁はボールを跳ね返す必要があるのであたり判定を発生させる必要があります。Top, Left, Right それぞれに Box Collider 2D を追加します。

Inspector -> Add Component -> Physics 2D -> Box Collider 2D

これは Top の Collider です。サイズを壁のサイズに合わせています。

Left, Right の Collider も以下のように設定します。左右の壁のサイズは同じなので設定値に違いはありません。

 ボールの作成

次にボールを作成します。ボールですが実態は四角い画像です。ということで GameArea の子に Ball という名前の Image を作成します。

GameArea右クリック -> UI -> Image

30*30 のサイズにしておきます。画面の中央にポツンと四角形が表示されます。

これを動かすには Rigidbody と Collider を追加しなければなりません。

Rigidbody 2D

Inspector -> Add Component -> Physics 2D -> Rigidbody 2D

Rigidbody2D を追加して Gravity Scale を0としておきます。Rigidbody は物理運動をシミュレートしてくれますが、Gravity Scale を1のままにしておくと重力がボールに働いで自由落下が始まってしまいます。

今回のゲームでは重力の影響で落下したりする必要はないので重力0の設定をしています。

Circle Collider

あたり判定用の Collider を追加したら、ボールを動かす準備は完了です。

Radiusをいい感じに設定します。円形の Collider の半径にあたる設計値なので、ボールの1辺の半分の値(15)を設定します。

あとはプログラム側から適当に力を加えると動き出し、壁にぶつかるといい感じに跳ね返るという算段です。

Ball.cs

ということで、スクリプトでボールを動かします。

using UnityEngine;

public class Ball : MonoBehaviour
{
    private void Start()
    {
        // このオブジェクトのRigidbody
        var body = gameObject.GetComponent<Rigidbody2D>();

        // 親のCanvas
        var canvas = GetComponentInParent<Canvas>();

        // 右上の方向に一定の強さで飛んでいくように
        var direction = new Vector2(Random.value, 1).normalized;
        body.velocity = direction * 640 * canvas.transform.localScale.x;
    }
}

/Scripts 以下にBall.csを作成します。内容は上の通り。Startメソッドでボールに力を与えて動かします。このスクリプトは Ball オブジェクトに貼り付けます。

コード自体はそれほど難しいことはしていません。Vector2で飛んでいく方向を決めています。normalizedをすることで、大きさが常に1になるようにしています。

body.velocity がボールに力を加えている箇所です。direction の大きさが1なので640をかけているところがこの力の大きさになります。

最後の Canvas の localScale を掛けているのは、画面がスケールされている場合には相対的に速度が変化してしまう問題に対応しています。同じ速度でも表示領域が小さいと相対的に早くボールが動いているのでゲーム性が全く変わってしまいます。これは参考サイトに詳しくあるので確認するとよいかと思います。

PhysicsMaterial

ゲームを動かしてみると、ボールが動き出し壁にぶつかってくれるのですが、壁に張り付いて跳ね返ってくれません。これは余計な摩擦や跳ね返りの際の減衰処理が働くせいです。

現実世界だと壁にぶつけたボールがぶつけたときのスピードのまま跳ね返るようなことはなく、いくらか勢いが落ちますが、それを再現されています。

今回のゲームでは跳ね返りでは速度が常に一定に保たれてほしいので、摩擦などの影響を受けないようにしなければなりません。

ここで登場するのが PhysicsMaterial 2D です。2D 物理オブジェクト間の衝突時の摩擦や弾性を調整します。

/PhysicsMaterials 以下に 右クリック -> Create -> Physics Material 2D から作成します。名前を BallPhysicsMaterial とします。

Inspector を確認すると、Friction(摩擦係数)とBounciness(反射係数)の設定値が確認できます。

Unity – マニュアル: Physic Material 2D

それぞれ、マニュアルによると以下のような意味があります。

Friction

コライダーの摩擦係数。

Bounciness

衝突が表面から弾むときの強さ。値が 0 の場合はまったく弾まず、値が 1 の場合はエネルギー損失なく弾みます。

それぞれ次のように設定するとスピードが減ることなく、きれいに跳ね返るようになります。摩擦ゼロ、跳ね返るときの減衰ゼロです。

作成したらBallへのアタッチを忘れずに行います。これは Collider に設定します。

ドラッグ&ドロップもしくは右にあるまるポチから設定します。

これでボールの跳ね返りを確認できるはずです。

自機のバー

ボールが落ちないように跳ね返すための自機、バーを作成します。

バーはカーソル(矢印)キーで操作することとします。

Barの作成

GameArea 以下に Image を作成します。ボールにぶつかる必要があるので Collider もつけます。サイズを 100*40 とし、Collider もそれに合わせます。

Bar.cs

自機を動かすためのスクリプトを作成します。

using UnityEngine;

public class Bar : MonoBehaviour
{
    public Rigidbody2D Body { get; private set; }
    public RectTransform RectTransform { get; private set; }

    void Awake()
    {
        this.Body = gameObject.GetComponent<Rigidbody2D>();
        this.RectTransform = gameObject.GetComponent<RectTransform>();
    }

    void Update()
    {
        // 左右ボタンが押下されているとバーを移動させる
        // ただし動かせる範囲からはみ出ないように調整
        var currentX = this.RectTransform.localPosition.x;
        if (Input.GetKey(KeyCode.RightArrow))
        {
            // 右に15動かす
            // ただし230以上なら230で止める
            var newX = Mathf.Min(currentX + 15, 230);
            var pos = new Vector3(newX, this.RectTransform.localPosition.y, 0);
            this.RectTransform.localPosition = pos;
        }
        else if (Input.GetKey(KeyCode.LeftArrow))
        {
            // 左に15動かす
            // ただし-230以下なら-230で止める
            var newX = Mathf.Max(currentX - 15, -230);
            var pos = new Vector3(newX, this.RectTransform.localPosition.y, 0);
            this.RectTransform.localPosition = pos;
        }
    }
}

Input.GetKey(KeyCode.RightArrow)でカーソルキー押下の判定を取得しています。その際にlocalPositionに新しい値を設定して移動させています。

一回押下するごとに15ずつ動かすようにしていますが、移動先が端の値(右230, 左-230)からはみ出る場合は、端の値で止めるようにしています。それが Mathf.Max(Min)で調整している箇所です。

これであらぬ位置まで動いてしまうことがなくなります。

まとめ

  • ゲームエリアに壁、ボール、自機バーを作成しました。
  • ボールが跳ね返るように Rigidbody, Collider, PhysicsMaterial を設定しました。
  • 実機バーを動かせるようにしました。

次回予定

とりあえず、今回はここまで。次回はUniRxで諸々の処理を書いていく予定です。

  • ブロックの配置
  • ブロックが壊れる処理

以上。

リンク

参考URL

コメント