Skip to content

Instantly share code, notes, and snippets.

@prime31
Forked from MattRix/ObjectInspector.cs
Last active October 24, 2022 09:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prime31/46685036a7800e3919dc to your computer and use it in GitHub Desktop.
Save prime31/46685036a7800e3919dc to your computer and use it in GitHub Desktop.
Generic inspector class that lets you implement OnScene/InspectorGUI in your class file instead of in a separate custom editor. It also provides attributes for getting Vector3/Vector3[]/List<Vector3> editing capabilities right in the scene view and getting buttons for any methods in your class in the inspector.
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class ExampleUsage : MonoBehaviour
{
// this attribute will add handles for your Vector3s so that you can drag them around in the scene view
[Vector3Inspectable]
List<Vector3> someListOfVectors;
// this method will appear as a button in the inspector
[MakeButton]
public void someVoidMethod()
{}
#if UNITY_EDITOR
void OnInspectorGUI()
{
// do inspector stuff
}
void OnSceneGUI()
{
// do scene stuff
}
#endif
}
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Globalization;
// when implementing this in your MonoBehaviours, wrap your using UnityEditor and
// OnInspectorGUI/OnSceneGUI methods in #if UNITY_EDITOR/#endif
/// <summary>
/// for fields to work with the Vector3 inspector they must either be public or marked with SerializeField and have the Vector3Inspectable
/// attribute.
/// </summary>
[CustomEditor( typeof( UnityEngine.Object ), true )]
[CanEditMultipleObjects]
public class ObjectInspector : Editor
{
MethodInfo _onInspectorGuiMethod;
MethodInfo _onSceneGuiMethod;
List<MethodInfo> _buttonMethods = new List<MethodInfo>();
// Vector3 editor
bool _hasVector3Fields = false;
IEnumerable<FieldInfo> _fields;
public void OnEnable()
{
var type = target.GetType();
if( !typeof( IObjectInspectable ).IsAssignableFrom( type ) )
return;
_onInspectorGuiMethod = target.GetType().GetMethod( "OnInspectorGUI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
_onSceneGuiMethod = target.GetType().GetMethod( "OnSceneGUI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
var meths = type.GetMethods( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic )
.Where( m => m.IsDefined( typeof( MakeButtonAttribute ), false ) );
foreach( var meth in meths )
{
_buttonMethods.Add( meth );
}
// the vector3 editor needs to find any fields with the Vector3Inspectable attribute and validate them
_fields = type.GetFields( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic )
.Where( f => f.IsDefined( typeof( Vector3Inspectable ), false ) )
.Where( f => f.IsPublic || f.IsDefined( typeof( SerializeField ), false ) );
_hasVector3Fields = _fields.Count() > 0;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if( _onInspectorGuiMethod != null )
{
foreach( var eachTarget in targets )
_onInspectorGuiMethod.Invoke( eachTarget, new object[0] );
}
foreach( var meth in _buttonMethods )
{
if( GUILayout.Button( CultureInfo.InvariantCulture.TextInfo.ToTitleCase( Regex.Replace( meth.Name, "(\\B[A-Z])", " $1" ) ) ) )
foreach( var eachTarget in targets )
meth.Invoke( eachTarget, new object[0] );
}
}
protected virtual void OnSceneGUI()
{
if( _onSceneGuiMethod != null )
_onSceneGuiMethod.Invoke( target, new object[0] );
if( _hasVector3Fields )
vector3OnSceneGUI();
}
#region Vector3 editor
void vector3OnSceneGUI()
{
foreach( var field in _fields )
{
var value = field.GetValue( target );
if( value is Vector3 )
{
Handles.Label( (Vector3)value, field.Name );
var newValue = Handles.PositionHandle( (Vector3)value, Quaternion.identity );
if( GUI.changed )
{
GUI.changed = false;
field.SetValue( target, newValue );
}
}
else if( value is List<Vector3> )
{
var list = value as List<Vector3>;
var label = field.Name + ": ";
for( var i = 0; i < list.Count; i++ )
{
Handles.Label( list[i], label + i );
list[i] = Handles.PositionHandle( list[i], Quaternion.identity );
}
Handles.DrawPolyLine( list.ToArray() );
}
else if( value is Vector3[] )
{
var list = value as Vector3[];
var label = field.Name + ": ";
for( var i = 0; i < list.Length; i++ )
{
Handles.Label( list[i], label + i );
list[i] = Handles.PositionHandle( list[i], Quaternion.identity );
}
Handles.DrawPolyLine( list );
}
}
}
#endregion
}
// ##### ##### ##### ##### ##### ##### ##### #####
// ## ObjectInspectorHelpers.cs (this needs to live outside of the Editor folder)
// ##### ##### ##### ##### ##### ##### ##### #####
using UnityEngine;
using System.Collections;
using System;
/// <summary>
/// implement this empty interface to tell the ObjectInspector that your class should be inspected
/// </summary>
public interface IObjectInspectable
{}
/// <summary>
/// makes any method have a button in the inspector to call it
/// </summary>
[AttributeUsageAttribute( AttributeTargets.Method )]
public class MakeButtonAttribute : Attribute
{}
/// <summary>
/// adds a vector3 editor to the scene GUI if this attribute is on any serialized Vector3, List<Vector3>
/// or Vector3[] fields.
/// </summary>
[AttributeUsageAttribute( AttributeTargets.Field )]
public class Vector3Inspectable : Attribute
{}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment