Script with [ExecuteInEditMode] and [Serializable] clears List prop on Play Mode

Hello !

I made a script that allows to append data to a List prop while in Edit Mode. In the inspector in Edit Mode, I can see my list and the data appended to it.

But when I launch Play Mode, the List is cleared and I can see “List is empty”.

I did put [Serializable] on top of the script and on the list field but it doesn’t work.

Here’s the basic code :

#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
using static Helpers.General;

[ExecuteInEditMode]
[SerializeField]
public class SuccessiveJumps : MonoBehaviour {

  [Serializable]
  public List<JumpZone> jumpZones = new();

  //// ------------ CLASSES ----------- ////

  [Serializable]
  public class JumpZone {

    public Vector3 zonePosition;
    public Quaternion zoneRotation = Quaternion.identity;

    internal JumpZone(Vector3 _zonePosition) {
      zonePosition = _zonePosition;
    }
  }

#if UNITY_EDITOR

  public void AddElement() { // There is an editor script that adds a button in the inspector that triggers this method and allows to append data to the list
    jumpZones.Add(new JumpZone(Vector3.one));
  }

#endif
}

Can someone please help ?

Thanks !

Post the editor script. I bet you’re modifying target directly rather than going through the SerializedObject. Target is a temporary instance in the Editor script that gets thrown away. Only changes to serialized properties are persisted.

The first two Serializable are not needed, only JumpZone needs it. MonoBehaviour is always Serializable, and fields/properties don’t need that attribute but they either have to be public (your case) or have the SerializeField attribute.

Thanks for the reply ! Here’s the editor script :

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using static SuccessiveJumps;

[CustomEditor(typeof(SuccessiveJumps))]
public sealed class SuccessiveJumpsInspector : Editor {

  private SuccessiveJumps successiveJumps;

  private void OnEnable() {
    successiveJumps = target as SuccessiveJumps;
  }

  public override void OnInspectorGUI() {
    DrawDefaultInspector();

    if (GUILayout.Button("Add Element")) successiveJumps.AddElement();
  }

}

Ok I have no idea what I did but now it works…?

Ok so I actually have another issue now, when I delete an item from the list, and then launch Play Mode, it reappears. Any idea why ?

EDIT: And now the list is empty again … What’s going on …

I didn’t even touch the code. Sometimes it becomes empty, sometimes not but the deleted items come back. I have no idea what’s happening

Okk… So apparently, if I tweak the values of the list from the inspector, it works. How can I fix this weird issue ?

Benji23245, Your code should not have compiled because the [SerializeField] attribute should only apply to fields and the [Serializable] attribute should only apply to classes.
You have the opposite here:

[SerializeField]
public class SuccessiveJumps : MonoBehaviour

and here:

[Serializable]
public List jumpZones = new();

But overall your code (if you swap the attributes) works fine. I checked in (version 2021.3.14.f1) and when starting the game the fields did not reset their values and after exiting the game everything returned to the expected state (as it was before launch). Everything was also saved after restarting the scene.

I removed a little bit of unnecessary stuff in your code, in my opinion

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

[ExecuteInEditMode]
public class SuccessiveJumps : MonoBehaviour
{
    [SerializeField]
    public List<JumpZone> jumpZones = new();
    public void AddElement()
    {
        jumpZones.Add(new JumpZone(Vector3.one));
    }

    //// ------------ CLASSES ----------- ////
    [Serializable]
    public class JumpZone
    {
        public Vector3 zonePosition;
        public Quaternion zoneRotation = Quaternion.identity;

        internal JumpZone(Vector3 _zonePosition)
        {
            zonePosition = _zonePosition;
        }
    }

}
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(SuccessiveJumps))]
public sealed class SuccessiveJumpsInspector : Editor
{

    private SuccessiveJumps successiveJumps;

    private void OnEnable()
    {
        successiveJumps = target as SuccessiveJumps;
    }

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        if (GUILayout.Button("Add Element")) successiveJumps.AddElement();
    }

}

I’m using version 2021.3.20f1, could it be a bug from this version ?

When testing my code, are you sure you just clicked the buttons to add/remove and didn’t manually tweak the list at all (by changing the values/order etc) ?

There is no “remove element” button in your code, but only “Add Element”.
I tried different ways:

  • just click the “Add Element” button and don’t change anything else manually in the inspector (not field values, not the order of elements, not adding/removing through the “plus/minus” button, which are there by default for the list)

  • and also manually change everything that could be changed in the inspector of this script

As a result, everything works fine, the values are not reset when the game starts and are restored when the game mode ends.

I have a suspicion that this script is in your prefab, an instance of which you created in the scene? If this is the case, then the data will indeed be reset in game mode. And if this script is added not to the prefab, but simply to an object, then nothing will be reset.

Try adding a method to indicate to the editor that the object has been modified

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(SuccessiveJumps))]
public sealed class SuccessiveJumpsInspector : Editor
{

    private SuccessiveJumps successiveJumps;

    private void OnEnable()
    {
        successiveJumps = target as SuccessiveJumps;
    }

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        if (GUILayout.Button("Add Element"))
        {
            successiveJumps.AddElement();
            EditorUtility.SetDirty(successiveJumps); // <--- mark as dirty
        }
    }

}

It worked ! It was indeed on a prefab, and setting it to dirty worked !

Thanks man !

I have another question though @samana1407 :

Would you know why this doesn’t work ? If it’s a bug or if it’s possible to make it work ?

Thank you !