In a rush? Grab the code off of GitHub

Have you ever wanted to call a method on one of your MonoBehaviours right from the editor? There are plenty ways to do this.

  1. You could make your own custom editor and add buttons in an OnInspectorGUI. This is recommended but annoying for one-offs.
  2. You could use my handy 5 minute custom editor.
  3. There is probably an asset store asset that is more robust than this. But rolling-your-own is more fun. Let's give it a shot.

so how will we approach this?

The approach is 3 fold:

  1. Create an Attribute to decorate a method that you wish to expose as a button
  2. Make a custom editor that will look for those on all MonoBehaviour's and descendants and draw/handle the buttons.
  3. Decorate any method you wish to expose with the attribute

At the top of this article you will find an image of the look we are going for.
And the code that created that method:

using UnityEngine;  
using CatchCo; // My namespace to avoid conflicts

public class TestMonoBehaviour : MonoBehaviour  
{
   // This is our fancy attribute. Easy no?
   [ExposeMethodInEditor]
   public void DoThePublicThing()
   {
      Debug.Log("DoThePublicThing");
   }

   // We'll make it work on private methods too
   [ExposeMethodInEditor]
   private void DoThePrivateThing()
   {
      Debug.Log("Thing done in private");
   }
}

lets look at the attribute first

The attribute is not very interesting in this case. It's declared as an attribute that can only decorate methods. The only purpose it currently serves is to help identify which methods need exposing.

using System;

// Place this file in any folder that is or is a descendant of a folder named "Scripts"
namespace CatchCo  
{
   // Restrict to methods only
   [AttributeUsage(AttributeTargets.Method)]
   public class ExposeMethodInEditorAttribute : Attribute
   {
   }
}

now for the "hard" part

Well that brings us to the part that actually does anything. It's a CustomEditor that works on any MonoBehaviour or decendent. Code first and then we'll talk about the approach and limitations. I've numbered important lines, see the explanation below.

using UnityEngine;  
using UnityEditor;  
using System.Reflection;

// Place this file in any folder that is or is a descendant of a folder named "Editor"
namespace CatchCo  
{
   [CanEditMultipleObjects] // Don't ruin everyone's day
   [CustomEditor(typeof(MonoBehaviour), true)] // Target all MonoBehaviours and descendants
   public class MonoBehaviourCustomEditor : UnityEditor.Editor
   {
      public override void OnInspectorGUI()
      {
         DrawDefaultInspector(); // Draw the normal inspector

         // Currently this will only work in the Play mode. You'll see why
         if (Application.isPlaying)
         {
            // Get the type descriptor for the MonoBehaviour we are drawing
            var type = target.GetType();

            // Iterate over each private or public instance method (no static methods atm)
            foreach (var method in type.GetMethods(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance))
            {
               // make sure it is decorated by our custom attribute
               var attributes = method.GetCustomAttributes(typeof(ExposeMethodInEditorAttribute), true);
               if (attributes.Length > 0)
               {

                  if (GUILayout.Button("Run: " + method.Name))
                  {
                     // If the user clicks the button, invoke the method immediately.
                     // There are many ways to do this but I chose to use Invoke which only works in Play Mode.
                     ((MonoBehaviour)target).Invoke(method.Name, 0f);
                  }
               }
            }
         }
      }
   }
}

points of interest

  • Currently this only handles parameterless methods. It wouldn't be difficult to draw fields for the parameters but I didn't require that at the time. Feel free to add it in.
  • Of course it only works in Play mode right now. Again, just a choice based on my needs. Though if you decide to execute anything outside of Play mode do be careful.
  • This has the limitation of not working on any MonoBehaviour with a current CustomEditor. It's not too much of a problem since you would already have a CustomEditor to work with.

So that's it for this quick share. Let me know via twitter (@CatchCo) if you decide to use and/or modify it.