Skip to content

Instantly share code, notes, and snippets.

@AdamAtomic
Last active June 8, 2019 04:15
Show Gist options
  • Save AdamAtomic/cd22eebdda1c3b82d27844c92c77e137 to your computer and use it in GitHub Desktop.
Save AdamAtomic/cd22eebdda1c3b82d27844c92c77e137 to your computer and use it in GitHub Desktop.
Based on someone else's good hack, put this Unity 2018.4.1f1 script next to EditorUtil.cs to make Inspecting prefabs act more like Unity 2018.2 and earlier.
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
using System.Reflection;
using System;
using System.Collections;
using System.Linq;
using Object = UnityEngine.Object;
using System.Collections.Generic;
public class CustomPrefabImporterEditor : AssetImporterEditor
{
#region Injection
/*
* See Editor/Mono/CustomEditorAttributes.cs for original logic of getting an editor by type. The UnityEditor.CustomEditorAttributes class handles this
*
* We sneak ourselfs in by
* 1. Call FindCustomEditorTypeByType to initialize state
* 2. Modify the kSCustomEditors and kSCustomMultiEditors lists with our class for the type 'UnityEditor.PrefabImporter'
* 3. ??
* 4. Profit!
*/
//We have to find the types in this assembly
private static readonly Assembly editorAssembly = Assembly.GetAssembly(typeof(AssetImporterEditor));
private static readonly string customEditorAttributes = "UnityEditor.CustomEditorAttributes",
findCustomEditorTypeByType = "FindCustomEditorTypeByType",
kSCustomEditors = "kSCustomEditors",
kSCustomMultiEditors = "kSCustomMultiEditors",
prefabImporter = "PrefabImporter",
m_InspectorType = "m_InspectorType";
[InitializeOnLoadMethod]
private static void inject()
{
callPrivateStaticMethod(customEditorAttributes, findCustomEditorTypeByType, null, false); //null as type to get does an early out.
var editorTypeDictionary = getPrivateStaticField<ICollection>(customEditorAttributes, kSCustomEditors);
//editorTypeKey is of type KeyValuePair<Type, List<UnityEditor.CustomEditorAttributes.MonoEditorType>>, but we cant cast it. Reflection all the way!
foreach (var editorTypeDictEntry in editorTypeDictionary)
{
if (getPublicProperty<Type>(editorTypeDictEntry, "Key").Name != prefabImporter) continue; //Only our importer
//editor is of type UnityEditor.CustomEditorAttribute.MonoEditorType
foreach (var editor in getPublicProperty<IList>(editorTypeDictEntry, "Value"))
setPublicField(editor, m_InspectorType, typeof(CustomPrefabImporterEditor));
}
}
private static object callPrivateStaticMethod(string typeName, string methodName, params object[] parameters)
{
var type = editorAssembly.GetType(typeName, true);
var method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
return method.Invoke(null, parameters);
}
private static T getPrivateStaticField<T>(string typeName, string fieldName)
{
var type = editorAssembly.GetType(typeName, true);
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
return (T)field.GetValue(null);
}
private static T getPublicProperty<T>(object target, string fieldName)
{
return (T)target.GetType().GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance).GetValue(target);
}
public static void setPublicField(object target, string fieldName, object value)
{
target.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.Instance).SetValue(target, value);
}
#endregion
#region Inspector GUI
private Editor[] targetEditors;
private bool[] folds;
public override void OnEnable()
{
base.OnEnable();
if (assetTargets == null) return;
if (assetTargets.Length <= 1)
{
var targets = (assetTarget as GameObject).GetComponents<Component>();
targetEditors = new Editor[targets.Length + 1];
folds = new bool[targetEditors.Length];
for( int f = 0; f < folds.Length; f++ ) { folds[f] = true; }
assetTarget.hideFlags = HideFlags.None;
targetEditors[0] = CreateEditor(assetTarget);
for (int i = 0; i < targets.Length; i++)
{
if( targets[i] == null ) { continue; }
targets[i].hideFlags = HideFlags.None; //This is the magic that makes them editable, by default they are set to NonEditable.
targetEditors[i + 1] = CreateEditor(targets[i]);
}
}
else //multiple
{
//Get all components that are shared on all objects
var commonComponents = assetTargets.SelectMany(a => (a as GameObject).GetComponents<Component>()).GroupBy(c => c.GetType()).Where(g => g.Count() > 1);
foreach (var t in assetTargets) t.hideFlags = HideFlags.None;
targetEditors = new Editor[commonComponents.Count() + 1];
targetEditors[0] = CreateEditor(assetTargets);
folds = new bool[targetEditors.Length];
for( int f = 0; f < folds.Length; f++ ) { folds[f] = true; }
int i = 1;
foreach (var group in commonComponents)
{
foreach (var c in group)
c.hideFlags = HideFlags.None;
targetEditors[i++] = CreateEditor(group.ToArray());
}
}
}
public override void OnInspectorGUI()
{
string[] splits;
for (int i = 0; i < targetEditors.Length; i++)
{
if( targetEditors[i] == null ) { continue; }
if( targetEditors[i].target is GameObject )
{
targetEditors[i].DrawHeader();
}
else
{
folds[i] = EditorGUILayout.InspectorTitlebar( folds[i], targetEditors[i].target );
}
if( folds[i] )
{
EditorGUI.BeginChangeCheck();
targetEditors[i].OnInspectorGUI();
EditorGUIUtility.LookLikeControls( 150.0f );
//When the object is changed it is reimported, and our editors point to incorrect objects. Restart to create new editors!
if (EditorGUI.EndChangeCheck())
{
OnEnable();
return;
}
}
}
for( int i = 0; i < 5; i++ ) { EditorGUILayout.Space(); }
using (new EditorGUI.DisabledScope(assetTargets.Length > 1))
{
if (GUILayout.Button("Open Prefab"))
{
AssetDatabase.OpenAsset(assetTarget);
GUIUtility.ExitGUI();
}
}
}
protected override void OnHeaderGUI() { }
public override bool showImportedObject { get { return false; } }
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment