【Unity】2Dシューティングゲームの作り方⑬:ボスと各シーンの作成

Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、Unityを使った2Dシューティングゲームの作り方の13回目として、ボスキャラクターの作成、そして起動画面やクリア画面など各シーンの作成を行っていきます。

前回、ゲーム内でBGMや効果音を流したり、爆発するエフェクトを作成していきました。

ここまででゲームとしての仕組み部分ができたので、今回はゲームクリアのためのボスを作っていきます。

ボスは、これまでの敵と同じように作成していきますが、アニメーションの機能を活用してレーザービームを撃てるようにしていきます。

また、現状メインシーンとリトライシーンしか作成していなかったり、ゲーム内で想定外の挙動が行われてしまっている部分があり、その修正が必要となっています。

そこで、ゲームクリアのためのボスの作成と、各シーンの作成ゲーム内のバクなどの調整までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

ボスを作成する

まずは、ゲームのクリアに繋がるボスを作成していきます。

通常の敵と同じように作成していく

ボスの作成は、これまで作成した敵と同じような仕組みで作っていきます。

まず、ステージの一番最後の部分にボスを配置しタグで「Enemy」を設定、ステージを進めていくとボスが現れるように「stage」オブジェクトの配下に設置しておきます。

ゲーム画面にボスが登場したらステージが移動しなくなるようにしたいので、「StageController」のスクリプトを修正しておきます。

using UnityEngine;

public class StageController : MonoBehaviour
{
    public float stageSpeed = 0.03f;
    public GameObject[] startPos;

    void Start()
    {
        transform.Translate(0, -startPos[GameManager.instance.startPos].transform.position.y, 0);
    }

    void Update()
    {
        if (transform.position.y > -110.8f) // ボスが表示された際の座標
        {
            transform.Translate(0, -stageSpeed, 0);
        }
    }
}

ステージを移動させているTranslateメソッドの処理に対して、15行目でY軸の座標の条件を指定してあげることで、以下のようにボスが出てきたらステージが止まるという処理にしています。

ボスが弾を発射する仕組みや動きなどは、通常の敵と同じように作っていて、基本的には弾を撃ちながら左右に動くという処理にしています。

このボスの動きは「BossController」というスクリプトで作っています。

using UnityEngine;

public class BossController : EnemyBase
{
    int direction = 1;  // 移動する向きを決める変数
    float bossSpeed = 0.05f;    // ボスのスピード

    int changeDirectionCount;   // 向きが変わった回数を取得

    void Update()
    {
        if (transform.position.y < 4.5f && changeDirectionCount < 3)    // ボスのY座標が4.5よりも小さい場合
        {
            transform.Translate(direction * bossSpeed, 0 ,0);   //  directionの向きに横移動する処理

            if (transform.position.x > 1.2f && direction == 1)  // X座標が1.2よりも大きい場合
            {
                direction = -1; // X軸をマイナス方向に進む
            }
            else if (transform.position.x < -1.2f && direction == -1)  // X座標が-1.2よりも小さい場合
            {
                direction = 1; // X軸をプラス方向に進む
                changeDirectionCount++; // 向きが変わった回数を計測する
            }
        }

        if (changeDirectionCount == 3)
        {
	    // レーザービームを発射する処理を作る
        }
    }
}

27行目以降に、後ほど紹介するレーザービームの発射処理を記述していきます。

また、ボスのレーザー以外の弾の発射は、これまでの「EnemyShot」で間隔を指定していたspandeltaの変数にpublic修飾子を付けて、2つの弾が交互に発射するように、空オブジェクトで発射位置を指定した後に、インスペクターウィンドウから値を設定しています。

using UnityEngine;

public class EnemyShoot : MonoBehaviour
{
    public GameObject enemyBullet;
    GameObject player;
    public float span = 2.0f;  // public修飾子を設定
    public float delta = 2.0f; // public修飾子を設定

    void Start()
    {
        player = GameObject.Find("player");
    }

    void Update()
    {
        if (player.activeSelf == true && 
            this.transform.position.y < 4.5f)
        {
            this.delta += Time.deltaTime;
            if (this.delta > this.span)
            {
                this.delta = 0;
                GameObject cloneObject = Instantiate(enemyBullet, this.transform.position, Quaternion.identity);
                cloneObject.name = "EnemyBullet";
            }
        }
    }
}

他にも、ボスにColliderコンポーネントをアタッチして、当たり判定を作っておきましょう。

敵の作り方に関しては、作り方の6回目と8回目の記事で詳しく解説しているので参考にしてみてください。

レーザービームをアニメーション機能で作成

ボスが3回左右に移動して向きを変えたら、レーザービームを発射するようにしていきます。

レーザービームを撃つ処理は、アニメーションの機能で作成していきます。

流れとしては、

  • ボスがレーザーをチャージする
  • レーザービームを発射する
  • 元の左右の動きに戻る

として、これを繰り返すようにアニメーションを使って動かしていきます。

最終的な目標としては、以下のような動きになります。

ボスがレーザーをチャージする

まずは、ボスのオブジェクト配下に、レーザービーム用の空オブジェクト「bossLaserPoint」を配置して、

この中にチャージ用の画像(bossLaserCharge)と、

実際に発射されるレーザーの画像(bossLaserShot)を分けて入れています。

なお、これらのレーザーに使っている画像は、UnityAssetStoreからダウンロードしたものを使っています。

そして、このbossLaserPointを選択した状態で、アニメーションファイルを作成していきます。

アニメーションファイルの作成方法などは、以下のページも参考にしてみてください。

アニメーションファイルが作成できたら、まず「Add Property」からレーザーのチャージ用の弾のScaleを追加、

そして、タイムラインの時間を選択した状態で、録画ボタンをクリックします。

これでシーンビューに戻り、レーザーのチャージのScaleの値を変更すると、タイムライン上にその変更された値がキーフレームで設定されていきます。

この作業を繰り返して、段々と弾が大きくなる動きにしてあげることで、ボスがチャージしているような動きを作ることができます。

レーザービームを発射する

チャージする動きを作れたら、今度はレーザービームを発射したいので、「Add Property」からレーザーのチャージ用の弾と実際のショット用の画像のアクティブ状態を切り替える「Is Active」、またレーザーが発射される画像のそれぞれのPositionScaleの値を追加していきます。

まず、チャージが終わったらチャージ弾を非アクティブにして、ショット用の画像をアクティブにしたいので、先ほどと同様にタイムラインを選択して、録画ボタンを押して値を変更するようにします。

次に、同じように録画ボタンを押して、最初はレーザーの上側と下側をくっつけて、間に光線部分が来るように、Scaleを変更して設定していきます。

あとは、レーザーが発射された状態を、タイムライン上でScaleとPositionの値が変わるように調整してあげます。

また、レーザーが発射されて、アニメーションの最後のキーフレームでは、ショット用の画像を非アクティブ、チャージ用の画像をアクティブにしておきます。

これで、以下のようにボスがチャージしたら、レーザービームを撃つという動きを作ることができました。

なお、レーザービームの画像にもColiderコンポーネントを付けて、プレイヤーへの当たり判定が行われるようにしておきます。

元の左右の動きに戻る

ボスがレーザービームを発射した後は、再度元の左右の動きを行うようにしていきます。

まず、ボスにアタッチしている「BossController」の中で、レーザーの発射と発射後の処理を記述していきます。

using UnityEngine;

public class BossController : EnemyBase
{
    int direction = 1;
    float bossSpeed = 0.05f;

    int changeDirectionCount; 
    public GameObject player;   // プレイヤーのオブジェクトを取得
    float playerPositionX; // プレイヤーのX座標を取得する
    float bossPositionX;  // ボスのX座標を取得する

    float span = 2.0f;  // レーザーをチャージする時間
    float delta;    // 時間を計測する変数
    public GameObject bossLaserPoint;   // レーザービームを発射するオブジェクト
    public GameObject bossBulletPoint;  // 弾を発射するオブジェクト
    public Animator laserAnimator;  // レーザービームのAnimatorコンポーネントを取得する変数

    void Update()
    {
 
        // 省略 //

        if (changeDirectionCount == 3)  // 3回向きが変わった場合
        {
            delta += Time.deltaTime;
            if (delta < span)
            {
                playerPositionX = player.transform.position.x;  // プレイヤーの座標を取得する
                bossPositionX = this.transform.position.x;  // 敵の座標を取得する
                transform.Translate((playerPositionX - bossPositionX) * bossSpeed, 0, 0); // プレイヤーに向かって敵が移動する処理
                bossLaserPoint.SetActive(true); // レーザーの発射位置をアクティブ化
                bossBulletPoint.SetActive(false);   // 通常の弾の発射位置を非アクティブ化
            }
            if (laserAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1)    // アニメーションの再生が終了した場合
            {
                bossLaserPoint.SetActive(false);    // レーザーの発射位置を非アクティブ化
                bossBulletPoint.SetActive(true);    // 通常の弾の発射位置を非アクティブ化
                changeDirectionCount = 0;   // 向きが変わった回数をリセット
                delta = 0;  // チャージ時間もリセット
            }
        }
    }

レーザーの発射は、32行目でbossLaserPointをSetActiveメソッドでアクティブ化することで、アニメーションを自動で再生させるようにしています。

そして、レーザーの発射後かどうかを判断するために、35行目の条件の中でnormalizedTimeという変数を使っています。

このnormalizedTimeは、現在再生されているステートでのアニメーションの再生時間を計測している変数で、アニメーションの最初の地点を「0」、最後の地点を「1」として値を返してくれるので、この値が1以上になったらアニメーションの再生が終了したと判断することができます。

また、normalizedTimeは、以下のように前にステートを指定する必要がありますが、

AnimationState.normalizedTime

GetCurrentAnimatorStateInfoメソッドを使うことで、現在再生されているステートを取得することができます。

アニメーションが終了したタイミングの中の37行目から40行目で、bossLaserPointを非アクティブにして、左右の移動カウントなどをリセットしてあげることで、元の左右の移動処理や通常の弾の発射が行われるようになります。

ボスを倒した際のエフェクトを設定

ボスのHPが0になったら、爆発のエフェクトを複数回行うという処理を作っていきます。

まず、BossControllerの中でプレイヤーの弾との当たり判定を作り、通常の敵と同じようにスコアを加算する処理などを行います。

using UnityEngine;

public class BossController : EnemyBase
{
    // 省略 //

    public GameObject clearEffect;  // ボスを倒した際のエフェクトオブジェクト
    bool isClearing = false;    // クリア後の処理を行ったかどうか判定する変数

    void Update()
    {
        // 省略 //
    }

    void OnTriggerEnter2D(Collider2D collision) // 当たり判定のあるオブジェクトと当たった場合
    {
        if (collision.gameObject.name == "PlayerBullet")   // 当たったオブジェクトがプレイヤーの弾の場合
        {
            hp--;   // HPを1減らす処理

            if (hp <= 0 && isClearing == false)    // HPが0になった場合
            {
                GameManager.instance.scorePoint += enemyPoint;  // スコアを加算する処理
                direction = 0;
                changeDirectionCount = 0;
                bossBulletPoint.SetActive(false);
                bossLaserPoint.SetActive(false);
                clearEffect.SetActive(true);    // クリア用のエフェクトをオンにする
                isClearing = true;  // クリア後の処理が終わったのでtrueに変更
            }
        }
    }
}

そして、28行目でclearEffectというオブジェクトをSetActiveでアクティブ状態にしてあげます。

このclearEffectは、ボス配下に設置している空オブジェクトで、前回作成した爆発エフェクトのスクリプトである「Explosion Effect」をアタッチしています。

using UnityEngine;

public class ExplosionEffect : MonoBehaviour
{
    public GameObject explosion; // 爆発のオブジェクトを入れておく変数を宣言

    float span = 0.2f;  // 爆発させる際の間隔を決める
    float delta;    // 時間の経過を計測する
    float count;    // 爆発の回数を計測する

    void Update()
    {
        delta += Time.deltaTime;    // フレーム間の時間を加算している

        if (this.gameObject.name == "clearEffect" && delta > span && count < 20)    // 爆発を繰り返す処理
        {
            GameObject instance = Instantiate(explosion);   // 爆発のオブジェクトを表示させる
            instance.transform.parent = this.gameObject.transform.parent;   // 親オブジェクトを指定する
            instance.transform.localPosition = new Vector2(Random.Range(-2.0f, 2.0f), Random.Range(-2.0f, 2.0f)); // 爆発のオブジェクトの位置をランダムに配置する
            Destroy(instance, 1.0f);    // 1秒後に破壊する
            delta = 0;  // 経過時間をリセット
            count++;    // 爆発回数を計測
        }

        if (count == 20)    // 20回爆発した場合
        {
            this.gameObject.transform.parent.gameObject.SetActive(false);   // 親オブジェクトのボス自体を非アクティブ化する
        }
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        // 省略 //
    }
}

Explosion EffectのUpdateメソッド内の14行目から29行目のUpdateメソッド内にclearEffect用の処理を追加しています。

この中の20行目で、爆発オブジェクトを表示する位置をRandom.Rangeメソッドの乱数を使い、ランダムに特定の範囲内に表示させるようにしています。

そして、0.2秒ごとの間隔で爆発するように繰り返す処理を行っていて、20回爆発エフェクトを表示させたら、28行目で親であるボスのオブジェクトをSetActiveメソッドで非アクティブ化しています。

これで実際にボスのHPが0になったら、

ボスに爆発エフェクトが大量に表示された後、消えていくという処理を作ることができました。

なお、爆発エフェクトの表示がボスや敵の後ろに表示されてしまう場合があったので、爆発のPrefabファイルを選択して、Sprite Rendererコンポーネントにある「Order in Layer」の値を1にして、手前に必ず表示されるようにしています。

各シーンを作成する

ボスを作成できたので、ゲームとしてのシーンをいくつか作成していきます。

シーンについては、これまでの作り方と同じなので、ざっくりと紹介していきます。

スタートシーン(起動画面)の作成

現状だといきなりゲームがスタートし始めてしまうため、スタート用のシーンを作成しています。

スタートシーンには、タイトルと操作方法をTextMeshProで作った以下のような画面を作ってみました。

ここでは省略しますが、スクリプトをアタッチしてスペースキーを押すとリトライシーンに飛ぶ処理を行っていて、そこからメインシーンに遷移してゲームがスタートする仕組みになっています。

また、プレイヤーの機体を配置したり、メインとは異なるBGMを流す設定もしてみました。

ゲームオーバーシーンの作成

プレイヤーのライフが0を下回ってもゲームが続いてしまっている状況なので、ライフが無くなったら、ゲームオーバーシーンに遷移するようにしていきます。

ゲームオーバーシーンも非常にシンプルで、以下のような画面を作成して、それまでのベストスコアを表示させるようにしています。

プレイヤーがやられてしまった際に、リトライシーンに遷移する処理を「RetryDirector」で記述していたので、このスクリプトを少し修正しておきます。

using UnityEngine;
using UnityEngine.SceneManagement;

public class RetryDirector : MonoBehaviour
{
    GameObject player;
    AudioSource audioSource;
    bool retryCheck = false;

    void Start()
    {
        player = GameObject.Find("player");
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (player.activeSelf == false && retryCheck == false)
        {
            audioSource.Play();
            retryCheck = true;
        }

        if (retryCheck == true && audioSource.isPlaying == false)
        {
            if (GameManager.instance.lifeCount > -1)
            {
                SceneManager.LoadScene("RetryScene");    // シーンを読み込んでリトライする
            }
            else
            {
                SceneManager.LoadScene("GameOverScene");    // ゲームオーバーシーンに遷移させる
            }
        }
    }
}

26行目から33行目で、GameManagerで管理している「lifeCount」の変数が0以上の時はリトライシーンに遷移して、それ以外の0よりも低くなった時はゲームオーバーシーンに遷移するようにしています。

そして、ゲームオーバーシーン内でベストスコアを表示させたいので、「GameManager」「bestScore」の変数を作成しています。

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    public int lifeCount;
    public int scorePoint;
    public int startPos;
    public int speedUpItem;
    public int bulletUpItem;
    public int bestScore;  // ベストスコアの数を計算する変数

    void Awake()
    {
        // 省略 //
    }

    void Update()
    {
        if (bestScore < scorePoint)
        {
            bestScore = scorePoint; // ベストスコアを更新する
        }
    }
}

22行目でscorePointが上回ったら、bestScoreを更新する処理を加えています。

また「BestScoreDisplay」というスクリプトを作成して、Startメソッドの中でbestScoreの値が表示されるように10行目で処理を行っています。

using TMPro;
using UnityEngine;

public class BestScoreDisplay : MonoBehaviour
{
    TextMeshProUGUI textMesh;

    void Start()
    {
        textMesh = GetComponent<TextMeshProUGUI>(); // TextMeshProコンポーネントを取得する
        textMesh.text = GameManager.instance.bestScore.ToString("d6");  // ベストスコアを表示させる
    }
}

また、ゲームオーバーシーンには、「BackStart」というスタート画面へ遷移するスクリプトをアタッチしています。

using UnityEngine;
using UnityEngine.SceneManagement;

public class BackStart : MonoBehaviour
{
    AudioSource audioSource;
    bool isPressing = false;    // スペースキーを押したかどうかを判定する

    void Start()
    {
        audioSource = GetComponent<AudioSource>();  // AudioSourceコンポーネントを取得する
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))    // スペースキーを押した場合
        {
            audioSource.Play(); // 音を再生する
            isPressing = true;  // スペースキーを押した判定にする
        }

        if (audioSource.isPlaying == false && isPressing == true)   // 音が終了したら
        {
            Destroy(GameManager.instance.gameObject);   // GameManagerのオブジェクトを消去する
            SceneManager.LoadScene("StartScene");   // スタートシーンに遷移させる
        }
    }
}

スペースキーを押したらスタートシーンに遷移するように、25行目でLoadSceneメソッドを使って処理を加えています。

ただ、そのまま遷移させてしまうと、スコアやライフなどがそのままになってしまうので、初期化しておく必要があります。

そのため、手前の24行目でDestroyメソッドを使って、GameManagerのオブジェクトを消去するようにしていて、スタート画面に戻った時には全部がリセットされて、新しいGameManagerのオブジェクトが作成されるようになっています。

ゲームクリアシーンの作成

最後に、ボスを倒した際に表示させるゲームクリアのシーンを作成しておきます。

ここでは、ゲーム画面の背景が少しずつフェードアウトするようにしたいので、元の背景画像を配置して、以下の「BackgroundTransparent」をアタッチしています。

using UnityEngine;

public class BackgroundTransparent : MonoBehaviour
{
    SpriteRenderer sr;
    float transparency = 1; // 透明度合いを指定する変数
    public GameObject canvas;   // Canvasオブジェクトを取得する
    AudioSource audioSource;

    void Start()
    {
        sr = GetComponent<SpriteRenderer>();    // SpriteRendererコンポーネントを取得する
    }

    void Update()
    {
        transparency -= 0.01f;  // transparencyを少しずつ減算させる
        sr.color = new Color(sr.color.r, sr.color.g, sr.color.b, transparency); // カラーの値に代入していく
        if (transparency <= 0)  // transparencyの値が0以下になった場合
        {
            canvas.SetActive(true); // canvasオブジェクトをアクティブ化する
            this.gameObject.SetActive(false);   // 背景画像を非アクティブ化する
        }
    }
}

17行目でtransparencyの値を少しずつ減少することで背景が透過されるようにしていて、19行目から23行目でこのtransparencyの値が0以下になったら、UIを表示させて背景画像を非アクティブ化しています。

このようにすることで、以下のように少しずつ背景が変化して、UIが表示される仕組みを作ってみました。

先ほどのゲームオーバーシーンと同じように、このクリアシーンにはベストスコアを表示させるスクリプトである「BestScoreDisplay」をアタッチさせて、スペースキーを押したら最初のスタートシーンに切り替わるように、「BackStart」をアタッチしています。

それぞれのシーンが作成できたら、メニューの「File」から「Build Profiles」を押して、Scene Listに各シーンを設定しておきます。

なお、ゲーム起動後はまず「StartScene」を表示させたいので、このシーンが一番上になるように設定しておきます。

これで、ざっくりな紹介となってしまいましたが、シーンの設定が完了しました。

バグ修正や調整を行う

シーン作成や遷移の処理が完了したら、実際にプレイをしてみてゲーム内のおかしい部分や調整を行っていきます。

ここでは、気になった部分の修正を紹介していきます。

プレイヤーの弾発射処理を修正

これまでプレイヤーが弾を発射するのは、Enterキーを押した瞬間のタイミングで発射していたので、連射する際はEnterキーを連打する必要がありました。

また、連打するとかなり早い間隔で弾を発射することができていたので、この部分を修正していきます。

プレイヤーの弾を発射する処理は、「PlayerShot」というスクリプトで記述しています。

using UnityEngine;

public class PlayerShoot : MonoBehaviour
{
    public GameObject playerBullet;
    AudioSource audioSource;
    float span = 0.2f;  // 弾を発射する間隔
    float delta;    // 時間を計測する変数

    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        delta += Time.deltaTime;    // フレーム間の時間を加算していく

        if (Input.GetKey(KeyCode.Return) && span < delta)   // Enterキーを押した場合
        {
            audioSource.Play();  // Audioを再生する
            GameObject cloneObject = Instantiate(playerBullet, this.transform.position, Quaternion.identity);  // 弾を表示させて座標を移動させる処理
            cloneObject.name = "PlayerBullet";  // 名前を変更する処理
            delta = 0;
        }
    }
}

19行目の条件指定部分を修正していて、キーを押した瞬間を検知する「GetKeyDown」から、キーを押している間を検知する「GetKey」に変更しています。

そして、弾を発射できる間隔を「Time.deltaTime」を使って指定していて、0.2秒ごとに発射される処理に変更してみました。

ライフの重複減少を修正

敵から攻撃を受けると、ライフが1減少する処理を作っていますが、敵から同時に攻撃を受けてしまうと、同フレーム内で同時に減少する処理が行われているようで、以下のようにライフが一気に2減少してしまっていました。

ライフを減少する処理は「PlayerController」で記述していたので、こちらを修正していきます。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 省略 //

    int collisionCount; // 敵から攻撃を受けた回数を計測する

    // 省略 //

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.name == "EnemyBullet" ||
            collision.gameObject.tag == "Enemy")
        {
            collisionCount++;   // 攻撃を受けた回数を計測
            if (collisionCount == 1)    // 1回目の攻撃のみ処理を行う
            {
                GameManager.instance.lifeCount = GameManager.instance.lifeCount - 1;
                this.gameObject.SetActive(false);
            }
        }

        if (collision.gameObject.tag == "Item")
        {
            itemGetAudio.Play();
        }
    }
}

新たに「collisionCount」という変数を作成して、まず16行目で敵から攻撃を受けた際に、このcollisionCountが計測されるようにしています。

そして、このcollisionCountが1の時にのみライフが減少するように、17行目から21行目で処理を書いています。

こうすることで、同時に敵から攻撃を受けても、ライフ減少の処理は必ず一度だけ行われるようになりました。

音量の調整

プレイヤーが弾を発射している音量が、最初は特に問題ないのですが、アイテムを取得して弾を2つ発射できるようになってから、音量が非常に大きくなっていました。

これは、弾を発射する音量を設定しているのが、「playerBulletPoint」のオブジェクトで、アイテムを取った後も音量を変えていなかったので、重複して音が発生していたため、音量が大きくなってしまっていました。

今回は、インスペクターウィンドウから弾を一つだけ発射している際の音量は「0.5」、弾を二つ発射している際の音量は「0.25」にしておくことで、重複しても同じ音量になるように調整してみました。

ボスの前で警告を表示

ステージの最後のボス戦の雰囲気を少し変えて、よりボス感を出していく処理を追加してみます。

まず、ボスが近づいてきたら、アラート音警告画面を表示させます。

ボスの直前に設置しているリトライポイント「retryPoint_3」のオブジェクトに、以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class WarningEffect : MonoBehaviour
{
    public GameObject player;   // プレイヤーのオブジェクトを取得する
    AudioSource audioSource;
    public GameObject warning;  // 警告画面用のオブジェクトを取得する
    bool isWarning = false; // 警告領域に入ったかどうかを検知する

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject == player) // プレイヤーと接触した場合
        {
            audioSource.Play(); // 警告音を再生する
            warning.SetActive(true);    // 警告画面をアクティブ化する
            isWarning = true;   // trueに変更する
        }
    }

    void Start()
    {
        audioSource = GetComponent<AudioSource>();  // AudioSourceコンポーネントを取得する
    }

    void Update()
    {
        if (isWarning && !audioSource.isPlaying && warning.activeSelf)  // 音声が終了した場合
        {
            warning.SetActive(false);   // 警告画面のオブジェクトを非アクティブ化する
        }
    }
}

プレイヤーがこのretryPoint_3に接触したら、14行目から16行目でwarningの画像オブジェクトをアクティブ化して、音声を流す処理を行っています。

そして、27行目でisPlayeing変数を使って音声を流し終わったタイミングで、warningの画像オブジェクトを非表示させるようにしています。

これでボスが近づいてきたら、

上記のように警告が表示される処理を作ってみました。

また、ボス戦のBGMを変更したいので、「BgmController」にも修正を行っていきます。

using UnityEngine;

public class BgmController : MonoBehaviour
{
    public GameObject player;
    AudioSource bgm;
    public GameObject warning;  // warningの画像オブジェクトを取得
    public AudioClip bossBgm;   // ボス戦のBGMのclipファイルを取得

    void Start()
    {
        bgm = GetComponent<AudioSource>();
    }
    void Update()
    {
        if (warning.activeSelf == true && bgm.isPlaying == true)    // warningが表示された場合
        {
            bgm.Stop(); // 音を停止する
        }

        if (warning.activeSelf == false && bgm.isPlaying == false)  // warningが消去された場合
        {
            bgm.clip = bossBgm; // ボス戦のBGMをAudioSourceにセットする
            bgm.Play(); // 音を再生する
        }

        if (player.activeSelf == false && bgm.isPlaying == true)
        {
            bgm.Stop();
        }
    }
}

16行目でwarningの画像オブジェクトがアクティブになったら、BGMを停止する処理を行っています。

そして、warningの画像オブジェクトが非アクティブに変わったタイミングで、23行目でAudioClipにボス戦用の音声clipを代入して、音を再生する処理を行っています。

これで、ボス戦の前でアラートが鳴り、ボス戦のBGMも変更することができました。

まとめ

このページでは、ゲームの最後となるボスを作り、各シーンを作成したり、バグや調整を行いました。

ボスは基本的には通常の敵と同じように作成しつつ、アニメーション機能を使ってレーザービームを発射させるようにしました。

また、各シーンの作成やバグ・調整も行ったので、ここまででゲームとしては完成となります。

最後にUnityからビルドを行って、ゲームファイルを書き出していく処理を行います。

後日、記事をアップロードする予定です!

最後までお読みいただきまして、ありがとうございました!

コメント