[Unity3D] シーンの切り替え・フェードイン/アウト

1, ヒエラルキーにPotalを作成し、RigidbodyとBoxColliderを作成する。各チェックとサイズを調整し、Prefabにする。
![46878420-B6EA-4548-83C1-C87610FB0AD3.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/860ba75c-5205-5d76-7a65-e7e84f5bc54d.jpeg)
![43CA53AF-2A80-49AA-B7E7-3E129A45508A_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/e8b5df81-72b4-507c-de5d-c7b053273f9c.jpeg)

2, PotalPrefabのLayer→IgnoreRaycastにする
![D156A67B-844E-4C61-9F4B-0F6D13BD62EA_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/a7a0dcc2-5aef-bbed-e840-389ea043ecad.jpeg)

3, SceneManagementフォルダを作成しPotal.csを作成し、下記スクリプトを書く
![3999E348-841C-422F-BC99-61D3F407ECEB_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/258a6308-a300-24b4-61cb-0bd6a829a35a.jpeg)

```C#:Portal
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace RPG.SceneManagement
{
    public class Potal : MonoBehaviour
    {
        [SerializeField] int sceneToLoad = -1;

        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Player")
            {
                SceneManager.LoadScene(sceneToLoad);
            }
        }
    }
}
```

4, PortalにPortal.csをアタッチし、SceneToLoadを1に設定する
![AA85A522-B2D6-444A-A3AB-6153C50F023B.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/ff3058cb-9691-5d97-9e1d-663b365cbae6.jpeg)

5, BuildSettingでSceneを設定する
![6EAAD168-B7A4-48F1-95A4-CA277C93016C_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/074ff66b-97f9-0634-d3f5-887c772a1316.jpeg)

別シーンから戻る
---
1,下記コードを追加する

```C#:Potal
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace RPG.SceneManagement
{
    public class Potal : MonoBehaviour
    {
        [SerializeField] int sceneToLoad = -1;

        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Player")
            {
                StartCoroutine(Transition());
            }
        }

        private IEnumerator Transition()
        {
            DontDestroyOnLoad(gameObject);
            yield return SceneManager.LoadSceneAsync(sceneToLoad);
            Debug.Log("Scene Loaded");
            Destroy(gameObject);
        }
    }
}
```
SpawnPointの作成
---
1, Potalオブジェクト内にSpawnPointを作成する
![C9D5C136-16D7-42E2-8EA2-59E485977A8F_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/c953e738-36ce-210f-92c4-c87280ee0e1f.jpeg)

2, SpawnPointを動かし、位置を決める
![B24D9B79-E0A7-40B1-81C7-BAF02846F7CE_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/5fd93091-6672-cb36-c099-2cbe1fbb9780.jpeg)

3, 下記コードを追加する

```C#:Portal
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.AI; // 追加

namespace RPG.SceneManagement
{
    public class Portal : MonoBehaviour
    {
        [SerializeField] int sceneToLoad = -1;
        [SerializeField] Transform spawnPoint;  //追加

        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Player")
            {
                StartCoroutine(Transition());
            }
        }

        private IEnumerator Transition()
        {
            DontDestroyOnLoad(gameObject);
            yield return SceneManager.LoadSceneAsync(sceneToLoad);

            Portal otherPortal = GetOtherPortal();    //追加
            UpdatePlayer(otherPortal);  //追加

            Destroy(gameObject);
        }

        //追加
        private void UpdatePlayer(Portal otherPortal)
        {
            GameObject player = GameObject.FindWithTag("Player");
            player.GetComponent<NavMeshAgent>().Warp(otherPortal.spawnPoint.position);
            player.transform.rotation = otherPortal.spawnPoint.rotation;
        }

        //追加
        private Portal GetOtherPortal()
        {
            foreach (Portal portal in FindObjectsOfType<Portal>())
            {
                if (portal == this) continue;

                return portal;
            }

            return null;
        }
    }
}
```
4, PotalのPotalスクリプトにSpawnPointを設定する
![A5A2C306-F5FA-42F6-9680-82AE3A60F508.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/494bf92c-a15b-8d14-84fe-d474a6fb0105.jpeg)

5, SpawnPotalを回転させてplayerの向きを決める
![9F963B44-5D4F-426F-AA80-664EE1A24063_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/958d7c7b-aa5f-d50a-7d70-f127ff7e3ba6.jpeg)

6, PlayerのNavMeshAgentを外す
![757A1E33-F833-4205-946C-AECF0AA64908_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/0f15755c-4214-60ea-2cda-541273e73809.jpeg)

複数のSpawnpointの切り替え
---
1, 下記コメント部のコードを追加

```C#:Portal
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.AI;

namespace RPG.SceneManagement
{
    public class Portal : MonoBehaviour
    {
        //追加
        enum DestinationIdentifier
        {
            A, B, C, D, E
        }

        [SerializeField] int sceneToLoad = -1;
        [SerializeField] Transform spawnPoint;
        [SerializeField] DestinationIdentifier destination; //追加

        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Player")
            {
                StartCoroutine(Transition());
            }
        }

        private IEnumerator Transition()
        {
            //追加
            if (sceneToLoad < 0)
            {
                Debug.LogError("Scene to load not set.");
                yield break;
            }

            DontDestroyOnLoad(gameObject);
            yield return SceneManager.LoadSceneAsync(sceneToLoad);

            Portal otherPortal = GetOtherPortal();
            UpdatePlayer(otherPortal);
            Destroy(gameObject);
        }


        private void UpdatePlayer(Portal otherPortal)
        {
            GameObject player = GameObject.FindWithTag("Player");
            player.GetComponent<NavMeshAgent>().Warp(otherPortal.spawnPoint.position);
            player.transform.rotation = otherPortal.spawnPoint.rotation;
            //player.transform.position = otherPortal.spawnPoint.position;
        }

        private Portal GetOtherPortal()
        {
            foreach (Portal portal in FindObjectsOfType<Portal>())
            {
                if (portal == this) continue;
                if (portal.destination != destination) continue;

                return portal;
            }
            return null;
        }
    }
}
```

2, Portalを作成し、Destinationのアルファベットを合わせる
![8C722C7D-7FB2-4EDA-AC52-BC0E0089FE1F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/c089c25e-0e4f-e926-5f89-89177f4dc81f.jpeg)

フェードアウトの作成
---
1, ヒエラルキーにUI→Canvasを作成する
![63AA0FF7-5FDE-4F01-A41F-93E760DEC65E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/76075d97-93d5-a2ba-fb51-06538c71e349.jpeg)

2, Faderに名前を変更し、CanvasGroupコンポーネントをつける
![714B2CAB-1BAF-46B4-8C83-6F7C70462D63.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/82026e41-1ed6-cb18-e417-463410f15f2d.jpeg)

3, imageコンポーネントをつける
![0A1C81EC-10CB-4635-982E-FC6EEFA5C143.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/809e57b5-c646-2631-bca5-38ca910947cb.jpeg)

4, Fader.csを作成し、下記スクリプトを書く

```C#:Fader
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.SceneManagement
{
    public class Fader : MonoBehaviour
    {
        CanvasGroup canvasGroup;

        private void Start()
        {
            canvasGroup = GetComponent<CanvasGroup>();

            StartCoroutine(FadeOutIn());
        }

        IEnumerator FadeOutIn()
        {
            yield return FadeOut(3f);
            Debug.Log("Fade out");
            yield return FadeIn(1f);
            Debug.Log("Fade in");
        }

        public IEnumerator FadeOut(float time)
        {
            while (canvasGroup.alpha < 1)
            {
                canvasGroup.alpha += Time.deltaTime / time;
                yield return null;
            }
        }

        public IEnumerator FadeIn(float time)
        {
            while (canvasGroup.alpha > 0)
            {
                canvasGroup.alpha -= Time.deltaTime / time;
                yield return null;
            }
        }
    }
}
```
5, Faderスクリプトをアタッチする
![9392841E-2600-41A9-A0B4-CB937C890FB9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/fb4cf589-7bd6-8bcf-e05e-498fb030e285.jpeg)

6,CoreとFaderをPrefabにする
![A9D922B8-CB05-41CB-A4BB-29A4056043C0_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/620ad124-110e-cc68-8979-c6c09a503681.jpeg)

7, Scene2にCorePrefabから新しく作成する
![475CB893-EE17-452F-B7E0-EAC8FF6A0084_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/c48b2f17-9d23-68ee-c6ce-8f9dd0dd7ac4.jpeg)

8, PersistentObjectを作成し、Faderを子オブジェクトにしてからPrefabにする
![EA73B877-F3A6-47EA-8BA1-2A402727716D_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/601369ff-74c9-88fe-8550-b837583bae50.jpeg)

9,PersistentObjectSpawner.csを作成し、Coreにアタッチする
![8352ECDC-4B79-438A-834C-5D11EF644D64.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/ef459021-3895-b83b-2f41-8b79d6f56b5f.jpeg)

10, 各スクリプトにコードを書く

```C#:PersistentObjectSpawner
using System;
using UnityEngine;

namespace RPG.Core
{
    public class PersistentObjectSpawner : MonoBehaviour
    {
        [SerializeField] GameObject persistentObjectPrefab;

        static bool hasSpawned = false;

        private void Awake()
        {
            if (hasSpawned) return;

            SpawnPersistentObjects();

            hasSpawned = true;
        }

        private void SpawnPersistentObjects()
        {
            GameObject persistentObject = Instantiate(persistentObjectPrefab);
            DontDestroyOnLoad(persistentObject);
        }
    }
}
```

```C#:Fader
using System.Collections;
using UnityEngine;
namespace RPG.SceneManagement
{
    public class Fader : MonoBehaviour
    {
        CanvasGroup canvasGroup;

        private void Start()
        {
            canvasGroup = GetComponent<CanvasGroup>();
        }

        public IEnumerator FadeOut(float time)
        {
            while (canvasGroup.alpha < 1)
            {
                canvasGroup.alpha += Time.deltaTime / time;
                yield return null;
            }
        }
        public IEnumerator FadeIn(float time)
        {
            while (canvasGroup.alpha > 0)
            {
                canvasGroup.alpha -= Time.deltaTime / time;
                yield return null;
            }
        }
    }
}
```

```C#:Portal
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
namespace RPG.SceneManagement
{
    public class Portal : MonoBehaviour
    {
        enum DestinationIdentifier
        {
            A, B, C, D, E
        }
        [SerializeField] int sceneToLoad = -1;
        [SerializeField] Transform spawnPoint;
        [SerializeField] DestinationIdentifier destination;
        [SerializeField] float fadeOutTime = 1f;
        [SerializeField] float fadeInTime = 2f;
        [SerializeField] float fadeWaitTime = 0.5f;

        //private void Start()
        //{
        //    transform.parent = null;
        //    DontDestroyOnLoad(gameObject);
        //}

        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Player")
            {
                StartCoroutine(Transition());
            }
        }
        private IEnumerator Transition()
        {
            if (sceneToLoad < 0)
            {
                Debug.LogError("Scene to load not set.");
                yield break;
            }

            transform.parent = null;
            DontDestroyOnLoad(gameObject);

            Fader fader = FindObjectOfType<Fader>();

            yield return fader.FadeOut(fadeOutTime);
            yield return SceneManager.LoadSceneAsync(sceneToLoad);

            Portal otherPortal = GetOtherPortal();
            UpdatePlayer(otherPortal);

            yield return new WaitForSeconds(fadeWaitTime);
            yield return fader.FadeIn(fadeInTime);

            Destroy(gameObject);
        }

        private void UpdatePlayer(Portal otherPortal)
        {
            GameObject player = GameObject.FindWithTag("Player");
            player.GetComponent<NavMeshAgent>().Warp(otherPortal.spawnPoint.position);
            player.transform.rotation = otherPortal.spawnPoint.rotation;
        }
        private Portal GetOtherPortal()
        {
            foreach (Portal portal in FindObjectsOfType<Portal>())
            {
                if (portal == this) continue;
                if (portal.destination != destination) continue;
                return portal;
            }
            return null;
        }
    }
}
```