FarmRPG　Unity Preserving Scene State  Part1 　Stateの作成と保存

# 概要
今回は、Sceneを切り替えた際に元の状態（State）を保存する実装を行います。
以下実装後の様子です。

![0616.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/281a3104-e767-0b54-055c-37b96aee2d2c.gif)




# 開発環境

IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10

# UnityEditor上の設定

## SceneItemManagerオブジェクトを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/fa3a2d13-3c84-210c-f872-77e76beb6045.png)


## SaveLoadManagerオブジェクトを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/b354f663-1de4-68a7-052c-9acfcfe114e4.png)

# 実装のポイント
## Stateについて

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/29dffe0f-a8a3-5cde-2cc1-357ab9034b7d.png)


クラス名|説明
----|-----
GameObjectSave|ゲームで使うシーンのstateを束ねるクラス
SceneSave|シーン毎のクラス。１シーンのマスの情報（未実装）、落ちているアイテム情報を定義する
SceneItem|Scene上のアイテムを定義するクラス UnityEdtiorのインスペクター上でItem
Vector3Serializable|UnityのVector3をPure C#に変換するためのクラス

インスペクターと上クラスの対応関係

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/2ed18bee-3029-531e-1879-a11cc55d1a3a.png)


## Preserving部分について
Stateを保存、復元するためのクラスたちです。
Stateの種類を増やしたい場合、SceneItemManagerを参考にして新規クラスを作れば実装できます。ISaveableインターフェイスとSaveLoadManagerは変更する必要ありません。

クラス名|説明
----|-----
ISaveable|インターフェイス。GameObjectSaveオブジェクトに対して読み込み保存を行う処理（ISaveableStoreScene）を定義する。
SceneItemManager| ISaveableの実装先その１。SceneのItemについての処理がある。
GridPropertiesManager| ISaveableの実装先その2。未だ未実装。
SaveLoadManager|ISaveableStoreSceneを実行するメソッドがあるクラス
SceneControllerManager|FadeAndSwitchSceneメソッドでStateの保存処理を実行


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/870f95f6-ebaa-9cf5-5eb4-9c821fdae720.png)


以下画像はStateを保存するときの処理の流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/436cf237-04f2-3e40-be37-d72288615161.png)



# コード部分

## State部分
### GameObjectSave

```diff_c_sharp:GameObjectSave.cs

using System.Collections.Generic;

[System.Serializable]
public class GameObjectSave
{
       public Dictionary<string, SceneSave> sceneData;

       public GameObjectSave()
       {
              sceneData = new Dictionary<string, SceneSave>();
       }

       public GameObjectSave(Dictionary<string, SceneSave> sceneData)
       {
              this.sceneData = sceneData;
       }
}


```


### SceneSave

```diff_c_sharp:SceneSave.cs

using System.Collections.Generic;

[System.Serializable]
public class SceneSave
{
    public Dictionary<string,List<SceneItem>> listSceneItemDictionary;
}


```


### SceneItem

```diff_c_sharp:SceneItem.cs
[System.Serializable]
public class SceneItem
{
    public int itemCode;
    public Vector3Serializable position;
    public string itemName;

    public SceneItem()
    {
        position = new Vector3Serializable();
    }
}

```



### Vector3Serializable

```diff_c_sharp:Vector3Serializable.cs
[System.Serializable]
public class Vector3Serializable
{
    public float x, y, z;

    public Vector3Serializable(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3Serializable()
    {
    }
}
```



## Preserving部分



### SceneControllerManager

```diff_c_sharp:SceneControllerManager.cs

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.SceneManagement;

public class SceneControllerManager :SingletonMonobehaviour<SceneControllerManager>
{
    private bool isFading;
    [SerializeField] private float fadeDuration = 1f;
    [SerializeField] private CanvasGroup faderCanvasGroup;
    [SerializeField] private Image faderImage;
    public SceneName startingSceneName;
    
    private IEnumerator Start(){}

    private IEnumerator Fade(float finalAlpha){}    
    
    private IEnumerator LoadSceneAndSetActive(string sceneName){}

    private IEnumerator FadeAndSwitchScenes(string sceneName, Vector3 spawnPosition)
    {
        yield return StartCoroutine(Fade(1f));
+        SaveLoadManager.Instance.StoreCurrentSceneData();    
    
        Player.Instance.gameObject.transform.position = spawnPosition;
        yield return SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene().buildIndex);

        yield return StartCoroutine(LoadSceneAndSetActive(sceneName));
        
        EventHandler.CallAfterSceneLoadEvent();       
        yield return StartCoroutine(Fade(0f));
    }

    public void FadeAndLoadScene(string sceneName, Vector3 spawnPosition)
    {
        if (!isFading)
        {
            StartCoroutine(FadeAndSwitchScenes(sceneName, spawnPosition));
        }
    }
    
}





```




### SaveLoadManager

```diff_c_sharp:SaveLoadManager.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SaveLoadManager : SingletonMonobehaviour<SaveLoadManager>
{
    public List<ISaveable> iSaveableObjectList;

    protected override void Awake()
    {
        base.Awake();
        iSaveableObjectList = new List<ISaveable>();
    }

    public void StoreCurrentSceneData()
    {
        foreach (ISaveable iSaveableObject in iSaveableObjectList)
        {
            iSaveableObject.ISaveableStoreScene(SceneManager.GetActiveScene().name);
        }
    }    
}
```

### ISaveable

```diff_c_sharp:ISaveable.cs

public interface ISaveable
{
    void ISaveableStoreScene(string sceneName);

    void ISaveableRestoreScene(string sceneName);
}

```

### SceneItemsManager

```diff_c_sharp:SceneItemsManager.cs
using System.Collections.Generic;
using UnityEngine;


[RequireComponent(typeof(GenerateGUID))]
public class SceneItemsManager : SingletonMonobehaviour<SceneItemsManager>,ISaveable
{
    private Transform parentItem;
    [SerializeField] private GameObject itemPrefab = null;
    private string _iSaveableUniqueID;
    public string ISaveableUniqueID
    {
        get { return _iSaveableUniqueID; }
        set {_iSaveableUniqueID = value; }
    }

    private GameObjectSave _gameObjectSave;
    public GameObjectSave GameObjectSave
    {
        get { return _gameObjectSave;}
        set { _gameObjectSave = value;}
    }

    private void AfterSceneLoad()
    {
        parentItem = GameObject.FindGameObjectWithTag(Tags.ItemParentTransform).transform;
    }

    protected override void Awake()
    {
        base.Awake();
        ISaveableUniqueID = GetComponent<GenerateGUID>().GUID;
        GameObjectSave = new GameObjectSave();
    }

    private void OnDisable()
    {
        ISaveableDeregister();
        EventHandler.AfterSceneLoadEvent -= AfterSceneLoad;
    }

    private void OnEnable()
    {
        ISaveableRegister();
        EventHandler.AfterSceneLoadEvent += AfterSceneLoad;
    }

    public void ISaveableDeregister()
    {
        SaveLoadManager.Instance.iSaveableObjectList.Remove(this);
    }


    public void ISaveableRegister()
    {
        // 以下処理を実行しないとiSaveableObjectListがnullになるので必要
        SaveLoadManager.Instance.iSaveableObjectList.Add(this);
    }

    public void ISaveableStoreScene(string sceneName)
    {
        GameObjectSave.sceneData.Remove(sceneName);

        List<SceneItem> sceneItemList = new List<SceneItem>();
        Item[] itemsInScene = FindObjectsOfType<Item>();

        foreach (Item item in itemsInScene)
        {
            SceneItem sceneItem = new SceneItem();
            sceneItem.itemCode = item.ItemCode;
            sceneItem.position = new Vector3Serializable(item.transform.position.x, item.transform.position.y,
                item.transform.position.z);
            sceneItem.itemName = item.name;
            
            sceneItemList.Add(sceneItem);
        }

        SceneSave sceneSave = new SceneSave();
        sceneSave.listSceneItemDictionary = new Dictionary<string, List<SceneItem>>();
        sceneSave.listSceneItemDictionary.Add("sceneItemList",sceneItemList);
        
        GameObjectSave.sceneData.Add(sceneName, sceneSave);
    }    
}


```







# 参考

## C#

### interface

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/interface#interface-inheritance

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/de103a97-a22b-ca1a-c731-44d9ec16b0dd.png)


### 

## Unity Editor コンポーネント

## Unity スクリプト
###

## その他

Section10 41 42


github コミット分（個人確認用　privateなので見れません）

Stateの定義

https://github.com/RYA234/LearnFarmRPG/commit/404cf5124f3e7b8086fbc06bf987c46b317a5c93

Stateの保存

https://github.com/RYA234/LearnFarmRPG/commit/b5284f8ee86bc9d8eca6165a1fc34ecc8ce3d55f