Last active
April 30, 2022 09:33
-
-
Save acoppes/7529b3486f939eb0825d1a0a2ab52170 to your computer and use it in GitHub Desktop.
Unity Editor helper methods to Refactor Data stored in Prefabs, Scenes and other Assets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UnityEditor; | |
using UnityEditor.SceneManagement; | |
namespace Utils.Editor | |
{ | |
public static class RefactorTools | |
{ | |
public static void RefactorAsset<T>(Func<T, bool> callback) where T : UnityEngine.Object | |
{ | |
var guids = AssetDatabase.FindAssets($"t:{typeof(T)}", null); | |
var assets = guids.Select(g => AssetDatabase.LoadAssetAtPath<T>( | |
AssetDatabase.GUIDToAssetPath(g))).ToList(); | |
try | |
{ | |
var total = assets.Count; | |
EditorUtility.DisplayProgressBar($"Refactoring {total} assets of type {typeof(T).Name}", "Start", 0); | |
for (var i = 0; i < assets.Count; i++) | |
{ | |
var asset = assets[i]; | |
EditorUtility.DisplayProgressBar($"Refactoring {assets.Count} assets of type {typeof(T).Name}", | |
asset.name, | |
i / (float)total); | |
var result = callback(asset); | |
if (result) | |
{ | |
EditorUtility.SetDirty(asset); | |
} | |
} | |
AssetDatabase.SaveAssets(); | |
} | |
finally | |
{ | |
EditorUtility.ClearProgressBar(); | |
} | |
} | |
public static void RefactorMonoBehaviour<T>(bool includeScenes, | |
Func<GameObject, bool> callback) where T : Component | |
{ | |
var guids = AssetDatabase.FindAssets($"t:prefab", null); | |
var prefabs = guids | |
.Select(g => AssetDatabase.LoadAssetAtPath<GameObject>( | |
AssetDatabase.GUIDToAssetPath(g))).ToList(); | |
// Ignore prefabs without component T | |
prefabs = prefabs.Where(p => p.GetComponentInChildren<T>(true) != null).ToList(); | |
// We sort by no variant prefabs first | |
prefabs.Sort(delegate(GameObject a, GameObject b) | |
{ | |
var aIsVariant = PrefabUtility.IsPartOfVariantPrefab(a); | |
var bIsVariant = PrefabUtility.IsPartOfVariantPrefab(b); | |
if (!aIsVariant && bIsVariant) | |
return -1; | |
if (aIsVariant && !bIsVariant) | |
return 1; | |
// if both no variants or both variants, we just use the name to compare just to be consistent. | |
return a.name.CompareTo(b.name); | |
}); | |
prefabs.ForEach(delegate(GameObject o) | |
{ | |
Debug.Log(o.name); | |
}); | |
try | |
{ | |
var total = prefabs.Count; | |
EditorUtility.DisplayProgressBar($"Refactoring {total} prefabs with {typeof(T).Name}", "Start", 0); | |
for (var i = 0; i < prefabs.Count; i++) | |
{ | |
var prefab = prefabs[i]; | |
EditorUtility.DisplayProgressBar($"Refactoring {prefabs.Count} assets of type {typeof(T).Name}", | |
prefab.name, | |
i / (float)total); | |
var contents = PrefabUtility.LoadPrefabContents(AssetDatabase.GetAssetPath(prefab)); | |
var result = callback(contents); | |
if (result) | |
{ | |
PrefabUtility.SaveAsPrefabAsset(contents, AssetDatabase.GetAssetPath(prefab)); | |
} | |
PrefabUtility.UnloadPrefabContents(contents); | |
} | |
} | |
finally | |
{ | |
EditorUtility.ClearProgressBar(); | |
} | |
// Then iterate in all scenes (if include scenes is true) | |
if (!includeScenes) | |
return; | |
var loadedScenesList = new List<string>(); | |
var loadedScenes = EditorSceneManager.sceneCount; | |
var activeScene = EditorSceneManager.GetActiveScene().path; | |
for (var i = 0; i < loadedScenes; i++) | |
{ | |
var scene = EditorSceneManager.GetSceneAt(i); | |
loadedScenesList.Add(scene.path); | |
} | |
var allScenesGuids = new List<string>(); | |
// Here we filter by all assets of type scene but under Assets folder to avoid all other scenes from | |
// external packages. | |
allScenesGuids.AddRange(AssetDatabase.FindAssets("t:scene", new [] | |
{ | |
"Assets" | |
})); | |
EditorUtility.DisplayProgressBar($"Refactoring {allScenesGuids.Count} scenes", "Starting...", 0); | |
var allScenesCount = allScenesGuids.Count; | |
for (var i = 0; i < allScenesCount; i++) | |
{ | |
var sceneGuid = allScenesGuids[i]; | |
var scenePath = AssetDatabase.GUIDToAssetPath(sceneGuid); | |
try | |
{ | |
EditorUtility.DisplayProgressBar($"Refactoring {allScenesGuids.Count} scenes", scenePath, | |
i / (float) allScenesCount); | |
var scene = EditorSceneManager.OpenScene(scenePath, | |
OpenSceneMode.Single); | |
var componentsList = new List<T>(); | |
// We can iterate over root objects and collect stuff to run the refactor over | |
var rootObjects = scene.GetRootGameObjects(); | |
for (var j = 0; j < rootObjects.Length; j++) | |
{ | |
var go = rootObjects[j]; | |
var components = go.GetComponentsInChildren<T>(true); | |
componentsList.AddRange(components.ToList()); | |
} | |
var modified = false; | |
foreach (var component in componentsList) | |
{ | |
var gameObject = component.gameObject; | |
var result = callback(gameObject); | |
if (result) | |
{ | |
modified = true; | |
if (component != null) | |
{ | |
EditorUtility.SetDirty(component); | |
} | |
else if (gameObject != null) | |
{ | |
EditorUtility.SetDirty(gameObject); | |
} | |
} | |
} | |
if (modified) | |
{ | |
EditorSceneManager.MarkSceneDirty(scene); | |
EditorSceneManager.SaveScene(scene); | |
} | |
} | |
finally | |
{ | |
EditorUtility.ClearProgressBar(); | |
} | |
} | |
var newActiveScene = EditorSceneManager.OpenScene(activeScene, | |
OpenSceneMode.Single); | |
for (var i = 0; i < loadedScenes; i++) | |
{ | |
if (loadedScenesList[i].Equals(activeScene)) | |
continue; | |
EditorSceneManager.OpenScene(loadedScenesList[i], | |
OpenSceneMode.Additive); | |
} | |
EditorSceneManager.SetActiveScene(newActiveScene); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment