初心者でもできる! 7日間で作るUnityゲーム開発

Day4:タイムトライアル機能を追加する!

タイムトライアル機能を追加してゲーム性を強化

前回の記事では、爆発エフェクトと爆発音を追加したことによってゲームの臨場感が増しました。臨場感は増しましたが、弾が当たったら爆発するだけではゲーム性がありません。そこで今回はタイムトライアル機能を追加してゲーム性を強化していきたいと思います。

不要なラベル表示を消す

現状ゲームを実行すると、画面左上に白い文字で

Try to kill the bear!
Use Fire1 to shoot bullets
Use Fire2 to toggle aiming with bazooka

と表示されます。

クマを殺せ!
Fire1ボタンで弾を撃て
Fire2ボタンでバズーカを構えろ

という意味の英語です。不要なので削除します。

さて、これらの文字はどのコードで表示しているのでしょうか? 自分が書いたコードなら、どこで何をやっているかがわかりますが、アセットストアのアセットのように他人が書いたコードの場合、どうすればよいでしょうか?

私ならこういう場合、IDEの検索機能を使います。MonoDevelopのメニュー[Search]ー[Find in Files]をクリックします。表示されたダイアログの「Find:」「Try to kill the bear」を入力して[Find]ボタンをクリックします。すると、Bazooka.csのコードが検索にヒットするので対象のコードを発見することができます。

このように、対象の処理をどこでやっているか分からない時にはIDEでソースコードを検索することをおすすめします。

Bazooka.csの以下の部分でラベル表示しているので、このコードを削除しましょう。

    void OnGUI()
    {
        GUILayout.Label("Try to kill the bear!");
        GUILayout.Label("Use Fire1 to shoot bullets");
        GUILayout.Label("Use Fire2 to toggle aiming with bazooka");
    }

Unityの標準UI機能では、OnGUIというメソッドの中にラベルやボタンなどのUIを表示するコードを書くルールになっています。

GUILayout.Labelメソッドの引数にラベル表示したい文字列を渡すと画面にラベルが表示される仕組みとなっています。

スコアカウント処理を追加する

GameControl.csのGameControlクラスに以下のフィールドを追加します。

    int score = 0;

続けて以下のコードを追加します。

    public void AddScore() {
        score++;
    }

    bool FinishedGame() {
        return GameObject.FindWithTag("Teddy") == null;
    }

AddScoreメソッドではフィールド定義したscoreを1加算しています。

FinishedGameメソッドはゲームが終了したかの判定をしています。GameObject.FindWithTag("Teddy") == null;でTeddyタグの付いているオブジェクトが一つもない場合trueを返しています。つまり、クマをすべて打ち終わったらゲーム終了という判定になります。

続いて、Bear.csのBearクラスのOnCollisionEnterメソッドを以下のように編集します。コメント行「今回追加」以下の2行を追加してください。

    void OnCollisionEnter(Collision collision) {
        if (avatar != null) {
            if (hitFlag == false && collision.collider.tag == "Bullet") {
                hitFlag = true;
                GameObject exp = (GameObject)Instantiate(detonator.gameObject, transform.position, Quaternion.identity);

                //今回追加
                var gameCotroll = GameObject.Find("GameControl");
                gameCotroll.transform.GetComponent<GameControl>().AddScore();
                
                var currentState = avatar.GetCurrentAnimatorStateInfo(0);
                var nextState = avatar.GetNextAnimatorStateInfo(0);
                if (!currentState.IsName("Base Layer.Dying") && !nextState.IsName("Base Layer.Dying")) {
                    avatar.SetBool("Dying", true);
                    Destroy(this.gameObject, 3.0f);
                }
            }
        }
    }

OnCollisionEnterはクマが何かと接触した際に呼び出されるメソッドです。今回追加した2行で、弾と接触時にGameControlのAddScoreメソッド呼び出しをしています。

var gameCotroll = GameObject.Find("GameControl");でHierarchyのGameControlオブジェクトを取得して、取得したGameControlにアタッチされているGameControlスクリプトをgameCotroll.transform.GetComponent<GameControl>().AddScore();で取得してAddScoreメソッドを呼び出しています。

タイム測定処理を追加する

GameControlクラスに以下のフィールドを追加します。

    float elapsedTime;

続けてUpdateメソッドを以下のように編集します。

    void Update () {
        if (!FinishedGame()) {
            elapsedTime += Time.deltaTime;
        }
    }

Updateメソッドは毎フレームごとに呼び出されるメソッドです。フレームとは画面を描画する単位です。fps(frame per second)という単位があって、1秒間に何回画面を描画するかを表します。30fpsだったら1秒間に30回描画処理が走るので、Updateメソッドは1秒間に30回呼び出されることになります。なので、Updateメソッドは1秒間にたくさん呼び出されるものだと覚えておいてください。そのため、Updateメソッドの中に重い処理を書くとゲームの動作が重たくなってしまうので、そうならないよう注意しましょう!

今回追加したコードはif (!FinishedGame()) {でゲームが終了していない場合、elapsedTime += Time.deltaTime;で経過時間を加算しています。Time.deltaTime;は前のフレームから現在のフレームまでに経過した時間を返します。これをUpdateメソッドの中で毎回加算していくことで、ゲーム内での経過時間を計測しています。

UI処理を追加する

GameControlクラスに以下のフィールドを追加します。

    const float BASE_WIDTH = 511;
    public GUIStyle style = new GUIStyle();
    public GUIStyle endMsgStyle = new GUIStyle();
    Rect scoreRect = new Rect(400, 10, 70, 30);
    Rect timerRect = new Rect(350, 50, 120, 30);
    Rect endMsgRect = new Rect(50, 10, 200, 35);
    Rect rankMsgRect = new Rect(50, 85, 250, 30);
    Rect replayBtnRect = new Rect(50, 150, 120, 40);

続けて以下のコードを追加します。

    float GetValueByScreenSize(float x) {
        float ratio = Screen.width / BASE_WIDTH;
        return x * ratio;
    }

    Rect MakeRect(Rect defaultRect) {
        float left = GetValueByScreenSize(defaultRect.left);
        float top = GetValueByScreenSize(defaultRect.y);
        float width = GetValueByScreenSize(defaultRect.width);
        float height = GetValueByScreenSize(defaultRect.height);
        Rect newRect = new Rect(left, top, width, height);
        return newRect;
    }

    void OnGUI() {
        GUI.Label(scoreRect, "スコア " + score, style);
        GUI.Label(timerRect, "タイム " + elapsedTime, style);
        if (FinishedGame()) {
            GUI.Label(endMsgRect, "Game Clear!!", endMsgStyle);
            GUI.Label(rankMsgRect, "あなたのタイムは" + elapsedTime + "です!", style);
            if (GUI.Button(replayBtnRect, "もう一度プレイする")) {
                Application.LoadLevel("Teddy Bear Bazooka");
            }
        }
    }

MakeRectメソッドで使用しているGetValueByScreenSizeメソッドでは、引数で指定された現在のスクリーンサイズと既定のスクリーンサイズ(横幅511)の比を掛けた値を返しています。この計算はゲームの画面サイズを変更した際のレイアウト合わせに使用します。既定の横幅を511にした理由は私がUIのレイアウトを調整した際のゲーム画面の幅が511であっただけなので、いくつを基準にしてもかまいません。

MakeRectメソッドでは引数で受け取ったRectのサイズをGetValueByScreenSizeを使って実際の画面の大きさに合わせて変更しています。

OnGUIメソッドで経過時間と現在のスコアの表示、ゲームクリア時のメッセージの表示ともう一度プレイするボタンの制御をしています。

GUI.Label(scoreRect, "スコア " + score, style);でスコアの表示をしています。GUI.Labelメソッドの第一引数でラベル表示する位置と大きさを指定しています。第一引数の型Rectはleft(左からの位置), top(上からの位置), width(横幅), height(高さ)を持っています。

第二引数ではラベルに表示する文字列を指定します。第三引数ではラベルの表示スタイルを指定します。文字の大きさやフォント、色などを指定できます。今回はstyleをpublicフィールドにしたのでUnityのInspector画面からスタイル情報を指定します。この操作は後述します。

if (FinishedGame()) {でゲーム終了時の判定をして、if (GUI.Button(replayBtnRect, "もう一度Playする")) {「もう一度プレイする」ボタンを表示しています。ボタン押下時にはGUI.Buttonがtrueを返すので、このif文の中のApplication.LoadLevel("Teddy Bear Bazooka");が実行され、Teddy Bear Bazookaシーンが読み込まれてゲームが最初から始まります。

最終的にGemeControl.csは以下のようになりました。正しく編集できたか確認してください。

using UnityEngine;
using System.Collections;

public class GameControl : MonoBehaviour {
    public GameObject bear;
    int score = 0;
    float elapsedTime;
    const float BASE_WIDTH = 511;
    public GUIStyle style = new GUIStyle();
    public GUIStyle endMsgStyle = new GUIStyle();
    Rect scoreRect = new Rect(400, 10, 70, 30);
    Rect timerRect = new Rect(350, 50, 120, 30);
    Rect endMsgRect = new Rect(50, 10, 200, 35);
    Rect rankMsgRect = new Rect(50, 85, 250, 30);
    Rect replayBtnRect = new Rect(50, 150, 120, 40);

        void Start () {
        for (int i = 0; i < 9; i++) {
            Instantiate(bear);
        }

        scoreRect = MakeRect(scoreRect);
        timerRect = MakeRect(timerRect);
        endMsgRect = MakeRect(endMsgRect);
        rankMsgRect = MakeRect(rankMsgRect);
        replayBtnRect = MakeRect(replayBtnRect);
    }

        void Update () {
        if (!FinishedGame()) {
            elapsedTime += Time.deltaTime;
        }
    }
    
    public void AddScore() {
        score++;
    }

    bool FinishedGame() {
        return GameObject.FindWithTag("Teddy") == null;
    }

    float GetValueByScreenSize(float x) {
        float ratio = Screen.width / BASE_WIDTH;
        return x * ratio;
    }

    Rect MakeRect(Rect defaultRect) {
        float left = GetValueByScreenSize(defaultRect.left);
        float top = GetValueByScreenSize(defaultRect.y);
        float width = GetValueByScreenSize(defaultRect.width);
        float height = GetValueByScreenSize(defaultRect.height);
        Rect newRect = new Rect(left, top, width, height);
        return newRect;
    }

    void OnGUI() {
        GUI.Label(scoreRect, "スコア " + score, style);
        GUI.Label(timerRect, "タイム " + elapsedTime, style);
        if (FinishedGame()) {
            GUI.Label(endMsgRect, "Game Clear!!", endMsgStyle);
            GUI.Label(rankMsgRect, "あなたのタイムは" + elapsedTime + "です!", style);
            if (GUI.Button(replayBtnRect, "もう一度プレイする")) {
                Application.LoadLevel("Teddy Bear Bazooka");
            }
        }
    }
}

ラベルの表示スタイルを設定する

続いて、Unityに戻ってHierarchyのGameControlをクリックします。Inspectorの「GameControl (Script)」「Style」ー「Normal」ー「BackGround」の横の丸いアイコンをクリックします図1⁠。

図1
図1

Select Texture2Dダイアログが表示されるので、一番上の左から2番目の「body normal」をダブルクリックします図2⁠。

図2
図2

これはラベルの背景に使う画像を指定です。薄い色の背景画像を指定することで文字を読みやすくしています。

続いて、⁠Background」の下にある「Text Color」の横のカラーボックスをクリックします図3⁠。

図3
図3

Colorダイアログが表示されるので、⁠Colors」の左上端の白い部分をクリックして色を変更して×ボタンでダイアログを閉じます図4⁠。

図4
図4

これでラベルの文字が白くなります。

続いて「Style」ー「Font」の丸いアイコンをクリックします。

図5
図5

Select Fontダイアログが表示されるので、⁠Arial」をダブルクリックします。

図6
図6

これでラベルのフォントが設定されました。

続いて「Font」の下にある「Font Size」「30」を入力します。

図7
図7

これでラベルのフォントサイズが30となります。

続いて「GameControl (Script)」「End Msg Rect」もStyleと同様に指定します。End Msg Rectはゲームクリア時に表示される「Game Clear!!」と表示されるラベルの表示スタイルです。⁠Text Color」で白以外の好きな色を選んでください。⁠Font Size」「60」を入力してください。それ以外はStyleで指定したのと同様にしてください。

以上で終了です。ゲームを実行してみましょう!ラベルやボタンが表示されるようになったことを確認してください。

図8
図8
図9
図9 ゲームクリア時の画面

いかがだったでしょうか? 時間を競うという要素が加わったのでゲームとしての面白さが出てきたかと思います。速いタイムでクリアできるようチャレンジしてみてください。


次回はプロ生ちゃんの音声を追加してゲームに華やかさを加えていきたいと思います。プロ生ちゃんの音声は声優の上坂すみれさんが担当されています。プロの声優さんの音声を加えることでゲームの雰囲気がぐっと華やかになります。お楽しみに!

おすすめ記事

記事・ニュース一覧