Is it possible to access field variables and edit inside of a PropertyDrawer or is it just for properties?
Perhaps, it is the bad naming, but the “Property” part in PropertyDrawer does not mean it is for properties. In fact, it is the opposite - PropertyDrawers are for serialized fields: private fields marked with the [SerializeField] attribute and public fields. PropertyDrawer cannot be used for a property because properties are not serialized by Unity.
PropertyDrawers are used to customize the look of serialized fields in the inspector. For each serialized field, Unity creates a SerializedProperty object (not to confuse with C# property). When you implement a PropertyDrawer class, Unity provides you with the SerializedProperty object, so that you can access the field value and draw the field yourself.
Why would we need SerializedProperty if we could access and change the field value directly? When you change the field value, it does not get automatically serialized (saved to a text file). You would need to save a new value to the asset each time it is changed. However, SerializedProperty does it for you. You just describe how you want the field to be displayed, and SerializedProperty takes care of the serialization part.
I tried to create a simple yet meaningful example of how you can implement a custom PropertyDrawer. Let’s say we have a Player MonoBehaviour that contains a PlayerHealth field:
public class Player : MonoBehaviour
{
[SerializeField] private PlayerHealth _health;
}
[Serializable]
public class PlayerHealth
{
public int MaxHealth = 100;
public int CurrentHealth = 100;
}
You don’t want users to assign a current health value higher than the max health. You can create a slider with [Range(0, 100)] but max health can be changed too. Then, it would be natural to create PropertyDrawer like this:
[CustomPropertyDrawer(typeof(PlayerHealth))]
public class PlayerHealthDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
// Draw the MaxHealth field.
SerializedProperty maxHealthProperty = property.FindPropertyRelative(nameof(PlayerHealth.MaxHealth));
EditorGUILayout.DelayedIntField(maxHealthProperty);
SerializedProperty currentHealthProperty = property.FindPropertyRelative(nameof(PlayerHealth.CurrentHealth));
// If the MaxHealth value was changed, check that CurrentHealth is not higher.
if (currentHealthProperty.intValue > maxHealthProperty.intValue)
currentHealthProperty.intValue = maxHealthProperty.intValue;
// Draw the CurrentHealth field as a slider, where max value is MaxHealth.
EditorGUILayout.IntSlider(currentHealthProperty, 0, maxHealthProperty.intValue);
EditorGUI.EndProperty();
}
}
And now you have a slider field with a variable max value: