Script Extension Updater

wmpunk

New member
I wrote a extension that allows simplified changing of script types to base or sub-types to easily be able to change already setup player components to extended classes. It replaces the script field at the top of UCC scripts with one that calls a method that will let you swap out scripts and preserve interchangeable parameters without throwing errors and updates the inspector to the correct type, it also attempts to block you from changing to scripts that will corrupt your component.

To Install create a new script inside a Editor folder called InspectorBasePartialExtension and copy the code below into it. Open up the InspectorBase script and change the class to partial and remove the OnInspectorGUI method. Open the StateInspector script and change object type check (line:160 I believe) to always be true.

note: I have yet to experience a issue with removing the type check from states but you should still back up your project.

Create a new script called InspectorBasePartialExtension inside a Editor folder and copy the supplied code into it.

CreateScript.PNG


Find the InspectorBase script and open it and make it partial and remove OnInspectorGUI().

FindInspectorBase.PNGModifyInspectorBase.PNGModifiedInspectorBase.PNG

Find the StateInspector script and change the object type check to always be true by commenting out the directed lines.

FindStateInspector.PNGModifyStateInspector.PNGModifiedStateInspector.PNG

You can now use the script field on UCC behaviors to safely change or upgrade to inherited or parented types while keeping shared values and updating the Inspector.

Functional.PNG

With out this modification changing anything in the script field will throw errors, not change the inspector and most of the time corrupt your behavior.


C#:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Reflection;

namespace Opsive.UltimateCharacterController.Editor.Inspectors
{
   public abstract partial class InspectorBase
   {
      private bool m_IsChangingScript = false;
      public bool IsChangingScript { get { return m_IsChangingScript; } }

      public override void OnInspectorGUI()
      {
         ChangeScriptField();
         if (IsChangingScript)
            return;

         EditorGUI.BeginChangeCheck();
         if (EditorGUI.EndChangeCheck())
         {
            Undo.RecordObject(target, "Change Value");
            serializedObject.ApplyModifiedProperties();
            Opsive.UltimateCharacterController.Editor.Inspectors.Utility.InspectorUtility.SetDirty(target);
         }
      }

      void ChangeScriptField()
      {
         if (m_IsChangingScript == true)
            return;

         var guiEnabledCache = GUI.enabled;
         GUI.enabled = Application.isPlaying ? false : guiEnabledCache;

         var script = PropertyFromName("m_Script");
         var scriptValue = script.objectReferenceValue as MonoScript;
         var selecedScript = EditorGUILayout.ObjectField("Script",scriptValue, typeof(UnityEditor.MonoScript), true) as MonoScript;

         if (selecedScript == null) goto RETRN;
         if (scriptValue == null) goto RETRN;

         if (scriptValue != selecedScript)
         {
            var selectedClass = selecedScript.GetClass();
            var scriptClass = scriptValue.GetClass();

            if (scriptClass == null)
            {      
               Debug.LogError("Script has no Class value");
               goto RETRN;
            }
            if (selectedClass == null)
            {
               Debug.LogError("Selected Script has no Class value");
               goto RETRN;
            }
            if (selectedClass.IsAbstract)
            {
               EditorUtility.DisplayDialog("Invalid Scrip", "Selected Script contains abstract Class", "Cancel");
               goto RETRN;
            }
            if (!scriptClass.IsAssignableFrom(selectedClass) && !selectedClass.IsAssignableFrom(scriptClass))
            {
               EditorUtility.DisplayDialog("Invalid Scrip", "Selected Script contains Class that is not base or sub-type of " + scriptClass.Name, "Cancel");
               goto RETRN;
            }

            //Debug.Log("SetChange Script to " + selecedScript.name);
            m_IsChangingScript = true;
            m_InspectorToDestroy = this;
            m_ScriptToChangeTo = selecedScript;
            EditorUtility.SetDirty(target);
            EditorApplication.update += ChangeScriptOnEditorUpdate;
         }

         RETRN:
         GUI.enabled = guiEnabledCache;
      }

      private static MonoScript m_ScriptToChangeTo = null;
      private static InspectorBase m_InspectorToDestroy = null;
      static void ChangeScriptOnEditorUpdate()
      {
         if (EditorApplication.update != null)
            EditorApplication.update -= ChangeScriptOnEditorUpdate;

         if (Application.isPlaying) goto CLEAR;
         if (m_ScriptToChangeTo == null) goto CLEAR;
         if (m_ScriptToChangeTo == null) goto CLEAR;
         if (m_InspectorToDestroy == null) goto CLEAR;

         var mono = m_InspectorToDestroy.target as MonoBehaviour;
         var go = mono.gameObject;
         var comps = go.GetComponents<Component>();

         var index = -1;
         for (int i = 0; i < comps.Length; i++)
         {
            if (comps[i] == mono)
            {
               index = i;
               break;
            }
         }
         if (index < 0) goto CLEAR;

         var preset = new UnityEditor.Presets.Preset(m_InspectorToDestroy.target);
         var prop = m_InspectorToDestroy.PropertyFromName("m_Script");
         prop.objectReferenceValue = m_ScriptToChangeTo;
         m_InspectorToDestroy.serializedObject.ApplyModifiedProperties();
         m_InspectorToDestroy.ResetTarget();

         comps = go.GetComponents<Component>();
         preset.ApplyTo(comps[index]);
         DestroyImmediate(preset);
         EditorUtility.SetDirty(comps[index]);

         CLEAR:  
         DestroyImmediate(m_InspectorToDestroy);
         m_ScriptToChangeTo = null;
         m_InspectorToDestroy = null;
 
      }
   }
}
 
Last edited:
Top