AutoSave.cs を Unity 2021.3.6f1 LTS に対応してみた

# 目的

非常に有名な tsubaki さんが作られた UnityEditor におけるシーンのオートセーブ機能
https://tsubakit1.hateblo.jp/entry/20140131/1391094449

こちらが、Unity 2021.3.6f1 LTS 版において、
結構な数の API が Obsolete となっていた。
Unity 2021.3.6f1 LTS 環境でエラーが出ないようにそれっぽく対応してみたので、
備忘として残しておくのが目的。

# 内容

これらを Asset/ 以下の任意の Editor/ フォルダに格納して使う。
設定方法や使い方は、ネット上にたくさんあるものと同じなので割愛。

```C#:AutoSave.cs
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

[InitializeOnLoad]
public static class AutoSave {
	public static readonly string manualSaveKey = "autosave@manualSave";
	static double nextTime = 0;
	static bool isChangedHierarchy = false;

	static AutoSave() {
		IsManualSave = true;
		EditorApplication.playModeStateChanged += (PlayModeStateChange state) => {
			if (state == PlayModeStateChange.ExitingEditMode) {
				if (IsAutoSave && EditorApplication.isPlayingOrWillChangePlaymode) {
					IsManualSave = false;
					if (IsSavePrefab) {
						AssetDatabase.SaveAssets();
					}
					if (IsSaveScene) {
						EditorSceneManager.SaveScene(SceneManager.GetActiveScene());
					}
					IsManualSave = true;
				}
				isChangedHierarchy = false;
			} else if (state == PlayModeStateChange.EnteredPlayMode) {
				// PlayModeStateChange.ExitingEditMode だとログがでない…
				Debug.Log("[SceneAutoSave] saved time=" + System.DateTime.Now);
			}
		};
		nextTime = EditorApplication.timeSinceStartup + Interval;
		EditorApplication.update += () => {
			if (isChangedHierarchy && nextTime < EditorApplication.timeSinceStartup) {
				nextTime = EditorApplication.timeSinceStartup + Interval;
				IsManualSave = false;
				if (IsSaveSceneTimer && IsAutoSave && !EditorApplication.isPlaying) {
					if (IsSavePrefab) {
						AssetDatabase.SaveAssets();
					}
					if (IsSaveScene) {
						Debug.Log("[SceneAutoSave] saved time=" + System.DateTime.Now);
						EditorSceneManager.SaveScene(SceneManager.GetActiveScene());
					}
				}
				isChangedHierarchy = false;
				IsManualSave = true;
			}
		};

		EditorApplication.hierarchyChanged += () => {
			if (EditorApplication.isPlaying == false) {
				isChangedHierarchy = true;
			}
		};
	}

	public static bool IsManualSave {
		get {
			return EditorPrefs.GetBool(manualSaveKey);
		}
		private set {
			EditorPrefs.SetBool(manualSaveKey, value);
		}
	}

	private static readonly string autoSave = "auto save";
	static bool IsAutoSave {
		get {
			string value = EditorUserSettings.GetConfigValue(autoSave);
			return !string.IsNullOrEmpty(value) && value.Equals("True");
		}
		set {
			EditorUserSettings.SetConfigValue(autoSave, value.ToString());
		}
	}

	private static readonly string autoSavePrefab = "auto save prefab";
	static bool IsSavePrefab {
		get {
			string value = EditorUserSettings.GetConfigValue(autoSavePrefab);
			return !string.IsNullOrEmpty(value) && value.Equals("True");
		}
		set {
			EditorUserSettings.SetConfigValue(autoSavePrefab, value.ToString());
		}
	}

	private static readonly string autoSaveScene = "auto save scene";
	static bool IsSaveScene {
		get {
			string value = EditorUserSettings.GetConfigValue(autoSaveScene);
			return !string.IsNullOrEmpty(value) && value.Equals("True");
		}
		set {
			EditorUserSettings.SetConfigValue(autoSaveScene, value.ToString());
		}
	}

	private static readonly string autoSaveSceneTimer = "auto save scene timer";
	static bool IsSaveSceneTimer {
		get {
			string value = EditorUserSettings.GetConfigValue(autoSaveSceneTimer);
			return !string.IsNullOrEmpty(value) && value.Equals("True");
		}
		set {
			EditorUserSettings.SetConfigValue(autoSaveSceneTimer, value.ToString());
		}
	}

	private static readonly string autoSaveInterval = "save scene interval";
	static int Interval {
		get {

			string value = EditorUserSettings.GetConfigValue(autoSaveInterval);
			if (value == null) {
				value = "60";
			}
			return int.Parse(value);
		}
		set {
			if (value < 60) {
				value = 60;
			}
			EditorUserSettings.SetConfigValue(autoSaveInterval, value.ToString());
		}
	}

	[SettingsProvider]
	static SettingsProvider CreateExampleOnGUI() {
		var provider = new SettingsProvider("Auto Save", SettingsScope.User) {
			label = "Auto Save",
			guiHandler = searchContext => {
				bool isAutoSave = EditorGUILayout.BeginToggleGroup("auto save", IsAutoSave);
				IsAutoSave = isAutoSave;
				EditorGUILayout.Space();
				IsSavePrefab = EditorGUILayout.ToggleLeft("save prefab", IsSavePrefab);
				IsSaveScene = EditorGUILayout.ToggleLeft("save scene", IsSaveScene);
				IsSaveSceneTimer = EditorGUILayout.BeginToggleGroup("save scene interval", IsSaveSceneTimer);
				Interval = EditorGUILayout.IntField("interval(sec) min60sec", Interval);
				EditorGUILayout.EndToggleGroup();
				EditorGUILayout.EndToggleGroup();
			},
			keywords = new HashSet<string>(new[] { "AutoSave", "Auto Save" }),
		};
		return provider;
	}

	[MenuItem("File/Backup/Backup")]
	public static void Backup() {
		string scenePath = SceneManager.GetActiveScene().path;
		string expoertPath = "Backup/" + scenePath;
		Directory.CreateDirectory(Path.GetDirectoryName(expoertPath));
		if (string.IsNullOrEmpty(scenePath)) {
			return;
		}
		byte[] data = File.ReadAllBytes(scenePath);
		File.WriteAllBytes(expoertPath, data);
	}

	[MenuItem("File/Backup/Rollback")]
	public static void RollBack() {
		string scenePath = SceneManager.GetActiveScene().path;
		string expoertPath = "Backup/" + scenePath;
		byte[] data = File.ReadAllBytes(expoertPath);
		File.WriteAllBytes(scenePath, data);
		AssetDatabase.Refresh(ImportAssetOptions.Default);
	}
}
```

```C#:SceneBackup.cs
using System.Collections;
using UnityEditor;
using UnityEngine;

public class SceneBackup : UnityEditor.AssetModificationProcessor {
	static string[] OnWillSaveAssets(string[] paths) {
		bool manualSave = AutoSave.IsManualSave;
		if (manualSave) {
			AutoSave.Backup();
		}
		return paths;
	}
}
```