Last active
June 8, 2019 04:15
-
-
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.
This file contains hidden or 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 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