Unity シーンを管理してトランジションを行う。uGUI

設計

シーン遷移する前にデータ読み込み(通信処理)でコルーチンを使うと思うので、トランジションはIEnumeratorで書く。

とりあえず適当にインターフェイスとか定義

using System.Collections;
using UnityEngine;

public interface ITransition
{
    IEnumerator onStartTransition();
    IEnumerator onEndTransition();
}
public abstract class ATransition : MonoBehaviour, ITransition
{
    public abstract IEnumerator onStartTransition();
    public abstract IEnumerator onEndTransition();
}

シーンマネージャで各シーンから遷移処理の呼び出し

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class SceneManager : MonoBehaviour
{
    #region singleton
    private static SceneManager instance = null;
    public static SceneManager Instance
    {
        get
        {
            if (instance == null)
                instance = (SceneManager)FindObjectOfType(typeof(SceneManager));
            return instance;
        }
    }
    void Awake()
    {
        if (SceneManager.Instance != this)
            Destroy(this);
    }
    #endregion

    [SerializeField]
    List<ATransition> cache;
    public T Cache<T>() where T : ATransition
    {
        return (T)cache.FirstOrDefault(x => (x as T) != null);
    }

    public void Transition(ATransition now, ATransition next)
    {
        StartCoroutine(_Transition(now, next));
    }
    private IEnumerator _Transition(ATransition now, ATransition next)
    {
        if (now != null)
            yield return now.onEndTransition();

        yield return next.onStartTransition();

        Debug.Log(string.Format("transition: end -> {0}, start -> {1}", now == null ? "null" : now.GetType().Name, next.GetType().Name));
    }
}

遷移用にScene1,Scene2クラス作成

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

public class Scene1 : ATransition
{
    public override IEnumerator onStartTransition()
    {
        gameObject.SetActive(true);
        yield return null;
    }
    public override IEnumerator onEndTransition()
    {
        gameObject.SetActive(false);
        yield return null;
    }
    public void onclick()
    {
        var next = SceneManager.Instance.Cache<Scene2>();
        SceneManager.Instance.Transition(this, next);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Scene2 : ATransition
{
    public override IEnumerator onStartTransition()
    {
        gameObject.SetActive(true);
        yield return null;
    }
    public override IEnumerator onEndTransition()
    {
        gameObject.SetActive(false);
        yield return null;
    }
    public void onclick()
    {
        var next = SceneManager.Instance.Cache<Scene1>();
        SceneManager.Instance.Transition(this, next);
    }
}

ヒエラルキー

  1. canvas作成。
  2. canvas内でCreate Empty > 名前をScene1に変更。その子でボタン作成。
  3. Scene1にScene1.csをadd compenent。ボタンにonclickを登録。
  4. canvas内でCreate Empty > 名前をScene2に変更。その子でボタン作成。
  5. Scene2にScene2.csをadd compenent。ボタンにonclickを登録。

  6. 適当にCreate Empty > SceneManager.csをadd component。

  7. SceneManager.csのインスペクターのキャッシュの部分に上記で作成したScene1,Scene2を追加。
  8. 適当にエントリーポイント用のスクリプト作って開始。※Scene1,Scene2は非アクティブ状態にしておく。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EntryPoint : MonoBehaviour {
    void Start() {
        var next = SceneManager.Instance.Cache<Scene1>();
        SceneManager.Instance.Transition(null, next);
    }
}

ボタンを押せば遷移するようになる。

シーンにCanvasGroupをadd componentして、alphaのアニメーションなどを行えばそれっぽくなる。

[SerializeField]
CanvasGroup group;

public override IEnumerator onStartTransition() {
    gameObject.SetActive(true);
    yield return startAnimation();
}
public override IEnumerator onEndTransition() {
    yield return endAnimation();
    gameObject.SetActive(false);
}

private IEnumerator startAnimation() {
    group.blockRaycasts = false;
    var start = Time.time;

    var now = start;
    while (now - start < 1.0f) {
        group.alpha = Mathf.Lerp(0f, 1f, now - start);
        yield return null;
        now = Time.time;
    }

    group.alpha = 1f;
    group.blockRaycasts = true;
}
private IEnumerator endAnimation() {
    group.blockRaycasts = false;
    var start = Time.time;

    var now = start;
    while (now - start < 1.0f) {
        group.alpha = Mathf.Lerp(1f, 0f, now - start);
        yield return null;
        now = Time.time;
    }
    
    group.alpha = 0f;
    group.blockRaycasts = true;
}