Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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

Discussion in 'Scripting' started by Benji23245, Sep 29, 2023.

  1. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    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 :


    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using UnityEditor;
    3. #endif
    4. using System;
    5. using System.Collections.Generic;
    6. using UnityEngine;
    7. using static Helpers.General;
    8.  
    9. [ExecuteInEditMode]
    10. [SerializeField]
    11. public class SuccessiveJumps : MonoBehaviour {
    12.  
    13.   [Serializable]
    14.   public List<JumpZone> jumpZones = new();
    15.  
    16.   //// ------------ CLASSES ----------- ////
    17.  
    18.   [Serializable]
    19.   public class JumpZone {
    20.  
    21.     public Vector3 zonePosition;
    22.     public Quaternion zoneRotation = Quaternion.identity;
    23.  
    24.     internal JumpZone(Vector3 _zonePosition) {
    25.       zonePosition = _zonePosition;
    26.     }
    27.   }
    28.  
    29. #if UNITY_EDITOR
    30.  
    31.   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
    32.     jumpZones.Add(new JumpZone(Vector3.one));
    33.   }
    34.  
    35. #endif
    36. }
    Can someone please help ?

    Thanks !
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    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.
     
  3. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    Thanks for the reply ! Here's the editor script :


    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using UnityEditor;
    3. #endif
    4. using UnityEngine;
    5. using static SuccessiveJumps;
    6.  
    7. [CustomEditor(typeof(SuccessiveJumps))]
    8. public sealed class SuccessiveJumpsInspector : Editor {
    9.  
    10.   private SuccessiveJumps successiveJumps;
    11.  
    12.   private void OnEnable() {
    13.     successiveJumps = target as SuccessiveJumps;
    14.   }
    15.  
    16.   public override void OnInspectorGUI() {
    17.     DrawDefaultInspector();
    18.  
    19.     if (GUILayout.Button("Add Element")) successiveJumps.AddElement();
    20.   }
    21.  
    22. }
    23.  
     
  4. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    Ok I have no idea what I did but now it works...?
     
  5. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    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
     
    Last edited: Sep 29, 2023
  6. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    Okk.... So apparently, if I tweak the values of the list from the inspector, it works. How can I fix this weird issue ?
     
  7. samana1407

    samana1407

    Joined:
    Aug 23, 2015
    Posts:
    69
    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
    <JumpZone> 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

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [ExecuteInEditMode]
    6. public class SuccessiveJumps : MonoBehaviour
    7. {
    8.     [SerializeField]
    9.     public List<JumpZone> jumpZones = new();
    10.     public void AddElement()
    11.     {
    12.         jumpZones.Add(new JumpZone(Vector3.one));
    13.     }
    14.  
    15.     //// ------------ CLASSES ----------- ////
    16.     [Serializable]
    17.     public class JumpZone
    18.     {
    19.         public Vector3 zonePosition;
    20.         public Quaternion zoneRotation = Quaternion.identity;
    21.  
    22.         internal JumpZone(Vector3 _zonePosition)
    23.         {
    24.             zonePosition = _zonePosition;
    25.         }
    26.     }
    27.  
    28. }
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(SuccessiveJumps))]
    5. public sealed class SuccessiveJumpsInspector : Editor
    6. {
    7.  
    8.     private SuccessiveJumps successiveJumps;
    9.  
    10.     private void OnEnable()
    11.     {
    12.         successiveJumps = target as SuccessiveJumps;
    13.     }
    14.  
    15.     public override void OnInspectorGUI()
    16.     {
    17.         DrawDefaultInspector();
    18.  
    19.         if (GUILayout.Button("Add Element")) successiveJumps.AddElement();
    20.     }
    21.  
    22. }
     
  8. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127
    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) ?
     
  9. samana1407

    samana1407

    Joined:
    Aug 23, 2015
    Posts:
    69
    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.
     
  10. samana1407

    samana1407

    Joined:
    Aug 23, 2015
    Posts:
    69
    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

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(SuccessiveJumps))]
    5. public sealed class SuccessiveJumpsInspector : Editor
    6. {
    7.  
    8.     private SuccessiveJumps successiveJumps;
    9.  
    10.     private void OnEnable()
    11.     {
    12.         successiveJumps = target as SuccessiveJumps;
    13.     }
    14.  
    15.     public override void OnInspectorGUI()
    16.     {
    17.         DrawDefaultInspector();
    18.  
    19.         if (GUILayout.Button("Add Element"))
    20.         {
    21.             successiveJumps.AddElement();
    22.             EditorUtility.SetDirty(successiveJumps); // <--- mark as dirty
    23.         }
    24.     }
    25.  
    26. }
     
    Last edited: Sep 30, 2023
  11. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    127