PolygonCollider2D / Custom Inspector: "Edit Collider"-Button disappears when adding custom editor buttons.
I wrote a custom editor script to sort of "unify" the points of the collider i.e. if x- or y-values of multiple points in the collider's path are similar (regarding a given tolerance), they are set to the same value.
The script itself works nicely as intended; but I have a problem with using it in the Unity-GUI: Whenever it is active, the "Edit Collider"-Button with the possibility to edit the collider's path disappears:
With my script commented out:
With my script active:
I use the following code to add the buttons for BoxCollider2Ds:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
[CustomEditor(typeof(PolygonCollider2D))]
public class PolygonColliderSmoother : Editor {
private List<float> referencesX;
private List<float> referencesY;
public override void OnInspectorGUI() {
base.OnInspectorGUI ();
EditorGUILayout.BeginVertical ();
if (GUILayout.Button ("Unify values with tolerance 0.5")) {
Unify (0.5f);
} else if (GUILayout.Button ("Unify values with tolerance 1")) {
Unify (1);
} else if (GUILayout.Button ("Unify values with tolerance 2")) {
Unify (2);
}
EditorGUILayout.EndVertical();
}
private void Unify(float tolerance) {
PolygonCollider2D collider = Selection.activeTransform.gameObject.GetComponent<PolygonCollider2D> ();
Vector2[] path = collider.GetPath (0);
Vector2[] newPath = new Vector2[path.Length];
referencesX = new List<float> ();
referencesY = new List<float> ();
for(int i =0; i < path.Length; i++) {
float useValueX, useValueY;
Vector2 point = path [i];
int referenceIndexX = FindReferenceIndex (referencesX, point.x, tolerance);
int referenceIndexY = FindReferenceIndex (referencesY, point.y, tolerance);
if (referenceIndexX == -1) {
referencesX.Add (point.x);
useValueX = point.x;
} else {
useValueX = referencesX[referenceIndexX];
}
if (referenceIndexY == -1) {
referencesY.Add (point.y);
useValueY = point.y;
} else {
useValueY = referencesY[referenceIndexY];
}
newPath [i] = new Vector2 (useValueX, useValueY);
}
collider.SetPath (0, newPath);
}
private int FindReferenceIndex(List<float> list, float closeTo, float tolerance) {
return list.FindIndex(x => Mathf.Abs (x - closeTo) <= tolerance);
}
}
Does anybody know what I am missing / should do here?
Answer by Adam-Mechtley · Sep 12, 2016 at 06:47 AM
Hi @jwulf! The problem here is that the Edit Collider buttons are defined in an internal editor class (Specifically UnityEditor.ColliderEditorBase.InspectorEditButtonGUI()). When you create your own custom editor and assign it to the class with the CustomEditor attribute, your editor will be preferred, and the internal one will be ignored. So what you end up seeing is just the property fields and none of the other stuff defined in Unity's internal custom editor.
I may soon be working on redesigning the Editor class a little bit to make this sort of thing easier, but the best option you have in the short term is to dig into the UnityEditor dll and either a) copy the parts of the code to replicate what you want or b) use reflection to get at the internal editor class and manage an instance of it inside your own editor. I've done both of these things in personal projects in the past, and while neither is ideal, they're about the best choices you have for now.
Hi @Adam-$$anonymous$$echtley, thanks a lot for the detailed explanation and suggestions! I think digging into the dll is a bit above my skill right now, but please allow me to ask a follow-up question with a different idea:
I tried moving my functionality to a custom window. So I created a window which checks if the current selection has a PolygonColider2D and if so, it shows the buttons just as described in my question above.
In that case, the "Edit Collider" button stays as desired in the inspector (of course, because I don't change the inspector at all), but when I push my custom buttons in the different window, it doesn't change the collider at all. I ensured via Debug.Log-Outputs that the function is indeed called, it just seems that collider.SetPath() is ignored. Is that because inspectors have some sort of higher priority and if other windows try to change a currently "inspected" elements, those changes are ignored or immediately overriden by the old values? Or am I possibly missing something else?
(Unfortunately, I can't post my code for that custom window because I am away from my workstation for a couple of days. But basically, I copied the code from above and used basic functionality as described in tutorials to create the window.)
@jwulf Nope I went ahead and did a quick test of my own and didn't see any problems. In the code you pasted above you are specifically getting/setting path 0. I'd maybe first check to make sure that's the right path index for the collider you're editing.
Cool, many thanks again for your effort! I'll try again in a couple of days when I have access to my project again.
Answer by Rocksuit · Apr 17 at 07:15 AM
You can use my code to modify the inspector, and override the function GetAssemblyEditorTypeName() to return "UnityEditor.PolygonCollider2DEditor". using UnityEditor; using System; using System.Reflection; using Game; using UnityEngine;
public abstract class NativeTypeEditor : Editor
{
private string unityTypeName;
private Editor editorInstance;
private Type nativeEditor;
private MethodInfo onSceneGUI;
private MethodInfo onValidate;
protected abstract string GetAssemblyEditorTypeName();
protected virtual void Awake()
{
Initialize();
}
private void OnEnable()
{
Initialize();
if (nativeEditor != null)
{
MethodInfo onEnable = nativeEditor.GetMethod("OnEnable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if (onEnable != null)
{
onEnable.Invoke(editorInstance, null);
}
onSceneGUI = nativeEditor.GetMethod("OnSceneGUI", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
onValidate = nativeEditor.GetMethod("OnValidate", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
}
}
private void OnDisable()
{
if (nativeEditor != null)
{
MethodInfo onDisable = nativeEditor.GetMethod("OnDisable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if (onDisable != null)
{
onDisable.Invoke(editorInstance, null);
}
}
}
public override void OnInspectorGUI()
{
if (editorInstance != null)
{
editorInstance.OnInspectorGUI();
}
}
private void OnSceneGUI()
{
if (onSceneGUI != null)
{
onSceneGUI.Invoke(editorInstance, null);
}
}
private void OnValidate()
{
Initialize();
if (nativeEditor != null)
{
MethodInfo onReset = nativeEditor.GetMethod("OnReset", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if (onReset != null)
{
onReset.Invoke(editorInstance, null);
}
}
}
private void OnDestroy()
{
if (nativeEditor != null)
{
MethodInfo onDestroy = nativeEditor.GetMethod("OnDestroy", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if (onDestroy != null)
{
onDestroy.Invoke(editorInstance, null);
}
DestroyImmediate(editorInstance);
nativeEditor = null;
}
}
private void Initialize()
{
if (unityTypeName == null)
{
unityTypeName = GetAssemblyEditorTypeName();
if (unityTypeName.IsNullOrEmpty())
{
Debug.LogError("Error: You should override the function GetNativeTypeName and return the editor name in assembly");
return;
}
}
if (nativeEditor == null)
{
nativeEditor = Assembly.GetAssembly(typeof(Editor)).GetType(unityTypeName);
if (nativeEditor == null)
{
Debug.LogError("Error: the return editor name of GetNativeTypeName is not correct. Samples: UnityEditor.RectTransformEditor");
return;
}
}
if (editorInstance == null)
{
editorInstance = CreateEditor(target, nativeEditor);
}
}
}
Your answer
Follow this Question
Related Questions
Access an Array of class inside another Array of class? (EditorGUILayout) 1 Answer
Custom Inspector Script Resetting Information 0 Answers
Update object properties in editor 0 Answers
Unable to edit fields from custom inspector 1 Answer
Is there a way to live-update script-controlled UI formatting in the editor? 0 Answers