Created
November 1, 2017 12:54
-
-
Save ByronMayne/abf5f29ae65f6bda460485bfa2e2551c to your computer and use it in GitHub Desktop.
A simple to use script for Unity that lets you loop over all scenes and/or prefabs and get a callback for each Behaviour, Property, and/or GameObject.
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; | |
using System.Collections.Generic; | |
using UnityEditor; | |
using UnityEditor.SceneManagement; | |
using UnityEngine; | |
using UnityEngine.SceneManagement; | |
public delegate void VistedDelegate<T>(T instance); | |
public interface IProgressHandler | |
{ | |
void ClearProgressBar(); | |
void UpdateProgress(int stepCount, string stepName, string message, int totalSteps, float progress); | |
} | |
public class AssetIterator : IProgressHandler | |
{ | |
public class AbortIteration : Exception { } | |
[Flags] | |
public enum IterationType | |
{ | |
None = 0, | |
Prefabs = 1 << 1, | |
Scenes = 1 << 2, | |
All = Prefabs | Scenes | |
} | |
private IterationType m_IterationType; | |
private IProgressHandler m_ProgressHandler; | |
private bool m_IsRunning; | |
private List<Behaviour> m_CachedBehaviours; | |
private int m_TotalSteps = 0; | |
private int m_CurrentStep = 0; | |
public VistedDelegate<GameObject> onGameObjectVisited; | |
public VistedDelegate<Component> onBehaviourVisited; | |
public VistedDelegate<SceneAsset> onSceneVisted; | |
public VistedDelegate<SerializedProperty> onPropertyVisited; | |
public IterationType iterationType | |
{ | |
get { return m_IterationType; } | |
set { m_IterationType = value; } | |
} | |
/// <summary> | |
/// Gets or sets the progress handler. By default this instance handles drawing | |
/// it's own progress bar. If this is set to null no progress bar is shown. To | |
/// revert to default set it back to this instance. 'this.progressHandler = this'. To | |
/// support escaping throw an <see cref="AbortIteration"/> to cancel iteration and | |
/// cause no errors to be logged. | |
/// </summary> | |
public IProgressHandler progressHandler | |
{ | |
get { return m_ProgressHandler; } | |
set { m_ProgressHandler = value; } | |
} | |
public void Start() | |
{ | |
m_IsRunning = true; | |
if (m_IterationType == IterationType.None) | |
{ | |
Debug.LogWarning("iterationType is currently set to None so there is no point running this script. Returning early."); | |
return; | |
} | |
if (onGameObjectVisited == null && | |
onBehaviourVisited == null && | |
onSceneVisted == null && | |
onPropertyVisited == null) | |
{ | |
Debug.LogWarning("No callbacks were subscribed for the Asset Iterator there is no point of running the script."); | |
return; | |
} | |
if (onSceneVisted == null && (m_IterationType & IterationType.Scenes) != IterationType.Scenes) | |
{ | |
Debug.LogWarning("You subscribed to the onSceneVistedDelegate however the IterationType was set to prefabs only. This callback will not be invoked."); | |
} | |
// Setup | |
m_CachedBehaviours = new List<Behaviour>(); | |
if ((m_IterationType & IterationType.Prefabs) == IterationType.Prefabs) | |
m_TotalSteps++; | |
if ((m_IterationType & IterationType.Scenes) == IterationType.Scenes) | |
m_TotalSteps++; | |
try | |
{ | |
if ((m_IterationType & IterationType.Prefabs) == IterationType.Prefabs) | |
{ | |
m_CurrentStep++; | |
IteratePrefabs(); | |
} | |
if ((m_IterationType & IterationType.Scenes) == IterationType.Scenes) | |
{ | |
m_CurrentStep++; | |
IterateScenes(); | |
} | |
} | |
catch (AbortIteration) | |
{ | |
// Nothing to do here they just quit. | |
} | |
catch (Exception e) | |
{ | |
// We have an exception that so we want to log it | |
Debug.LogException(e); | |
} | |
finally | |
{ | |
m_IsRunning = false; | |
if (m_ProgressHandler != null) | |
{ | |
m_ProgressHandler.ClearProgressBar(); | |
} | |
} | |
} | |
private void IteratePrefabs() | |
{ | |
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab"); | |
int length = prefabGuids.Length; | |
for (int i = 0; i < length; i++) | |
{ | |
float progress = ((float)i / length); | |
string assetpath = AssetDatabase.GUIDToAssetPath(prefabGuids[i]); | |
GameObject gameObject = AssetDatabase.LoadAssetAtPath<GameObject>(assetpath); | |
Transform root = gameObject.transform; | |
IterateHeirarchy(root, root, false); | |
if (m_ProgressHandler != null) | |
{ | |
m_ProgressHandler.UpdateProgress(m_CurrentStep, "Iterating Prefabs", gameObject.name, m_TotalSteps, progress); | |
} | |
} | |
} | |
private void IterateScenes() | |
{ | |
// Store our starting scene | |
Scene startingScene = SceneManager.GetActiveScene(); | |
// Get all the scenes in the build | |
EditorBuildSettingsScene[] scenesInBuild = EditorBuildSettings.scenes; | |
// Store our length | |
int length = scenesInBuild.Length; | |
// Loop over every scene | |
for (int i = 0; i < length; i++) | |
{ | |
float progress = ((float)i / length); | |
// Open the scene | |
Scene openedScene = EditorSceneManager.OpenScene(scenesInBuild[i].path, OpenSceneMode.Single); | |
// Update our progress bar | |
if (m_ProgressHandler != null) | |
{ | |
m_ProgressHandler.UpdateProgress(m_CurrentStep, "Iterating Scenes", openedScene.name, m_TotalSteps, progress); | |
} | |
// Grab all our root objects | |
GameObject[] roots = openedScene.GetRootGameObjects(); | |
// Loop over them all and get their transforms | |
for (int x = 0; x < roots.Length; x++) | |
{ | |
Transform root = roots[x].transform; | |
IterateHeirarchy(root, root, true); | |
} | |
// Unload it | |
EditorSceneManager.CloseScene(openedScene, true); | |
} | |
// Return back to our starting scene | |
if (startingScene.IsValid()) | |
{ | |
EditorSceneManager.OpenScene(startingScene.name); | |
} | |
} | |
private void IterateHeirarchy(Transform root, Transform transform, bool isInScene) | |
{ | |
// Callback: GameObject | |
if (onGameObjectVisited != null) | |
{ | |
onGameObjectVisited(transform.gameObject); | |
} | |
m_CachedBehaviours.Clear(); | |
transform.GetComponents(m_CachedBehaviours); | |
// Callback: MonoBehaviour | |
foreach (MonoBehaviour behaviour in m_CachedBehaviours) | |
{ | |
if (behaviour == null) | |
{ | |
// If a component is missing this case will be true. | |
continue; | |
} | |
if (onBehaviourVisited != null) | |
{ | |
onBehaviourVisited(behaviour); | |
} | |
if (onPropertyVisited != null) | |
{ | |
SerializedObject serializedObject = new SerializedObject(behaviour); | |
SerializedProperty iterator = serializedObject.GetIterator(); | |
while (iterator.Next(true)) | |
{ | |
onPropertyVisited(iterator); | |
} | |
} | |
} | |
for (int i = 0; i < transform.childCount; i++) | |
{ | |
IterateHeirarchy(root, transform.GetChild(i), isInScene); | |
} | |
} | |
void IProgressHandler.ClearProgressBar() | |
{ | |
EditorUtility.ClearProgressBar(); | |
} | |
void IProgressHandler.UpdateProgress(int stepCount, string stepName, string message, int totalSteps, float progress) | |
{ | |
if (EditorUtility.DisplayCancelableProgressBar(string.Format("{0} {1}/{2}", stepName, stepCount, totalSteps), message, progress)) | |
{ | |
throw new AbortIteration(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment