[Unity Editor] Animaker #3 - create/remove Scriptable Object in script

qweasfjbv·2024년 12월 31일

UnityEditor

목록 보기
3/12

개요


저번에 preset을 로드하고 저장하는 기능까지 구현했지만, 새로운 프리셋을 만들거나 삭제하는 기능은 없습니다.
물론 프리셋이 저장된 폴더에 가서 만들거나 삭제해도 되지만, 이는 얘기치 않은 null exception 이나 여러 문제들을 일으킬 수 있기 때문에 Editor 차원에서 지원해야합니다.

구현


SO 생성

        public static AnimOptionSO CreateNewPreset(string name)
        {
            if (HasFilesInFolder(name + ".asset")) {
                return null;
            }

            AnimOptionSO newAsset = ScriptableObject.CreateInstance<AnimOptionSO>();
            AssetDatabase.CreateAsset(newAsset, Constants.PATH_PRESET + "/" + name + ".asset");
            AssetDatabase.SaveAssets();
            EditorUtility.FocusProjectWindow();
            Selection.activeObject = newAsset;
            return newAsset;
        }

        private static bool HasFilesInFolder(string name)
        {
            string[] assetGuids = AssetDatabase.FindAssets("", new[] { Constants.PATH_PRESET });

            foreach (string guid in assetGuids)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(guid);
                if (System.IO.Path.GetFileName(assetPath.ToUpper()) == name.ToUpper())
                {
                    return true; // File exists
                }
            }

            return false; // No file exists
        }

위의 CreateNewPreset 을 통해 전달받은 이름의 Preset을 만듭니다.
여기서 생기는 문제는, 이름이 겹칠 경우에 예기치 못한 동작들이 발생할 수 있기 때문에 미리 확인하고 막아야합니다.
여기서는 HasFilesInFolder 함수를 통해서 만들기 전에 같은 이름의 파일이 있는지 확인하도록 했습니다.

그 후에, SaveAssets 로 디스크에 저장하고, FocusProjectWindow 로 생성된 프리셋에 포커스를 줬습니다.


        if (GUILayout.Button("Save as new preset..."))
        {
            AnimOptionSO tmpSo = PresetLoader.CreateNewPreset(presetName);
            if (tmpSo == null)
            {
                EditorUtility.DisplayDialog("Denied",
                    "The preset with the same name already exists.", "OK");
            }
            else
            {
                SavePreset(tmpSo);
                scriptableObjects = PresetLoader.LoadScriptableObjects();
            }
        }

에디터에서는 위와같이 처리해주었습니다.
버튼을 누르면 CreateNewPreset 이 호출되고, 반환값이 null 이라면 Dialogue를, null이 아니라면 새로 만든 프리셋 SO에 현재 설정된 값들을 저장해주었습니다.

같은 이름의 프리셋을 생성하려고 하면 다이얼로그 창이 띄워지는 것을 확인할 수 있습니다.

SO 삭제

        public static void RemovePreset(string name)
        {
            AssetDatabase.DeleteAsset(Constants.PATH_PRESET + "/" + name + ".asset"); 
            AssetDatabase.SaveAssets();              
        }

프리셋을 삭제하는 부분은 생각보다 간단합니다.
파일의 경로를 토대로 삭제하고, SaveAssets 로 바로 디스크에 적용합니다.


        private void RemovePreset()
        {
            if (scriptableObjects.Count <= 1)
            {
                EditorUtility.DisplayDialog("Denied",
                    "You cannot delete this asset because it is the only one remaining.",
                    "OK");
                return;
            }

            PresetLoader.RemovePreset(selectedObject.name);
            scriptableObjects = PresetLoader.LoadScriptableObjects();
            selectedObject = scriptableObjects[0];
        }

에디터에서는 Null Exception 이 발생하지 않도록 주의해야합니다.
삭제했을 때, scriptableObjects 나 selectedObject 에서 삭제한 SO를 참조하고 있다면 에러가 발생합니다.
그러한 상황을 방지하기 위해서, 삭제한 직후에 SO들을 다시 로드하도록 구현했습니다.

마무리


에디터보다는 에셋을 다루는 부분이다보니 생각보다 빠르게 끝낼 수 있었습니다.

이제 남은 부분은 다음과 같습니다.

  • SO UX 개선
  • Show Preview window UX 개선
  • GUI Layout 개선

0개의 댓글