I've created a "Jump" class for my game (to be able to edit how objects jump in a more convenient way than having to manually calculate the right accelerations to make an object jump a certain height, and to easily add special features to the jump like floatier falling). Using OnGUI(), I've created a custom inspector for Jumps so that values I put in won't be applied to the object until I call a method from a ContextMenu that ensures all the values are valid (i.e. all of the Jump's parameters can be simultaneously true, according to the kinematic equations).
As far as I can tell, the custom inspector is working fine when editing scenes and prefabs, but when I try adding a Jump variable to a (embed) State Graph, the inspector for it is unusably short and cramped. The ContextMenu works fine for both, however.


Here's the CustomPropertyDrawer Jumps use:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using UnityEditor;
using Unity.VisualScripting;
using System.ComponentModel;
using Unity.Mathematics;
[CustomPropertyDrawer(typeof(Jump))]
public class JumpEditor : PropertyDrawer
{
const float propertyCount = 8;
const float width = 200;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float baseHeight = EditorGUIUtility.standardVerticalSpacing;
return baseHeight * propertyCount * 10;
}
public override void OnGUI(Rect position, SerializedProperty jump, GUIContent label)
{
EditorGUI.BeginProperty(position, label, jump);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
Rect heightRect = new Rect(position.x, position.y + FieldHeight(0, position), width, position.height / propertyCount);
Rect timeRect = new Rect(position.x, position.y + FieldHeight(1, position), width, position.height / propertyCount);
Rect startVelocityRect = new Rect(position.x, position.y + FieldHeight(2, position), width, position.height / propertyCount);
Rect addedGravityRect = new Rect(position.x, position.y + FieldHeight(3, position), width, position.height / propertyCount);
Rect baseGravityRect = new Rect(position.x, position.y + FieldHeight(4, position), width, position.height / propertyCount);
Rect groundRequirementRect = new Rect(position.x, position.y + FieldHeight(5, position), width, position.height / propertyCount);
Rect velocityModeRect = new Rect(position.x, position.y + FieldHeight(6, position), width, position.height / propertyCount);
Rect validityRect = new Rect(position.x, position.y + FieldHeight(7, position), width, position.height / propertyCount);
SerializedProperty height = jump.FindPropertyRelative("height");
SerializedProperty time = jump.FindPropertyRelative("time");
SerializedProperty initialVelocity = jump.FindPropertyRelative("startVelocity");
SerializedProperty addedGravity = jump.FindPropertyRelative("addedGravity");
SerializedProperty assumedBaseGravity = jump.FindPropertyRelative("assumedBaseGravity");
SerializedProperty valuesValid = jump.FindPropertyRelative("variablesValid");
SerializedProperty groundRequired = jump.FindPropertyRelative("mustBeGrounded");
SerializedProperty addVelocity = jump.FindPropertyRelative("addVelocity");
EditorGUI.PropertyField(heightRect, height, new GUIContent("Height"));
EditorGUI.PropertyField(timeRect, time, new GUIContent("Air Time"));
EditorGUI.PropertyField(startVelocityRect, initialVelocity, new GUIContent("Initial Vertical Velocity"));
EditorGUI.PropertyField(addedGravityRect, addedGravity, new GUIContent("Added Gravity"));
EditorGUI.PropertyField(baseGravityRect, assumedBaseGravity, new GUIContent("Assumed Base Gravity"));
EditorGUI.PropertyField(groundRequirementRect, groundRequired, new GUIContent("Must be grounded to initiate?"));
EditorGUI.PropertyField(velocityModeRect, addVelocity, new GUIContent("Add to previous y-velocity?"));
EditorGUI.indentLevel = indent;
GUI.enabled = false;
EditorGUI.PropertyField(validityRect, valuesValid, new GUIContent("Values Valid & Saved?"));
GUI.enabled = true;
EditorGUI.EndProperty();
Event e = Event.current;
if (e.type == EventType.MouseDown && e.button == 1 && position.Contains(e.mousePosition))
{
GenericMenu context = new GenericMenu();
context.AddItem(new GUIContent("Calculate Start Velocity and Time to Accomodate"), true, SetIVelocityAndTime);
context.AddItem(new GUIContent("Calculate Added Gravity and Start Velocity to Accomodate"), true, SetTimeAndHeight);
context.ShowAsContext();
}
//Sets the Jump's addedGravity and startVelocity variables to fit the Height and Time values currently in the Inspector.
void SetTimeAndHeight()
{
//Use x = vf(t) - .5(at^2) to calculate acceleration due to gravity, knowing x = Height, t = Time/2, and vf = 0 (We're only considering the movement to the apex of the jump)
float acceleration = -2 * height.floatValue / (time.floatValue * time.floatValue);
addedGravity.floatValue = acceleration - assumedBaseGravity.floatValue;
//Use vf = vi + a(t) to calculate new StartVelocity, with t = Time/2, vf = 0
initialVelocity.floatValue = -AssumedAcceleration() * time.floatValue/2;
SaveValues();
}
//Sets the Jump's startVelocity and airTime variables to fit the Height and AddedGravity values currently in the Inspector.
void SetIVelocityAndTime()
{
//Use s = vf(t) - .5(a)(t^2) to calculate new airTime, with s = height, vf = 0 and a = AssumedAcceleration()
//=> s = -.5(a)(t^2) => sqrt((-s)/.5a), multiply by 2 to get the time going up and down.
time.floatValue = (float) math.sqrt(-height.floatValue / (.5 * AssumedAcceleration())) * 2;
//Use vf = vi + a(t) => vi = vf - a(t) to calculate new StartVelocity, with t = Time/2, vf = 0
initialVelocity.floatValue = -AssumedAcceleration() * time.floatValue/2;
SaveValues();
}
float AssumedAcceleration()
{
return assumedBaseGravity.floatValue + addedGravity.floatValue;
}
//Applies the modified properties and displays that they were saved.
//Only call once you know the values are valid.
void SaveValues()
{
valuesValid.boolValue = true;
jump.serializedObject.ApplyModifiedProperties();
}
}
private float FieldHeight(int index, Rect position)
{
return position.height * index / propertyCount;
}
}
I assumed that State/Script Graph would space out the fields the same way the normal Inspector does, but it instead seems to cram all the field into one space. However, based on the amount of space being set aside for Jumps compared to other types of variables, it does look like State Graph is using the GetHeight() method in the Jump CustomPropertyDrawer. I'm guessing the problem is due to some difference in how the State Graph variable blackboard interprets the Rects used to place the fields compared to how the normal inspector does it.
I've tried looking in the Visual Scripting part of the Project Settings, but nothing there seems to help.
You seem to be forgetting about the property fields' height themselves.
You only have the standardVerticalSpacing (which is basically just 2) but you probably would also additionally want the normal fields height.
You could either just use singleLineHeight (which is basically 18 - see constants here) or get the actual properties like you do already for height, time etc and then rather use EditorGUI.GetPropertyHeight.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With