Skip to content

Instantly share code, notes, and snippets.

@LotteMakesStuff
Last active November 10, 2024 13:09
Show Gist options
  • Save LotteMakesStuff/dd785ff49b2a5048bb60333a6a125187 to your computer and use it in GitHub Desktop.
Save LotteMakesStuff/dd785ff49b2a5048bb60333a6a125187 to your computer and use it in GitHub Desktop.
Code running pack! two property drawing scripts that make it super easy to trigger code via buttons in the inspector, and get feedback! [TestButton] draws you little buttons that call a method on your MonoBehaviour, and [ProgressBar] give you a great way to show feedback from long running methods. These are *super* helpful for things like proced…
using UnityEngine;
using System.Collections;
public class InspectorButtonsTest : MonoBehaviour
{
[TestButton("Generate world", "DoProcGen", isActiveInEditor = false)]
[TestButton("Clear world", "ClearWorld", 2, isActiveInEditor = false)]
[ProgressBar(hideWhenZero = true, label = "procGenFeedback")]
public float procgenProgress = -1;
[HideInInspector]
public string procGenFeedback;
// Silly little enumerator to test progress bars~
IEnumerator DoProcGen()
{
// lets pretend we have some code here that procedurally generates a map
procGenFeedback = "Initilizing";
procgenProgress = 0.01f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "Seeding terrain";
procgenProgress = 0.2f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "Plotting cities";
procgenProgress = 0.4f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "Drawing roads";
procgenProgress = 0.6f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "Reticulating splines";
procgenProgress = 0.8f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "Finalizing";
procgenProgress = 0.9f;
yield return new WaitForSeconds(0.25f);
procGenFeedback = "DONE in 6 seconds";
procgenProgress = 1f;
}
// resets values
void ClearWorld()
{
procgenProgress = 0;
}
}
using UnityEngine;
public class ProgressBarAttribute : PropertyAttribute
{
public bool hideWhenZero;
public string label;
}
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(ProgressBarAttribute))]
public class ProgressBarDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.Float)
{
GUI.Label(position, "ERROR: can only apply progress bar onto a float");
return;
}
if ((attribute as ProgressBarAttribute).hideWhenZero && property.floatValue <= 0)
return;
var dynamicLabel = property.serializedObject.FindProperty((attribute as ProgressBarAttribute).label);
EditorGUI.ProgressBar(position, property.floatValue/1f, dynamicLabel == null ? property.name : dynamicLabel.stringValue);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if ((attribute as ProgressBarAttribute).hideWhenZero && property.floatValue <= 0)
return 0;
return base.GetPropertyHeight(property, label);
}
}
// NOTE DONT put in an editor folder
using UnityEngine;
using System;
[AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = true)]
public class TestButtonAttribute : PropertyAttribute
{
public string buttonLabel;
public string methodName;
// Set this false to make the button not work whilst in playmode
public bool isActiveAtRuntime = true;
// Set this to false to make the button not work when the game isnt running
public bool isActiveInEditor = true;
public TestButtonAttribute(string buttonLabel, string methodName, int order = 1)
{
this.buttonLabel = buttonLabel;
this.methodName = methodName;
this.order = order; // Defualt the order to 1 so this can draw under headder attribles
}
}
// NOTE put in a Editor folder
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(TestButtonAttribute))]
public class TestButtonDrawer : DecoratorDrawer
{
public override void OnGUI(Rect position)
{
// cast the attribute to make it easier to work with
var buttonAttribute = (attribute as TestButtonAttribute);
// check if the button is supposed to be enabled right now
if (EditorApplication.isPlaying && !buttonAttribute.isActiveAtRuntime)
GUI.enabled = false;
if (!EditorApplication.isPlaying && !buttonAttribute.isActiveInEditor)
GUI.enabled = false;
// figure out where were drawing the button
var pos = new Rect(position.x, position.y, position.width, position.height - EditorGUIUtility.standardVerticalSpacing);
// draw it and if its clicked...
if (GUI.Button(pos, buttonAttribute.buttonLabel))
{
// tell the current game object to find and run the method we asked for!
Selection.activeGameObject.BroadcastMessage(buttonAttribute.methodName);
}
// make sure the GUI is enabled when were done!
GUI.enabled = true;
}
public override float GetHeight()
{
return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing*2;
}
}
@dearamy
Copy link

dearamy commented Feb 8, 2017

Always got this error: "Assertion failed on expression: 'ShouldRunBehaviour ()'
UnityEngine.GameObject:BroadcastMessage(String)
ButtonDrawer:OnGUI(Rect) (at Assets/Attributes/Editor/TestButtonDrawer.cs:21)
UnityEditor.DockArea:OnGUI()"
I'm using Unity5.5.1

@LotteMakesStuff
Copy link
Author

@dearamy this is an error thrown by unity when you GameObject.BroadcastMessage(..) in the editor. It dosnt show if your in play mode. Its super annoying, but totally harmless.

@Flassari
Copy link

Flassari commented Feb 9, 2017

Do note that GameObject.BroadcastMessage calls that function in every MonoBehaviour on the GameObject, which might not be what you want in case you use this attribute on multiple components.

@LotteMakesStuff
Copy link
Author

@Flassari Yes, you need to be careful with naming your methods!

@tbg10101
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment