Access [field: SerializeField] property in Unity Custom Editor

I’m currently working on making my own custom editor, and I having a problem dealing with [field: SerializeField] properties.
Below is my ScriptableObject script that I’m trying to write my own custom editor and some of the custom editor script I wrote.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[CreateAssetMenu(fileName = "newCombatAbilityData", menuName = "Data/Combat Ability Data")]
public class CombatAbility : ScriptableObject
{
    [field: SerializeField] public Sprite combatAbilityIcon { get; private set; }
    [field: SerializeField] public string combatAbilityName { get; private set; } = "Default Combat Ability Name";
    [field: SerializeField, TextArea] public string combatAbilityDescription { get; private set; } = "Default Combat Ability Description";
    [field: SerializeField] public int staminaCost { get; private set; }
    [field: SerializeField] public Vector3Int castingRange { get; private set; }
    public Dictionary<Vector3Int, bool> castingRangeDictionary { get; private set; }
    [field: SerializeField] public Vector3Int AOE { get; private set; }
    public Dictionary<Vector3Int, bool> AOEDictionary { get; private set; }
    [field: SerializeReference] public List<CombatAbilityComponent> combatAbilityComponents { get; private set; }

    public void AddComponent(CombatAbilityComponent componentData)
    {
        if (combatAbilityComponents.FirstOrDefault(type => type.GetType().Equals(componentData.GetType())) == null)
        {
            combatAbilityComponents.Add(componentData);
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

[CustomEditor(typeof(CombatAbility))]
public class CombatAbilityEditor : Editor
{
    [SerializeField] private Texture2D gridCellTexture;

    private const float maxGridCellLength = 25.0f;
    private const float gridCellInterval = 2.0f;

    private static List<Type> combatAbilityComponentTypes = new List<Type>();

    private bool showAddCombatAbilityComponentsButtons;
    private bool hasValueInDictionary;

    private CombatAbility combatAbilityData;
    private SerializedProperty combatAbilityIcon;
    private SerializedProperty combatAbilityName;
    private SerializedProperty combatAbilityDescription;
    private SerializedProperty staminaCost;
    private SerializedProperty castingRange;
    private SerializedProperty AOE;
    private SerializedProperty combatAbilityComponents;

    private void OnEnable()
    {
        combatAbilityData = target as CombatAbility;

        combatAbilityIcon = serializedObject.FindProperty("combatAbilityIcon");
        combatAbilityName = serializedObject.FindProperty("combatAbilityName");
        combatAbilityDescription = serializedObject.FindProperty("combatAbilityDescription");
        staminaCost = serializedObject.FindProperty("staminaCost");
        castingRange = serializedObject.FindProperty("castingRange");
        AOE = serializedObject.FindProperty("AOE");
        combatAbilityComponents = serializedObject.FindProperty("combatAbilityComponents");
    }
 
   public override void OnInspectorGUI()
   {
        // Do something
   }

I tried to fetch properties by using serializedObject.FindProperty(“propertyName”) in OnEnable, but I’m getting a error saying that
“NullReferenceException: Object reference not set to an instance of an object
UnityEditor.EditorGUILayout.IsChildrenIncluded (UnityEditor.SerializedProperty prop) (at <0ab653b677424c84aa7f1e3350b765bc>:0)”
I found out that simply using public or [SerializeField] works well with the code above, [field: SerializeField] is causing problem.
Is there any way that I can fix this?
Thank you in advance.

When using [field:SerializeField], it means “serialize the backing field of this auto-property”. This means that the “propertyPath” you are after is not the name of the property, but that of the backing field.

Backing field names are auto-generated by the compiler, and i would warn you against using them.

  • First off, it is not guaranteed that the name will always be the same (it is highly unlikely that it changes, but it could change).
  • Second it would make “renaming” fields a little more ugly (you have to use the auto-generated name for the [FormerlySerializedAs(...)])
  • Third, it could later give problems if that propertyPath is used elsewhere where “richText” parsing is enabled (Debug.Log’s, displaying the path on a Label from UIToolkit, etc).

That being said, the name your are after is <PROPERTYNAME>k__BackingField ( “<” and “>” included)

  combatAbilityIcon = serializedObject.FindProperty("<combatAbilityIcon>k__BackingField");
1 Like

Thx. Where did you find these?
If it doesn’t bother you, can I ask you an additional question?
I wonder is there a way to only set the x axis coordinate of the button in OnInspectorGUI?

I dont believe it is properly documented, but i guess it is because it was not mean to be used “directly”. But rather just as a commodity when no custom inspectors where involved. And on the official C# documentation that I linked it doesnt appear also.

But you can just log all your property paths, thats how i found out when I struggled with this

using var iterator = serializedObject.GetIterator();
if (iterator.Next(true))
{
      do Debug.Log(iterator.propertyPath);
      while (iterator.Next(false));
}

And for the button, you can insert a GUILayout.Space(pixelsWidth) before the button, which would be your X offset, and then use a GUILayout.Width(width) for the width of the Button itself

using (new GUILayout.HorizontalScope())
{
    GUILayout.Space(40);
    bool pressed = GUILayout.Button("Some Button", GUILayout.Width(120));
    if (pressed)
        Debug.Log("Hello world");
}

You can also use GUILayout.FlexibleSpace in case what you want is to center the button

using (new GUILayout.HorizontalScope())
{
    GUILayout.FlexibleSpace();
    bool pressed = GUILayout.Button("Some Button", GUILayout.Width(120));
    if (pressed)
        Debug.Log("Hello world");
    
    GUILayout.FlexibleSpace();
}
1 Like

Thanks. Exactly what I wanted.
Really appreciate it.