【Unity】シーン遷移時のフェードを自作する

###はじめに

Unityで普通に画面遷移すると、とても質素で安っぽくなってしまいます。画面遷移時に一瞬だけ暗転するとそれだけで結構いい感じになります。
アセットのようなものもあるみたいですが、それ程難しくない気がしたので今回は自作してみました。
[完成イメージ](#完成)

###環境

```xml:環境

Windows10
Unity2019.3.12f1

```

###構成

今回はシーン１とシーン２を用意してここに実装します。スクリプトは以下を用意します。

- SceneController：MainCameraにくっつけます
- FadeManader：暗転パネル用Canvasにくっつけます

![コメント 2021-01-31 105218.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637123/97876070-3d63-90c8-486a-8ec97249291f.png)

一応シーン１は赤、シーン２は青にしてあります。
あとスクショにはありませんが、シーン切り替え用のボタンも配置してあります。

###流れ

先におおまかな流れを書いておくと、

1.暗転用のパネルを作る
2.暗転用のパネルを召喚
3.パネルのフェードイン、アウトの仕組みを作る
4.シーン切り替え時にフェードするようにする

の４本立てとなります。

###実装

#####暗転用パネル

まずは暗転用のパネルをつくります。
イメージでも良いんですがパネルのほうが好きなので今回はパネルでいきます。
パネルは毎シーンごとに用意する方法もあるかと思いますが、一応シーンを跨ぐので最初にPrefabから呼び出してDontDestroyOnLoadで保持することにします。

DontDestroyOnLoadはCanvas以下のUIには適応できないので、別のCanvasを作ってそこにPanelを置きCanvasごとPrefab化します。以下の感じ。
![コメント 2021-01-31 110952.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637123/2dd74703-42b7-1562-c2f6-1647f4863459.png)

それと、このCanvasが一番上に来るようにソート順を0から1に上げておきます。
ついでに見つけやすくするためタグも変更しておきます。

中のPanelは色を黒にして、「レイキャストターゲット」のチェックを外しておきます。
![コメント 2021-01-31 112058.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637123/f2e54e12-0421-e07f-7d57-ea5130582320.png)
「レイキャストターゲット」をOFFにしておくことで下にあるBotton等を触れるようになります。

ここまで一旦CanvasごとPrefab化しておきます。

#####暗転用パネル召喚

Prefab化した暗転用パネルを呼び出してDontDestroyOnLoadします。
まずは暗転用パネルをインスタンス化します(今回は面倒だったのでMainCameraにアタッチしたSceneControllerを使って暗転用パネルを召喚します)。

```C#:SceneController.cs

public class SceneController : MonoBehaviour
{
    public GameObject fade;//インスペクタからPrefab化したCanvasを入れる

    void Start()
    {
        if (!FadeManager.isFadeInstance)//isFadeInstanceは後で用意する
        {
            Instantiate(fade);
        }
    }
}
```
次にインスタンス化したCanvasをDontDestroyOnLoadします。
FadeManagerはPrefab化したCanvasにアタッチします(Panelではないので注意してください)。

```C#:FadeManager.cs

public class FadeManager : MonoBehaviour
{
    public static bool isFadeInstance = false;//Canvas召喚フラグ

    void Start()
    {
        if (!isFadeInstance)//起動時
        {
            DontDestroyOnLoad(this);
            isFadeInstance = true;
        }
        else//起動時以外は重複しないようにする
        {
            Destroy(this);
        }
    }
}
```
あとはインスペクタ上でしっかり設定すれば開始時にCanvasが召喚されてDontDestroyOnLoadになると思います。

#####フェード

そしたら次はCanvas内のPanelの透過率を変化させてフェードする仕組みをつくります。

```C#:FadeManager.cs
using UnityEngine.UI;//追加

public class FadeManager : MonoBehaviour
{
    public static bool isFadeInstance = false;

    public bool isFadeIn = false;//フェードインするフラグ
    public bool isFadeOut = false;//フェードアウトするフラグ

    public float alpha = 0.0f;//透過率、これを変化させる
    public float fadeSpeed = 0.2f;//フェードに掛かる時間

    void Start()
    {
        //省略
    }

    void Update()
    {
        if (isFadeIn)
        {
            alpha -= Time.deltaTime / fadeSpeed;
            if (alpha <= 0.0f)//透明になったら、フェードインを終了
            {
                isFadeIn = false;
                alpha = 0.0f;
            }
            this.GetComponentInChildren<Image>().color = new Color(0.0f, 0.0f, 0.0f, alpha);
        }
        else if (isFadeOut)
        {
            alpha += Time.deltaTime / fadeSpeed;
            if (alpha >= 1.0f)//真っ黒になったら、フェードアウトを終了
            {
                isFadeOut = false;
                alpha = 1.0f;
            }
            this.GetComponentInChildren<Image>().color = new Color(0.0f, 0.0f, 0.0f, alpha);
        }
    }

    public void fadeIn()
    {
        isFadeIn = true;
        isFadeOut = false;
    }

    public void fadeOut()
    {
        isFadeOut = true;
        isFadeIn = false;
    }
}

```
this.GetComponentInChildren<Image>().colorはCanbasの子であるPanelのImageコンポーネントを操作しています。
これでisFadeInやisFadeOutのフラグを操作することでパネルのフェードインやアウトができます。
あとはこのフラグ操作をシーンチェンジのタイミングに合わせてで行うだけです。

#####シーン切り替え

```C#:SceneController.cs
using System.Threading.Tasks;//追加
using UnityEngine.SceneManagement;//追加

public class SceneController : MonoBehaviour
{
    public GameObject fadeCanvas;//操作するCanvas、タグで探す

    void Start()
    {
        //省略
        Invoke("findFadeObject", 0.02f);//起動時用にCanvasの召喚をちょっと待つ
    }

    void findFadeObject()
    {
        fadeCanvas = GameObject.FindGameObjectWithTag("Fade");//Canvasをみつける
        fadeCanvas.GetComponent<FadeManager>().fadeIn();//フェードインフラグを立てる
    }

    public async void sceneChange(string sceneName)//ボタン操作などで呼び出す
    {
        fadeCanvas.GetComponent<FadeManager>().fadeOut();//フェードアウトフラグを立てる
        await Task.Delay(200);//暗転するまで待つ
        SceneManager.LoadScene(sceneName);//シーンチェンジ
    }
}
```
InvokeやDelayの時間はFadeManagerのfadeSpeedと合わせて調整してください。

###完成

![unity.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637123/ee3246b9-96ad-5077-58ae-d1eb7f7fd51e.gif)