Snippets

Sam Gates Otto - Populate your public variables with a single click!

You are viewing an old version of this snippet. View the current version.
Revised by Sam Gates 3562165
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;

namespace Helios.Otto
{
    //add me to an Editor folder
    public class Otto
    {
        /// <summary>
        /// Loops through all public fields on the target mono behaviour and searches for a relevant asset in the asset database
        /// Separate your search terms with an underscore, and being as specific as possible with your variable names helps Otto to find the right assets
        /// Example: "Optimistic Unicorn.mp4" can be found with variable names "Optimistic", "Unicorn", or best of all: "Optimistic_Unicorn"
        /// </summary>
        [MenuItem("CONTEXT/MonoBehaviour/Auto Populate - All")]
        static void AutoPopulateAll(MenuCommand command)
        {
            Debug.Log("[Otto]: Auto populating variables");
            var type = command.context.GetType();
            Debug.Log($"[Otto]: Doing a kickflip over the {type.Name}");
            var fields = type.GetFields();
            var fieldsSet = 0;
            for (int i = 0; i < fields.Length; i++)
            {
                var assetName = fields[i].Name.Replace('_',' ');
                if (SearchAssets(command.context, assetName, fields[i]))
                {
                    fieldsSet++;
                    continue;
                }

                if (SearchScene(command.context, assetName, fields[i])) fieldsSet++;
            }
            Debug.Log($"[Otto]: All done! I set {fieldsSet} fields");
        }

        [MenuItem("CONTEXT/MonoBehaviour/Auto Populate - Assets")]
        static void AutoPopulateAssets(MenuCommand command)
        {
            Debug.Log("[Otto]: Auto populating variables");
            var type = command.context.GetType();
            Debug.Log($"[Otto]: Doing a kickflip over the {type.Name}");
            var fields = type.GetFields();
            var fieldsSet = 0;
            for (int i = 0; i < fields.Length; i++)
            {
                var assetName = fields[i].Name.Replace('_', ' ');
                if (SearchAssets(command.context, assetName, fields[i])) fieldsSet++;
            }
            Debug.Log($"[Otto]: All done! I set {fieldsSet} fields");
        }

        [MenuItem("CONTEXT/MonoBehaviour/Auto Populate - Scene")]
        static void AutoPopulateScene(MenuCommand command)
        {
            Debug.Log("[Otto]: Auto populating variables");
            var type = command.context.GetType();
            Debug.Log($"[Otto]: Doing a kickflip over the {type.Name}");
            var fields = type.GetFields();
            var fieldsSet = 0;
            for (int i = 0; i < fields.Length; i++)
            {
                var assetName = fields[i].Name.Replace('_', ' ');
                if (SearchScene(command.context, assetName, fields[i])) fieldsSet++;
            }
            Debug.Log($"[Otto]: All done! I set {fieldsSet} fields");
        }

        static bool SearchAssets(UnityEngine.Object target, string search, FieldInfo field)
        {
            var asset = AssetDatabase.FindAssets(search);
            if (asset.Length > 0)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(asset[0]);
                var loadedAsset = AssetDatabase.LoadAssetAtPath(assetPath, field.FieldType);
                if (loadedAsset != null)
                {
                    Undo.RecordObject(target, $"Set field {field.Name}");
                    field.SetValue(target, loadedAsset);
                    return true;
                }
            }
            return false;
        }

        static bool SearchScene(UnityEngine.Object target, string search, FieldInfo field)
        {
            //if we can't even find the obejct type in the scene, why search for it?
            //i.e. you won't find a float or audio clip in the scene!
            if (TypeNotSceneCompatible(field.FieldType)) return false;

            List<GameObject> obj = new List<GameObject>();
            SceneManager.GetActiveScene().GetRootGameObjects(obj);
            var rootObj = new List<GameObject>(obj);
            foreach (GameObject o in rootObj)
            {
                for (int childIndex = 0; childIndex < o.transform.childCount; childIndex++)
                {
                    var t = o.transform.GetChild(childIndex);
                    if (!obj.Contains(t.gameObject)) obj.Add(t.gameObject);
                }
            }
            object targetObject = obj.Find(x => x.name.Contains(search));

            if (!field.FieldType.Name.Contains("GameObject"))
            {
                targetObject = ((GameObject)targetObject)?.GetComponent(field.FieldType);
            }

            if (targetObject != null)
            {
                Undo.RecordObject(target, $"Set field {field.Name}");
                field.SetValue(target, targetObject);
                return true;
            }

            return false;
        }

        static bool TypeNotSceneCompatible(Type type)
        {
            return (!type.IsSubclassOf(typeof(MonoBehaviour)) && !type.IsSubclassOf(typeof(Behaviour)) && type.Name != "GameObject");
        }
    }
}
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.