【Unity入門】2Dアクションを作ろう【シーン切り替え】

この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。

<シーン間を移動しよう>

SceneManager.LoadScene

さて、まだ未完成ですが、今まで2つのシーンを作ってきました。

now scene

現在、タイトルシーンとステージ1のシーンがあります。今回はタイトル→ステージ1へのシーン移動を行なっていこうと思います。

と、言ってもタイトルシーンを作った時、ボタンを押したら次のシーンへいく手前のところまで行っていたので後は少し手を加えてあげればシーン移行が可能になります。

↓の記事でタイトルシーンの解説をしているので、忘れてしまった人やこの記事から入った人は参考にしてみてください。

現状タイトルシーンにくっついているスクリプトは↓のような感じになっています。

title script

ボタンを押したら、PressStartの関数が呼ばれるようになっています。

ここに次のシーンへいく命令を書いていきます。

次のシーンへ行く命令を書くと↓のようになります。

クリックすると展開します。
 using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class title : MonoBehaviour
{

    private bool firstPush = false;

    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    
    public void PressStart()
    {
        Debug.Log("Press Start!");
        if (!firstPush)
        {
            Debug.Log("Go Next Scene!");
            SceneManager.LoadScene("stage1");
            firstPush = true;
        }
    }
}

ポイントとしては

 using UnityEngine.SceneManagement;

これを書くことによってUnityのシーンに関数命令を使う事ができるようになります。

そして、

 SceneManager.LoadScene("行きたいシーンの名前");

これで指定した名前のシーンへ行く事ができます。

ボタン側から関数を呼ぶのを忘れずに

button on click set method

この状態で実行すると

scene change error

おっと、エラーが発生しています。

Scene ‘シーン名’ couldn’t be loaded because …

これは、単にシーンの名前を間違っているか、シーンの設定が足りてないので起きているエラーになります。

シーン名はstage1で合っているのでシーンの設定が足りていない事になります。

自分だけかもしれませんが、このシーンの設定はよく忘れるのでこのエラーが出たらサッと直しましょう。

File>Build Settings…から

open build settings window

この画面のAdd Open Scenesをクリックします。

build setting add open scene

すると現在開いているシーンが表示されたかと思います。これで現在開いているシーンを設定に登録する事ができました。

add current scene

これをtitle,stage1両方行いましょう。

add title scene

これで両シーン登録する事ができました。

ここで右側にある数字に注目してください。0と1と書いてあると思います。

1以降は別にどうでもいいのですが(命名規則等でコントロールする場合を除く)0はちょっと重要になってきます。

0に割り振ってあるシーンはゲーム開始時一番最初に開くシーンになります。

タイトルシーンを一番最初に開いて欲しいのにstage1が0になってしまっています。

ドラッグ&ドロップで場所を移動できるのでtitleを0番目に持ってきましょう。

change order build setting

はい。これでOKです。この状態でシーンを再生してみましょう。

scene change

はい、これでシーンの移動が可能になりました。

覚えておきたい

・using UnityEngine.SceneManagement;
・SceneManager.LoadScene(“行きたいシーンの名前”);
・シーンを移動するにはシーンを設定に登録しておく必要があるよ!

<シーンの移動とは>

さて、今までシーンとは(一般的には)場面場面だよと解説してきました。

よってシーン移動というのは場面を切り替えるという事になります。

しかしながら、あくまでシーンというものは「オブジェクトとそのパラメータを記録し保持しているもの」なので、シーン移動というのは正確には「前のオブジェクトとそのパラメータ」をメモリ上から解放し、「次のオブジェクトとそのパラメータ」をロードするという事になります。

今はまだちゃんと理解する必要はありませんが、シーンの移動によってアンロード&ロードをしていると思ってください。

<シーンの切り替えにフェードを入れよう>

Spriteを使ってフェードを実現しよう

さて、先ほどシーンの切り替えに成功しましたが、パッと変わってしまって、なんだかゲームっぽくありません。すごく淡白な感じがします。

シーン切り替え時にフェードを入れて演出したいところなのですが、色々難しい部分がたくさんあるので、最も簡単(だと思う)方法でフェードを実装していきたいと思います。

今回は簡易的なフェードですが、本格的なフェードはまた解説する機会があればしたいと思います。

タイトルシーンのCanvasの子にImageを作成してください。

scene fade image

Imageの大きさを背景と同じにしてください。

そして、適当な色の画像を用意します。自分は↓のような画像を用意しました。

black

テクスチャタイプをスプライトに変更してImageに突っ込みます。すると画面が真っ黒になったかと思います。

black fade image

ここでImageのImage Typeという項目を変更します。Filledにしてください。

change image type

次にFill Methodという項目をVerticalに変更します。

fill method vertical

この状態でFill Amountという項目をいじると画像が行ったり来たりすると思います。

fill amont change

今は手動でやってしまっているので、これをスクリプト制御するようにしたいと思います。

自分はFadeImageという名前でスクリプトを作成しました。

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.UI;
 
 public class FadeImage : MonoBehaviour
 {
     private Image img = null;

     void Start()
     {
         img = GetComponent<Image>();
     }

     void Update()
     {
         
     }
 }

とりあえず、Imageのインスタンスを捕まえます。

UGUIをスクリプトに書く際には

 using UnityEngine.UI;

これを追加するといいと思います。

フェードインを実装するのに初期設定を行っていきます。

     void Start()
     {
         img = GetComponent<Image>();
         img.color = new Color(1, 1, 1, 1);
         img.fillAmount = 1;
         img.raycastTarget = true; 
     }

初期状態はフェードインをして欲しいので黒色にして画面全体に表示されている状態にします。

img.raycastTarget = true;

これは当たり判定をオンにしています。フェードが完了するまでボタンを押せなくするためです。フェードが完了したらオフにします。

フェードインを実装しよう

次はフェードインを実装するために、Updateにアルファ値を下げる処理とfillAmountをいじる処理を書きます。

クリックすると展開します
 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FadeImage : MonoBehaviour
{
    private Image img = null;
    private float timer = 0;
    private float wait = 0.5f;
    private bool fadeIn = false;

    void Start()
    {
        img = GetComponent<Image>();
        img.color = new Color(1, 1, 1, 1);
        img.fillAmount = 1;
        img.raycastTarget = true;
        fadeIn = true;
    }

    void Update()
    {
        if (fadeIn)
        {
            //フェードイン中
            if (timer < 1 + wait && timer > wait)
            {
                img.color = new Color(1, 1, 1, 1 - timer + wait);
                img.fillAmount = 1 - timer + wait;
            }
            //フェードイン完了
            else if (timer >= 1 + wait)
            {
                img.color = new Color(1, 1, 1, 0);
                img.fillAmount = 0;
                img.raycastTarget = false;
            }
            timer += Time.deltaTime;
        }
    }
}

ちょっとややこしい形になりましたが、シーンを最初に読み込んだ時というのはフレームが飛びやすいのでフレームが飛んでも大丈夫なようにちょっと待つ処理を入れています。

ついでにfadeInのフラグがたったらフェードインするようにします。

            //フェードイン中  
            if (timer < 1 + wait && timer > wait)
             {
                 img.color = new Color(1, 1, 1, 1 - timer + wait);
                 img.fillAmount = 1 - timer + wait;
             }
             //フェードイン完了 
             else if (timer >= 1 + wait)
             {
                 img.color = new Color(1, 1, 1, 0);
                 img.fillAmount = 0;
                 img.raycastTarget = false;
                 timer = 0.0f;
                 fadeIn = false; 
             }

この処理は、ちょっと時間を待ってから1秒かけて画像のアルファ値とfillAmountを下げる処理を書いています。

1が最大値なので、そこから1秒かけて引いている感じです。

フェードインが完了したら後処理で値をきちんとします。これはTime.deltaTimeの値がマチマチになるのできっちり1秒になるとは限らないからです

最後の値はきっちりしてfadeInのフラグをfalseにしましょう。

この状態で再生するとフェードインします。

fade in

今のままではフェードインを他のスクリプトから呼ぶ事ができないのでフラグで読めるようにします。

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

public class FadeImage : MonoBehaviour
{
    [HideInInspector] public bool compFadeIn = false;    //フェードイン完了

    private Image img = null;
    private float timer = 0;
    private float wait = 0.5f;
    private bool fadeIn = false;
    
    /// <summary>
    /// フェードインを開始する
    /// </summary>
    public void StartFadeIn()
    {
        if (fadeIn)
        {
            return;
        }
        fadeIn = true;
        compFadeIn = false;
        timer = 0.0f;
        img.color = new Color(1, 1, 1, 1);
        img.fillAmount = 1;
        img.raycastTarget = true;
    }

    void Start()
    {
        img = GetComponent<Image>();
        StartFadeIn();
    }

    void Update()
    {
        if (fadeIn)
        {
            //フェードイン中
            if (timer < 1 + wait && timer > wait)
            {
                img.color = new Color(1, 1, 1, 1 - timer + wait);
                img.fillAmount = 1 - timer + wait;
            }
            //フェードイン完了
            else if (timer >= 1 + wait)
            {
                img.color = new Color(1, 1, 1, 0);
                img.fillAmount = 0;
                img.raycastTarget = false;
                timer = 0.0f;
                fadeIn = false;
                compFadeIn = true;
            }
            timer += Time.deltaTime;
        }
    }
}

関数を用意して外から呼べるようにします。

     /// <summary>
     /// フェードインを開始する
     /// </summary>
     public void StartFadeIn()
     {
         if (fadeIn)
         {
             return;
         }
         fadeIn = true;
         compFadeIn = false;
         timer = 0.0f; 
         img.color = new Color(1, 1, 1, 1);
         img.fillAmount = 1;
         img.raycastTarget = true;
     }

フェードインに必要な各種フラグを初期化します。Startに書いてあった部分をこちらに移し、Startからフェードインを呼ぶようにします。

何度も呼ばれると困るのでfadeInがtrueの時フェードインしないようにreturnで返します。

そして、↓のフラグで他のスクリプトがフェードインを完了したことを認知できるようにします。

[HideInInspector] public bool compFadeIn = false;    //フェードイン完了

Imageを透明にする時の注意点

さて、フェードインを実装しましたが、Imageのアルファ値をいじって透明にしています。

ここで注意点があるのですが、実は透明であるということは非常に重いのです。

よく思い出してみてください。完全な透明だとイメージしづらいですが、うっすら透明のオブジェクトが画面に乗っている場合どうなるでしょうか?

うっすら透明のオブジェクトがあった場合、後ろのオブジェクトの色にうっすら透明の色が乗って描画されると思います。

透明なオブジェクトが存在した場合、後ろのオブジェクトを描画した後、透明なオブジェクトの色を重ねる処理が走ります。

要は、透明なオブジェクトが存在すると同じ場所が2回描画されます。透明なオブジェクトが増えると同じ場所が何回も描画されることになります。そのため非常に重いのです。

とはいえ、フェードさせるのに透明な状態を使う必要がある為、透明は使わなければなりません。

しかしながら、「フェードしていない時」透明なオブジェクトがずっと存在していると意味もなく重くなってしまいます。

そこで、Canvas RendererのCull Transparent Meshにチェックを入れましょう。

canvas renderer cull transparent mesh

これにチェックが入っていると完全に透明になっているオブジェクトは無視されるようになります。

KeyPoint

・長い間透明な状態になるUGUIがある場合、Cull Transparent Meshにチェックを入れよう!

フェードアウトを実装しよう

では、フェードアウトの方も作りましょう。

フェードインと同じようにすればOKです。

ただし、フェードアウト時はすぐにフェードアウト開始したいのでwaitを外します。

クリックすると展開する
 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FadeImage : MonoBehaviour
{
    [HideInInspector] public bool compFadeIn = false;   //フェードイン完了
    [HideInInspector] public bool compFadeOut = false;  //フェードイン完了

    private Image img = null;
    private float timer = 0;
    private float wait = 0.5f;
    private bool fadeIn = false;
    private bool fadeOut = false;

    /// <summary>
    /// フェードインを開始する
    /// </summary>
    public void StartFadeIn()
    {
        if (fadeIn || fadeOut)
        {
            return;
        }
        fadeIn = true;
        compFadeIn = false;
        timer = 0.0f;
        img.color = new Color(1, 1, 1, 1);
        img.fillAmount = 1;
        img.raycastTarget = true;
    }

    /// <summary>
    /// フェードアウトを開始する
    /// </summary>
    public void StartFadeOut()
    {
        if (fadeIn || fadeOut)
        {
            return;
        }
        fadeOut = true;
        compFadeOut = false;
        timer = 0.0f;
        img.color = new Color(1, 1, 1, 0);
        img.fillAmount = 0;
        img.raycastTarget = true;
    }

    void Start()
    {
        img = GetComponent<Image>();
        StartFadeIn();
    }

    void Update()
    {
        if (fadeIn)
        {
            //フェードイン中
            if (timer < 1 + wait && timer > wait)
            {
                img.color = new Color(1, 1, 1, 1 - timer + wait);
                img.fillAmount = 1 - timer + wait;
            }
            //フェードイン完了
            else if (timer >= 1 + wait)
            {
                img.color = new Color(1, 1, 1, 0);
                img.fillAmount = 0;
                img.raycastTarget = false;
                timer = 0.0f;
                fadeIn = false;
                compFadeIn = true;
            }
            timer += Time.deltaTime;
        }
        else if (fadeOut)
        {
            //フェードアウト中
            if (timer < 1)
            {
                img.color = new Color(1, 1, 1, timer);
                img.fillAmount = timer;
            }
            //フェードアウト完了
            else if (timer >= 1)
            {
                img.color = new Color(1, 1, 1, 1);
                img.fillAmount = 1;
                img.raycastTarget = false;
                timer = 0.0f;
                fadeOut = false;
                compFadeOut = true;
            }
            timer += Time.deltaTime;
        }
    }
}

これをボタンを押した時に起動するようにしましょう。

↓タイトルの方のスクリプトにpress startを押したらフェードアウトし、フェードアウトが完了したらシーンを移動するようにします。

 using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class title : MonoBehaviour
{
    public FadeImage fade;

    private bool firstPush = false;

    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        if (fade.compFadeOut)
        {
            SceneManager.LoadScene("stage1");
        }
    }
    
    public void PressStart()
    {
        Debug.Log("Press Start!");
        if (!firstPush)
        {
            if (fade != null)
            {
                fade.StartFadeOut();
                firstPush = true;
            }
        }
    }
}

インスペクターでインスタンスを渡すのを忘れないようにしましょう。

set fade instaince

各シーンにフェードを配置しよう

さて、このフェードの設定をいちいちするのはだるいのでプレハブ化しましょう。

Prefab用のフォルダを作っておくと便利かと思います

prefab fade

これをステージシーンにも配置しましょう。

stage set fade

Canvasを設置し、Scale With Screen Sizeにしましょう。そして作ったプレハブを設置すればOKです。

タイトルシーンに戻って再生すると

scene fade

これでシーンのフェードを作る事ができました。

何かうまくいかない事があった場合は↓の記事を参考にしてみてください



タイトルとURLをコピーしました