- Home /
[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[i] = new TileData[height];
for (int j = 0; j < height; j++)
{
if (terrain.Length > i && terrain[i] != null && terrain[i].Length > j)
terrainCopy[i][j] = terrain[i][j];
else
{
Debug.Log("else, height : " + height);
terrainCopy[i][j] = null;
if (terrain.Length > i)
{
if (terrain[i] == null)
Debug.Log("null");
else
Debug.Log("terrain[i].length <= j");
}
else
Debug.Log("terrain.length <= i");
}
}
}
terrain = new TileData[width][];
for (int i = 0; i < width; i++)
{
terrain[i] = new TileData[height];
for (int j = 0; j < height; j++)
{
if (terrainCopy[i][j] == null)
terrain[i][j] = null;
else
{
terrain[i][j] = CreateInstance<TileData>();
terrain[i][j].Copy(terrainCopy[i][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[i] == null)
{
EditorGUILayout.EndVertical();
continue;
}
for (int j = 0; j < fightMapData.Terrain[i].Length; j++)
fightMapData.Terrain[i][j] = (TileData)EditorGUILayout.ObjectField(fightMapData.Terrain[i][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[i] != null)
{
defense += data.ArmorPieces[i].Defense;
weight += data.ArmorPieces[i].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);
}
}
}
@$$anonymous$$oJoe_Blue_ if I understand well, it seems that you've solved your own question:
*EDIT : An array of array (like : ScriptableObject[][] array;) can't be serialize. I fixed my issue using a simple array.
if so, could you post the solution as an answer, rather than an "EDIT" inside the question. It will be more helpful for others having the same problem.
Answer by MoJoe_Blue_ · Jan 17, 2018 at 10:21 PM
An array of array (like : ScriptableObject[][] array;) can't be serialize. I fixed my issue using a simple array.
I was searching for a while and people were always telling me to use things like EditorUtility.SetDirty or AssetDatabase.SaveAssets. Even if those are valid propositions for the right cases, all I needed was someone telling me that two dimensional arrays within scriptable objects can't be serialized. Then I could stop searching for convoluted solutions and simply use auxiliary one-dimensional arrays for serialization. Thanks
Your answer
Follow this Question
Related Questions
Why doesn't my ScriptableObject save using a custom EditorWindow? 3 Answers
How should I serialize data that is also editable in the Inspector? 2 Answers
Saving UnityEngine.Object into ScriptableObject 1 Answer
ScriptableObject : Serialized Polymorphism Classes Can Not be Deserialize with Polymorphism 1 Answer
Dictionary in inspector 4 Answers