LoadSceneAsyncを使った画面遷移のサンプル


![loadsceneasync.gif](https://qiita-image-store.s3.amazonaws.com/0/113504/b78e5d5d-cc19-5ac8-3566-39e7b54ba6b2.gif "loadsceneasync.gif")

# 環境
Unity 2018.3.0f2
UniRx 6.2.2

UniRxのUniTaskを使うため2018.3以上が好ましい

# サンプル
https://github.com/teach310/commonAsset2/tree/release/0.4.0

# 背景
ハッカソンとかでSceneManager.LoadSceneを使っているのをよく見る
シーンが重いと固まるため、普段からLoadSceneAsync使った方が良いと思う
LoadSceneAsyncだとシーン遷移の間バリア作らないといけないのが面倒
=> async使えるようになったしサンプル作って公開しよう！

# サンプルの設計について
![スクリーンショット 2018-12-30 16.14.55.png](https://qiita-image-store.s3.amazonaws.com/0/113504/0f89a452-b37b-2860-70d1-32cd8dc9b8da.png "スクリーンショット 2018-12-30 16.14.55.png")

サンプルではSampleTitle, SampleMain, SampleResultという3つのシーンを作りました。

**そして、各シーンにそれぞれシーン名と同名のクラスを作って置いておきます。**
本記事ではこれらをシーンクラスと呼びます。

![スクリーンショット 2018-12-30 16.28.07.png](https://qiita-image-store.s3.amazonaws.com/0/113504/a09c392c-3644-f5f6-240a-7aeeaede3b73.png "スクリーンショット 2018-12-30 16.28.07.png")



このシーンクラスがそのシーンにおける処理の起点となります。
そして基本的にはAwake, Start関数の使用は禁止です。初期化は全てこのシーンクラスから行います。



ハッカソンでは複数人が一緒に作業するため、コードの場所、命名規則がぐちゃぐちゃになる、Start関数が乱立するといったことがあると思います。
上記のようなルールを作っておくことで、他の誰かが作ったシーンのコード読むときはとりあえずそのシーンクラスを読み解けばいいので統一感がでます。

![スクリーンショット 2018-12-30 16.04.20.png](https://qiita-image-store.s3.amazonaws.com/0/113504/33178928-401c-4b15-d749-83227b419e0d.png "スクリーンショット 2018-12-30 16.04.20.png")

各シーンクラスは`SceneBase.cs`を継承します。
シーンクラスは`OnLoad`をOverrideすることで、前のシーンからのパラメータの受け取りや、初期化を行うことができます。

# 画面遷移

```SampleTitle.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx.Async; // 必須

namespace CA2 {
    public class SampleTitle : SceneBase {
        
        public void GoMain() => SimpleSceneNavigator.Instance.GoForwardAsync<SampleMain>().Forget();
    }
}
```

タイトルはメインに行く機能のみを持つため、最小限の画面遷移コードが書かれています。

`SimpleSceneNavigator.Instance.GoForwardAsync<遷移先のシーンクラス>().Forget();`

ポイントはシーンクラスを渡すことでシーン遷移を行うことです。


## SimpleSceneNavigatorについて

ここが本題です。

```SimpleSceneNavigator.cs
using System.Collections;
using System.Collections.Generic;
using UniRx.Async;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace CA2 {
    public class SimpleSceneNavigator : MonoBehaviour {
        public static SimpleSceneNavigator Instance = null;

        void Awake () {
            if(Instance != null){
                Destroy(this.gameObject);
                return;
            }
            DontDestroyOnLoad (this.gameObject);
            Instance = this;
            goLoadingBarrier.SetActive(false);
        }
        
        [SerializeField] GameObject goLoadingBarrier;

        public async UniTask GoForwardAsync<T> (object options = null)
            where T : SceneBase { 
            goLoadingBarrier.SetActive(true);
            await SceneManager.LoadSceneAsync(typeof(T).Name);
            goLoadingBarrier.SetActive(false);
            var nextScene = Component.FindObjectOfType<T>();
            if(nextScene == null)
                throw new System.Exception(typeof(T).Name + " is Null");

            nextScene.OnLoad(options);
        }
    }
}
```

SimpleSceneNavigatorの機能は以下の3つです。

1. LoadSceneAsyncでシーン遷移
2. シーン遷移中はバリアを貼る
3. 遷移先のシーンクラスにパラメータを渡す

また構造は以下のようになっています
![スクリーンショット 2018-12-30 16.38.39.png](https://qiita-image-store.s3.amazonaws.com/0/113504/137b007d-59b0-f5b7-2cc1-07ce37c2f7c6.png "スクリーンショット 2018-12-30 16.38.39.png")

子にCanvasおよびBarrierを持ちます。
Barrierはタッチを防ぐためのものです。
サンプルではバリアが表示されていることをわかりやすく示すために半透明の黒いRawImageにしていますが、実際にゲーム作るときは色はいらないので透明で良いと思います。

このSceneNavigatorプレハブをシーン上にあらかじめ配置しておくことでシーン遷移が可能になります。

# パラメータを渡す

メインからリザルトへの遷移のときには, リザルトシーンでスコア(10)を表示するためにパラメータを渡しています。

![スクリーンショット 2018-12-30 16.44.54.png](https://qiita-image-store.s3.amazonaws.com/0/113504/59a0077d-63d8-7bf5-6318-713c197808df.png "スクリーンショット 2018-12-30 16.44.54.png")


```SampleMain.cs
    public class SampleMain : SceneBase {

        public void GoResult(){
            SimpleSceneNavigator.Instance.GoForwardAsync<SampleResult>(new SampleResult.Options(10)).Forget();
        }
    }
```

```SampleResult.cs
    public class SampleResult : SceneBase {

        public Text label;

        public class Options {
            public Options (int score) {
                this.Score = score;

            }
            public int Score { get; private set; }
        }

        public override void OnLoad (object options) {
            var op = options as Options;
            label.text = op.Score.ToString();
        }

        public void GoTitle () => SimpleSceneNavigator.Instance.GoForwardAsync<SampleTitle> ().Forget();
    }
```

SampleMainからSampleResultへ遷移するときに引数としてパラメータを渡しています

`new SampleResult.Options(10)`

これはSampleResultのOnLoadの引数として渡ってきます。

```SampleResult.cs
        public override void OnLoad (object options) {
        	// objectからOptionsにキャストする
            var op = options as Options; 
            label.text = op.Score.ToString();
        }
```


> 余談ですが、Options作るときにVisualStudioCodeでプロパティーからコンストラクターを自動生成する機能を発見しました。ほしかった機能！

# まとめ
画面遷移のサンプルを作りました。
個人開発やハッカソンでの開発の参考になれば幸いです。