【Unity】コルーチンとシーン遷移

#やりたいこと
- コルーチンについての理解
- コルーチンを用いたフェードイン・フェードアウトの実装

# コルーチンの最小構成
## コルーチンの定義
```csharp
    IEnumerator  Fadeout() {
      yield return null;  
    }
```
- 戻り値の型はIEnumerator
- 戻り値は yield return ~~~;

## コルーチンの呼び出し
```csharp
   StartCoroutine("Fadeout");
```
```csharp
   StartCoroutine(Fadeout());
```
- メソッドを渡す。
(おそらくリフレクションを利用している。)
- コルーチンの定義と呼び出しは同一クラスである必要がある。
(別クラスである場合、エラーが発生する。)

# コルーチンの呼び出し順序
## テストコード
```csharp
public class Fade : MonoBehaviour
{
    void Update()
    {
        Debug.Log("BeginUpdate");
        if (Input.GetKeyDown(KeyCode.A))
        {
            Debug.Log("BeginFadeout");
            StartCoroutine("Fadeout");
            Debug.Log("EndFadeout");
        }
        Debug.Log("EndUpdate");
    }

    public IEnumerator  Fadeout()
    {
        for (int i = 1; i <= 3; i++)
        {
            Debug.Log("Fadeout : " + i + "回目");
            yield return null;
        }
    }
}
```
## ログ
--- 1フレーム目 --- 
BeginUpdate
BeginFadeout
**Fadeout : 1回目**
EndFadeout
EndUpdate
--- 2フレーム目 ---
BeginUpdate
EndUpdate
**Fadeout : 2回目**
--- 3フレーム目 ---
BeginUpdate
EndUpdate
**Fadeout : 3回目**

## 実行結果
- 1フレーム目は通常のメソッドと同じ振る舞いをする。
- 2フレーム以降は自動で実行され、順序はUpdate()とは別のタイミングである。

# コルーチンについての理解を深める。
## 1フレームに一度実行する。
```Csharp
  yield return null;
```
## コルーチンを一定時間ごとに実行する。
```Csharp
　yield return new WaitForSeconds(1.0f); // 1秒ごと
```
## コルーチンを途中で終了する。(以降の処理は呼ばれない。)
```
  yield break;
```
## 全レンダリング完了後に実行する。
```
  yield return new WaitForEndOfFrame();
```
## すべてのコルーチンを停止する。
```
  StopAllCoroutines();
```

※すべてのというのはそのクラス内(MonoBeheiver)ということ。別のクラスやオブジェクトには影響しない。

## 

## フェードイン・フェードアウトの実装
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class FadeSceneManager : MonoBehaviour
{
    private static Canvas canvas;
    private static Image image;

    // シングルトン
    private static FadeSceneManager singlton;
    public static FadeSceneManager Instance
    {
        get {
            if (singlton == null)
            {
                Init();
            }
            return singlton;
        }
    }
    private FadeSceneManager() { }
    private static void Init() {
        // Canvas作成
        GameObject canvasObject = new GameObject("CanvasFade");
        canvas = canvasObject.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvas.sortingOrder = 100;
        // 用途不明 (3Dになった際に必要になる？)
        // canvasObject.AddComponent<GraphicRaycaster>();

        // Image作成
        image = new GameObject("ImageFade").AddComponent<Image>();
        image.transform.SetParent(canvas.transform, false);
        // 画面中央をアンカーとし、Imageのサイズをスクリーンサイズに合わせる。
        image.rectTransform.anchoredPosition = Vector3.zero;
        Camera camera = GameObject.Find("Main Camera").GetComponent<Camera>();
        image.rectTransform.sizeDelta = new Vector2(Screen.width, Screen.height);

        // 遷移先シーンでもオブジェクトを破棄しない。
        DontDestroyOnLoad(canvas.gameObject);

        // シングルトンオブジェクトを保持する。
        // newの場合、オブジェクトが正しく初期化されない可能性があるので、
        // Add - GetComponentを通して、オブジェクトを取得する。
        canvasObject.AddComponent<FadeSceneManager>();
        singlton = canvasObject.GetComponent<FadeSceneManager>();
    }

    // 画面遷移を行う。
    // interval - フェードイン，フェードアウトに要する時間(秒)
    public void LoadScene(float interval, string sceneName)
    {
        StartCoroutine(Fade(interval, sceneName));
    }

    private IEnumerator Fade(float interval, string sceneName)
    {
        float time = 0f;
        canvas.enabled = true;

        // フェードイン
        while (time <= interval)
        {
            float fadeAlpha = Mathf.Lerp(0f, 1f, time / interval);
            image.color = new Color(0.0f, 0.0f, 0.0f, fadeAlpha);
            time += Time.deltaTime;
            yield return null;
        }

        // シーン非同期ロード
        yield return SceneManager.LoadSceneAsync(sceneName);

        // フェードアウト
        time = 0f;
        while (time <= interval)
        {
            float fadeAlpha = Mathf.Lerp(1f, 0f, time / interval);
            image.color = new Color(0.0f, 0.0f, 0.0f, fadeAlpha);
            time += Time.deltaTime;
            yield return null;
        }
        // 描画を更新しない。
        canvas.enabled = false;
    }
}
```
ほとんど参考サイトの通りです。
メリット : ゲームオブジェクトを動的に作成するため、初期配置が不要。

# 参考サイト
[コルーチン](https://docs.unity3d.com/ja/2019.1/Manual/Coroutines.html)
[C#におけるyieldの挙動](https://tyheeeee.hateblo.jp/entry/2013/08/07/C%23におけるyield_returnの挙動)

[【Unity】シーン遷移時のフェードイン・フェードアウトを実装してみた ](https://naichilab.blogspot.com/2013/12/unity.html)
[Unityシーン間のフェードインアウトを2行で実現できる簡単スクリプト](https://onosendai.net/fade/)