UnityでFPSをつくる その10-6 [ シーン遷移 ]

2021年7月17日

前回までで、タイトルシーンにポストプロセスを加えることができました。
今回はスクリプトで制御をして、メインシーンへと遷移させます。
以下が完成イメージです。

No.569

HierarchyウィンドウへCreate Emptyで空のGameObjectを作り、「SceneManager」にリネームします。

No.570
No.571

SceneManagerへ、以前作成したスクリプト「CursorState」をアタッチします。

No.572

これでカーソルが非表示になり、画面中央に固定されました。
次に「PRESS START」のテキストを点滅させて、ボタンクリックでメインシーンへ遷移するようにします。
新規に「ToMainScene」という名前でスクリプトを作り、

No.573

以下のコードをコピペしてください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//シーンを扱う為の名前空間
using UnityEngine.SceneManagement;

public class ToMainScene : MonoBehaviour
{
    [SerializeField] Text pressStart;

    [SerializeField] Image fadeBoard;

    [SerializeField] SpriteRenderer titleImage;

    //色、透明度
    float fadeBoardColorRed, fadeBoardColorGreen, fadeBoardColorBlue, fadeBoardColorAlpha,
            titleImageColorRed, titleImageColorGreen, titleImageColorBlue, titleImageColorAlpha,
                pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha;

    //点滅の間隔
    float textBlinkDuration = 8f;

    //アルファ値加算許可、スタートボタン押下済みフラグ
    bool alphaAdditionFlag, pressStartDoneFlag;

    // Start is called before the first frame update
    void Start()
    {
        //colorの個々の値は取得できても変更できない為、変数に代入する
        fadeBoardColorRed = fadeBoard.color.r;

        fadeBoardColorGreen = fadeBoard.color.g;

        fadeBoardColorBlue = fadeBoard.color.b;

        fadeBoardColorAlpha = fadeBoard.color.a;

        titleImageColorRed = titleImage.color.r;

        titleImageColorGreen = titleImage.color.g;

        titleImageColorBlue = titleImage.color.b;

        titleImageColorAlpha = titleImage.color.a;

        pressStartColorRed = pressStart.color.r;

        pressStartColorGreen = pressStart.color.g;

        pressStartColorBlue = pressStart.color.b;

        pressStartColorAlpha = pressStart.color.a;
    }

    // Update is called once per frame
    void Update()
    {
        BeforePressStart();

        if (Input.GetMouseButtonDown(0)) 
        {
            //スタートボタン押下前のテキスト点滅間隔を終了
            pressStartDoneFlag = true;

            StartCoroutine(FadeOut());
        }
    }

    IEnumerator FadeOut()
    {
        //フェードアウト
        var isFadeOut = true;

        //何秒かけて暗転させるか
        var fadeOutDuration = 3f;

        //点滅の間隔
        var textBlinkDurationAfterPressStart = 0.3f;

        while (isFadeOut)
        {
            //fadeOutDurationの時間で暗転するよう、アルファ値を加算
            fadeBoardColorAlpha += Time.deltaTime / fadeOutDuration;

            //titleImageはfadeBoard暗転の影響を受けないので、個別にアルファ値を減算
            titleImageColorAlpha -= Time.deltaTime / fadeOutDuration;

            //STARTキーのクリック前より、短い間隔でアルファ値 0.5 ~ 1 の間を行き来するようにする
            if (pressStartColorAlpha >= 0.5f && !alphaAdditionFlag)
            {
                pressStartColorAlpha -= Time.deltaTime / textBlinkDurationAfterPressStart;

                pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);

                if (pressStartColorAlpha <= 0.5f)
                {
                    alphaAdditionFlag = true;
                }
            }
            else if (pressStartColorAlpha <= 1f && alphaAdditionFlag)
            {
                pressStartColorAlpha += Time.deltaTime / textBlinkDurationAfterPressStart;

                pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);

                if (pressStartColorAlpha >= 1f)
                {
                    alphaAdditionFlag = false;
                }
            }

            //アルファ値以外は変更せずにcolorに値を代入
            fadeBoard.color = new Color(fadeBoardColorRed, fadeBoardColorGreen, fadeBoardColorBlue, fadeBoardColorAlpha);

            titleImage.color = new Color(titleImageColorRed, titleImageColorGreen, titleImageColorBlue, titleImageColorAlpha);

            //fadeBoardの透明度が1(完全に不透明)かつ、titleImageの透明度が0(完全に透明)になったら
            if (fadeBoardColorAlpha >= 1 && titleImageColorAlpha <= 0)
            {
                //フェードアウト終了
                isFadeOut = false;
            }

            yield return null;
        }

        yield return new WaitForSeconds(1f);

        //メインシーンに切り替え
        SceneManager.LoadScene("MainScene");
    }

    //スタートボタン押下前のテキスト点滅
    void BeforePressStart() 
    {
        //アルファ値加算許可&スタートボタン押下済みフラグが立っていない場合
        if (pressStartColorAlpha >= 0.5f && !alphaAdditionFlag && !pressStartDoneFlag)
        {
            pressStartColorAlpha -= Time.deltaTime / textBlinkDuration;

            pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);

            if (pressStartColorAlpha <= 0.5f)
            {
                alphaAdditionFlag = true;
            }
        }
        //完全に透明にならないよう、アルファ値 0.5 ~ 1 の間を行き来するようにする
        else if (pressStartColorAlpha <= 1f && alphaAdditionFlag && !pressStartDoneFlag)
        {
            pressStartColorAlpha += Time.deltaTime / textBlinkDuration;

            pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);

            if (pressStartColorAlpha >= 1f)
            {
                alphaAdditionFlag = false;
            }
        }
    }
}

スクリプトの中身を見ていきます。
4,6行目では、今回もUIとシーン遷移を扱う為、namespaceを追加しています。

No.574

10~14行目で、Inspectorから各コンポーネントを指定できるよう、クラス型の変数をSerializeField化。

No.575

17~19行目で、各コンポーネントの色(RGB)と透明度(A)を取得する為の変数を宣言。

No.576

22行目で、スタートボタンが押される前のテキストの点滅間隔を決める変数を宣言。

No.577

25行目で、アルファ値を一定の範囲で増減させる為のフラグと、スタートボタンが押されたことを判定するフラグを宣言。

No.578

今回のようにアルファ値だけを変更する場合でも、結果を反映させる為にnew Color(r,g,b,a)の形式で戻り値を利用する必要があるので、31~53行目で各コンポーネントのRGBA値を変数に取得します。

No.579

59行目で、スタートボタンが押される前の、テキストの挙動を定義した関数を呼び出しています。

No.580

定義元へ移動するには、関数名をクリックして「F12」キーを押します。

No.581

定義元では、「1」からスタートするテキストのアルファ値に対して、指定した時間間隔(textBlinkDuration)で透明になっていくように減算しています。

No.582

アルファ値減算の結果を反映させる為に、new Color(r,g,b,a)の形式で、テキストのカラー変数に代入します。

No.583

アルファ値が「0.5」以下になったら加算許可のフラグを立て、「1」以上になるまで減算の時と同じ時間間隔で加算していき、「1」以上になったら加算許可のフラグを下ろし、減算処理が始まります。
以降、スタートボタンが押されるまで繰り返されます。

No.584

スタートボタンにあたるマウスの左ボタンがクリックされたら、スタートボタン押下フラグを立てて上記の繰り返し処理を終了させ、コルーチンを開始します。

No.585

70~132行目でコルーチンの定義をしています。
フェードアウト開始フラグを立て、暗転にかける時間を設定し、暗転時はテキストをスタートボタン押下前よりも短い間隔で点滅させる為、textBlinkDurationよりも小さい値を設定しています。

No.586

fadeBoardのアルファ値を初期値「0」から加算、titleImageはfadeBoardより前面に位置していて影響を受けないので、アルファ値を初期値「1」から減算して透明にします。

No.587
No.588

テキストのアルファ値をスタートボタン押下前より短い間隔で点滅させることにより、ユーザーの入力に対しての反応動作を表現しています。

No.589

アルファ値の加減算をfadeBoardとtitleImageに反映させ、画面が真っ暗になった時点でフェードアウトを終了させます。

No.590

この処理を1フレームにつき1回繰り返すようにし、画面が真っ暗になってから1秒経過後にMainSceneへ遷移します。

No.591

このスクリプトをSceneManagerオブジェクトへアタッチして、各コンポーネントへの参照を以下のようにします。

No.592

次に、シーンを登録します。
メニューバーからFile → Build Settingsを開いてください。

No.593

Build Settingsウィンドウが開いた状態で「Add Open Scenes」ボタンをクリック、現在編集中のシーンが追加されます。

No.594

シーン名の右横に表示された数字が、ビルド後にゲームを起動した際の実行順になるので、TitleSceneをドラッグして一番上に置きます。

No.595

実行して、スタートボタンを押した後にメインシーンへ遷移するか確認してみます。

No.596

想定どおりの挙動になりました。
ただ、現状ではメインシーンへ遷移してすぐに戦闘開始になるので、一拍置きたいと思います。
メインシーンに切り替えて、HierarchyウィンドウからUI → Textを追加します。

No.597

追加したTextオブジェクトを「GET READY」にリネーム、Inspectorウィンドウから各項目を以下のように設定します。

No.598

ここへ、Add Componentから UI → Effects → Shadowを追加します。

No.599

Shadowコンポーネントの項目を、以下のように設定します。

No.600

これで、テキストオブジェクト「GET READY」の外観が完成しました。

No.601

この「GET READY」を表示するタイミングは後ほどスクリプトで制御するので、一旦、非表示にしておきます。

No.602

同様に、もう一つテキストオブジェクトを追加します。
Hierarchyウィンドウで「GET READY」を選択した状態から、「Ctrl + D」で複製します。
複製された「GET READY (1)」を「START」にリネーム、各項目を以下のように設定します。

No.603

ここに、Add Componentから UI → Effects → Outlineを追加します。

No.604

追加されたOutlineコンポーネントの項目を、以下のように設定します。

No.605

テキストコンポーネントにチェックを入れて表示すると、以下のようになります。(外観の確認が終わったら、チェックを外して非表示に戻してください。)

No.606

次に、この「GET READY」と「START」をスクリプトで制御していきます。
「ImmediateryAfterTheStart」の名前でスクリプトを新規作成し、以下のコードをコピペしてください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ImmediatelyAfterTheStart : MonoBehaviour
{
    [SerializeField] PlayerMove playerMove;

    [SerializeField] SightDisplay sightDisplay;

    [SerializeField] Image sight;

    [SerializeField] EnemyMove enemyMove;

    [SerializeField] EnemyShooting enemyShooting;

    [SerializeField] Text getReady;

    [SerializeField] Text start;

    float timeElapsed,

          flameOutPositionY = 600f,

          getReadyTextStartTime = 0.5f,

          getReadyTextEndTime = 2f,

          startTextStartTime = 2f,

          startTextEndTime = 3.5f,

          battleStartTime = 4.5f;

    bool  getReadyTextFlag,

          startTextFlag,

          battleStartFlag;

    // Start is called before the first frame update
    //ゲーム開始直後では停止させておきたいスクリプトやUIをOFF
    void Start()
    {
        playerMove.enabled = false;

        sightDisplay.enabled = false;

        sight.enabled = false;

        enemyMove.enabled = false;

        enemyShooting.enabled = false;
    }

    // Update is called once per frame
    void Update()
    {
        //時間計測
        timeElapsed += Time.deltaTime;

        //開始時間になったらgetReadyを表示、処理の繰り返しを避ける為にフラグを立てる
        if (timeElapsed >= getReadyTextStartTime && !getReadyTextFlag)
        {
            getReady.enabled = true;

            getReadyTextFlag = true;
        }
        //終了時間になったらgetReadyを非表示
        else if (timeElapsed >= getReadyTextEndTime && getReady.enabled) 
        {
            getReady.enabled = false;
        }

        //開始時間になったらstartを表示、処理の繰り返しを避ける為にフラグを立てる
        if (timeElapsed >= startTextStartTime && !startTextFlag)
        {
            start.enabled = true;

            startTextFlag = true;
        }
        //終了時間になったらstartを+y方向へフレームアウト
        else if (timeElapsed >= startTextEndTime && start.transform.localPosition.y < flameOutPositionY)
        {
            //どのくらい時間をかけるのか
            var frameOutDuration = 0.1f;

            start.transform.localPosition += new Vector3(0f, flameOutPositionY * Time.deltaTime / frameOutDuration, 0f);
        }

        //バトル開始時間になったらスクリプトや、UIをON。処理の繰り返しを避ける為にフラグを立てる
        if (timeElapsed >= battleStartTime && !battleStartFlag)
        {
            playerMove.enabled = true;

            sightDisplay.enabled = true;

            sight.enabled = true;

            enemyMove.enabled = true;

            enemyShooting.enabled = true;

            battleStartFlag = true;
        }
    }
}

このスクリプトをSceneManagerオブジェクトへアタッチして、各項目を以下のように設定してください。

No.607

実行すると、以下のようになります。

No.608

コードを見ていきます。
UIの表示・非表示を扱うので、namespaceにUnityEngine.UIを宣言。

No.609

対象となるスクリプトやUIをInspectorウィンドウから設定できるよう、SerializeFiled化。

No.610

時間の区切りに使うfloat型変数を宣言。

No.611

処理の繰り返しを避ける為の判定フラグ用、bool型変数を宣言。

No.612

PlayerとEnemyオブジェクトにアタッチされた、移動と攻撃を制御しているスクリプトと、Playerの照準UIをOFF。

No.613

Time.deltaTimeでフレーム毎の経過時間を加算していき、表示や非表示等を制御するタイミングの目安にします。

No.614

時間経過に合わせて最初に「GET READY」を表示、一度表示をONにしたら{}内の処理を再度行う必要がないのでフラグで管理しています。

No.615

「GET READY」を非表示にすると同時に「START」を表示、フレームの差異を無くす為にTime.deltaTimeを掛けて1秒でフレームアウトをするところを、指定時間で到達するように除算して調整しています。

No.616

最後に、停止しておいたスクリプトやUIをONにして戦闘を開始します。

No.617

長くなってきたので、続きは次回にしたいと思います。おつかれさまでした!

FPS

Posted by kenji