Skip to content

Instantly share code, notes, and snippets.

@ByronMayne
Created November 1, 2017 12:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ByronMayne/abf5f29ae65f6bda460485bfa2e2551c to your computer and use it in GitHub Desktop.
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.
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