[Solved]Why doesn't my ScriptableObject based asset save using a custom inspector ?

So I made an asset system based on the ScriptableObject. I have several assets made this way and they all save perfectly but one. Some have custom inpector display, some have not.


My problem is that I can’t identify why this one does not save. Every time I hit the Play button or change scene in Editor the asset is reset to default.
I tried to use EditorUtility.SetDirty(...), AssetDatabase.SaveAssets(), SerializedObject.ApplyModifiedProperties(), none of them have any effect.


Here is the asset that doesn’t save :

using UnityEngine;

[CreateAssetMenu(fileName = "DefaultMap", menuName = "Terrain/Map")]
public class FightMapData : ObjectData
{
    [SerializeField]
    private int height = 1;
    public int  Height { get { return height; } set { height = value; } }
    [SerializeField]
    private int width = 1;
    public int  Width { get { return width; } set { width = value; } }
    
    [SerializeField]
    private TileData[][]    terrain;
    public TileData[][]     Terrain { get { return terrain; } set { terrain = value; } }

    public void ResizeTerrain()
    {
        if (terrain == null)
            terrain = new TileData[width][];
        else if (terrain.Length == width && terrain[0] != null && terrain[0].Length == height)
            return;

        TileData[][] terrainCopy = new TileData[width][];

        for (int i = 0; i < width; i++)
        {
            terrainCopy *= new TileData[height];*

for (int j = 0; j < height; j++)
{
if (terrain.Length > i && terrain != null && terrain*.Length > j)*
terrainCopy_[j] = terrain*[j];
else
{
Debug.Log("else, height : " + height);_

_terrainCopy[j] = null;*_

if (terrain.Length > i)
{
if (terrain == null)
Debug.Log(“null”);
else
Debug.Log(“terrain*.length <= j”);*
}
else
Debug.Log(“terrain.length <= i”);
}
}
}

terrain = new TileData[width][];

for (int i = 0; i < width; i++)
{
terrain = new TileData[height];

for (int j = 0; j < height; j++)
{
if (terrainCopy*[j] == null)*
terrain*[j] = null;*
else
{
terrain*[j] = CreateInstance();*
terrain_[j].Copy(terrainCopy*[j]);
}
}
}
}*_

It’s custom inspector :
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(FightMapData))]
public class FightMapDataEditor : Editor
{
private bool mapFoldout = true;
private Vector2 scrollPosition;

public TileData FillTileData = null;

public override void OnInspectorGUI()
{
FightMapData fightMapData = (FightMapData)target;

fightMapData.ResizeTerrain();

EditorGUILayout.Space();
fightMapData.Id = EditorGUILayout.TextField(“Id”, fightMapData.Id);
fightMapData.Name = EditorGUILayout.TextField(“Name”, fightMapData.Name);

EditorGUILayout.Space();
fightMapData.Height = EditorGUILayout.IntField(“Height”, fightMapData.Height);
fightMapData.Width = EditorGUILayout.IntField(“Width”, fightMapData.Width);

EditorGUILayout.Space();
mapFoldout = EditorGUILayout.Foldout(mapFoldout, “Map”);
if (mapFoldout)
{
EditorGUILayout.Space();
FillTileData = (TileData)EditorGUILayout.ObjectField(“TileData to fill Map”, FillTileData, typeof(TileData), false);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(“Fill”))
fightMapData.Fill(FillTileData);
if (GUILayout.Button(“Fill blanks”))
fightMapData.FillBlanks(FillTileData);
EditorGUILayout.EndHorizontal();

EditorGUILayout.Space();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, true, true);
EditorGUILayout.BeginHorizontal();
for (int i = 0; i < fightMapData.Terrain.Length; i++)
{
EditorGUILayout.BeginVertical();
if (fightMapData.Terrain == null)
{
EditorGUILayout.EndVertical();
continue;
}

for (int j = 0; j < fightMapData.Terrain*.Length; j++)*
fightMapData.Terrain_[j] = (TileData)EditorGUILayout.ObjectField(fightMapData.Terrain*[j], typeof(TileData), false);
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndScrollView();
}
}
}
----------*

Here is an example asset that save perfectly :
using UnityEngine;_

[CreateAssetMenu(fileName = “DefaultUnit”, menuName = “Unit”)]
public class UnitData : ObjectData
{
public enum Classes { None, Soldier }

[SerializeField]
private Classes _class = 0;
public Classes Class { get { return _class; } set { _class = value; } }

[SerializeField]
private int level = 1;
public int Level { get { return level; } set { level = value; } }

[SerializeField]
private int baseHitPoints = 0;
public int BaseHitPoints { get { return baseHitPoints; } set { baseHitPoints = value; } }
[SerializeField]
private int strength = 0;
public int Strength { get { return strength; } set { strength = value; } }
[SerializeField]
private int dexterity = 0;
public int Dexterity { get { return dexterity; } set { dexterity = value; } }
[SerializeField]
private int luck = 0;
public int Luck { get { return luck; } set { luck = value; } }
[SerializeField]
private int speed = 0;
public int Speed { get { return speed; } set { speed = value; } }

[System.Serializable]
public struct WeaponRank
{
public WeaponData.WeaponType Type;
public WeaponData.Rank Rank;
public int Experience;
}
[SerializeField]
private WeaponRank[] weaponRanks = new WeaponRank[6];
public WeaponRank[] WeaponRanks { get { return weaponRanks; } set { weaponRanks = value; } }

[SerializeField]
private bool canMount = false;
public bool CanMount { get { return canMount; } set { canMount = value; } }
[SerializeField]
private int movement = 0;
public int Movement { get { return movement; } set { movement = value; } }

[SerializeField]
private ArmorData[] armorPieces = new ArmorData[4];
public ArmorData[] ArmorPieces { get { return armorPieces; } set { armorPieces = value; } }
[SerializeField]
private WeaponData[] weapons = new WeaponData[4];
public WeaponData[] Weapons { get { return weapons; } set { weapons = value; } }

[SerializeField]
private HorseData horse = null;
public HorseData Horse { get { return horse; } set { horse = value; } }

[ExecuteInEditMode]
public void Awake()
{
WeaponRanks[0].Type = WeaponData.WeaponType.OneHanded;
WeaponRanks[1].Type = WeaponData.WeaponType.TwoHanded;
WeaponRanks[2].Type = WeaponData.WeaponType.Polearms;
WeaponRanks[3].Type = WeaponData.WeaponType.Archery;
WeaponRanks[4].Type = WeaponData.WeaponType.Crossbow;
WeaponRanks[5].Type = WeaponData.WeaponType.Throwing;
}
}

It’s custom inspector :
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(UnitData))]
public class UnitDataEditor : Editor
{
private bool weaponsFoldout = true;
private bool armorsFoldout = true;

private bool weaponRanksFoldout = true;

public override void OnInspectorGUI()
{
UnitData data = (UnitData)target;

EditorGUILayout.Space();
data.Id = EditorGUILayout.TextField(“Id”, data.Id);
data.Name = EditorGUILayout.TextField(“Name”, data.Name);

EditorGUILayout.Space();
data.Sprite = (Sprite)EditorGUILayout.ObjectField(“Sprite”, data.Sprite, typeof(Sprite), false);

EditorGUILayout.Space();
EditorGUILayout.LabelField(“Equipment”, EditorStyles.boldLabel);

weaponsFoldout = EditorGUILayout.Foldout(weaponsFoldout, “Weapons”);
if (weaponsFoldout)
{
data.Weapons[0] = (WeaponData)EditorGUILayout.ObjectField(“Equipped Weapon”, data.Weapons[0], typeof(WeaponData), false);
data.Weapons[1] = (WeaponData)EditorGUILayout.ObjectField(“Seond Weapon”, data.Weapons[1], typeof(WeaponData), false);
data.Weapons[2] = (WeaponData)EditorGUILayout.ObjectField(“Third Weapon”, data.Weapons[2], typeof(WeaponData), false);
data.Weapons[3] = (WeaponData)EditorGUILayout.ObjectField(“Fourth Weapon”, data.Weapons[3], typeof(WeaponData), false);
}

EditorGUILayout.Space();
armorsFoldout = EditorGUILayout.Foldout(armorsFoldout, “Armors”);
if (armorsFoldout)
{
data.ArmorPieces[0] = (ArmorData)EditorGUILayout.ObjectField(“Head Armor”, data.ArmorPieces[0], typeof(ArmorData), false);
data.ArmorPieces[1] = (ArmorData)EditorGUILayout.ObjectField(“Torso Armor”, data.ArmorPieces[1], typeof(ArmorData), false);
data.ArmorPieces[2] = (ArmorData)EditorGUILayout.ObjectField(“Gauntlets”, data.ArmorPieces[2], typeof(ArmorData), false);
data.ArmorPieces[3] = (ArmorData)EditorGUILayout.ObjectField(“Boots”, data.ArmorPieces[3], typeof(ArmorData), false);
}

EditorGUILayout.Space();
data.CanMount = EditorGUILayout.Toggle(“Can Mount”, data.CanMount);
if (data.CanMount)
{
data.Horse = (HorseData)EditorGUILayout.ObjectField(“Horse”, data.Horse, typeof(HorseData), false);
if (data.Horse == null)
EditorGUILayout.LabelField(“Movement Bonus : 0”);
else
EditorGUILayout.LabelField("Movement Bonus : " + data.Horse.MovementBonus);
}

EditorGUILayout.Space();
EditorGUILayout.LabelField(“Statistics”, EditorStyles.boldLabel);

data.Class = (UnitData.Classes)EditorGUILayout.EnumPopup(“Class”, data.Class);

EditorGUILayout.Space();
data.Level = EditorGUILayout.IntField(“Level”, data.Level);

EditorGUILayout.Space();
data.BaseHitPoints = EditorGUILayout.IntField(“Base Hit Points”, data.BaseHitPoints);
data.Strength = EditorGUILayout.IntField(“Strength”, data.Strength);
data.Dexterity = EditorGUILayout.IntField(“Dexterity”, data.Dexterity);
data.Luck = EditorGUILayout.IntField(“Luck”, data.Luck);
data.Speed = EditorGUILayout.IntField(“Speed”, data.Speed);
data.Movement = EditorGUILayout.IntField(“Movement”, data.Movement);

int defense = 0;
int weight = 0;

for (int i = 0; i < 4; i++)
{
if (data.ArmorPieces != null)
{
defense += data.ArmorPieces*.Defense;*
weight += data.ArmorPieces*.Weight;*
}
}

if (data.Weapons[0] != null)
weight += data.Weapons[0].Weight;

EditorGUILayout.LabelField("Defense : " + defense);
EditorGUILayout.LabelField("Weight : " + weight);

EditorGUILayout.Space();
weaponRanksFoldout = EditorGUILayout.Foldout(weaponRanksFoldout, “Weapon Ranks”);
if (weaponRanksFoldout)
{
EditorGUILayout.LabelField(“One Handed”);
data.WeaponRanks[0].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[0].Rank);
data.WeaponRanks[0].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[0].Experience);
EditorGUILayout.Space();
EditorGUILayout.LabelField(“Two Handed”);
data.WeaponRanks[1].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[1].Rank);
data.WeaponRanks[1].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[1].Experience);
EditorGUILayout.Space();
EditorGUILayout.LabelField(“Polearms”);
data.WeaponRanks[2].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[2].Rank);
data.WeaponRanks[2].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[2].Experience);
EditorGUILayout.Space();
EditorGUILayout.LabelField(“Archery”);
data.WeaponRanks[3].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[3].Rank);
data.WeaponRanks[3].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[3].Experience);
EditorGUILayout.Space();
EditorGUILayout.LabelField(“Crossbows”);
data.WeaponRanks[4].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[4].Rank);
data.WeaponRanks[4].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[4].Experience);
EditorGUILayout.Space();
EditorGUILayout.LabelField(“Throwing”);
data.WeaponRanks[5].Rank = (WeaponData.Rank)EditorGUILayout.EnumPopup(“Rank”, data.WeaponRanks[5].Rank);
data.WeaponRanks[5].Experience = EditorGUILayout.IntField(“Experience”, data.WeaponRanks[5].Experience);
}
}
}

An array of array (like : ScriptableObject array;) can’t be serialize. I fixed my issue using a simple array.