Home > Net >  ScriptableObject not saving data when entering play mode
ScriptableObject not saving data when entering play mode

Time:05-30

I have the following scriptable object:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
    public class Style : ScriptableObject
    {
        public Texture2D[] textures;
        public Sprite[] sprites;
        public AnimationCurve animationCurve;
        Sprite[] MakeSprites(Texture2D[] baseTextures){
            Sprite[] output = new Sprite[baseTextures.Length];
            for (int i = 0; i < baseTextures.Length; i  )
            {
                output[i] = MakeSprite(baseTextures[i]);
            }
            return output;
        }
        
        Sprite MakeSprite(Texture2D baseTexture)
        {
            Sprite sprite = Sprite.Create(baseTexture, new Rect(0, 0, baseTexture.width, baseTexture.height), new Vector2(baseTexture.width / 2f, baseTexture.height / 2f), Mathf.Max(baseTexture.width, baseTexture.height));
            return sprite;
        }
        public void UpdateSprites()
        {
            sprites = MakeSprites(textures);
        }
    }
    
    [CustomEditor(typeof(Style))]
    public class customStyleEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();
    
            Style style = (Style) target;
            if (GUILayout.Button("Update Sprites"))
            {
                style.UpdateSprites();
                EditorUtility.SetDirty(target);
            }
        }
    }

This works as I would expect, but when I enter play mode, the sprites field resets to being empty, I'm pretty sure this is to do with my custom editor script, but I have tried several fixes without any success. Including: serializedObject.ApplyModifiedProperties(); EditorUtility.SetDirty(target) Everything else I have tried has caused an error.

So, what is the problem here and how do I fix it?

CodePudding user response:

It looks to be that sprites get saved as a reference to a unity asset rather then getting serialized. So it seems that it needs to be a saved sprite in the project to save it in this way.

Meaning that if you generate a sprite that is not stored in the project then it's going to lose the reference as the only place it will be stored is in memory, which gets wiped on starting play mode.

If you look at the asset file you'll actually see how it stores it. This is how it looks if i drag in the image in the textures and the sprites. See that the guid and fileId are all the same and reference to an image that it has in the project. (this will be maintained between play and edit mode)

  textures:
 - {fileID: 2800000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}
 - {fileID: 2800000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}
  sprites:
 - {fileID: 21300000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}
 - {fileID: 21300000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}

However when i run the provided code and save that, this is what gets saved:

 textures:
 - {fileID: 2800000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}
 - {fileID: 2800000, guid: e8a45d89f3b96e44e82022b97a4860a3, type: 3}
  sprites:
 - {fileID: 0}
 - {fileID: 0}

It's not saving the sprite but rather a reference to it which doesn't exist. When that object no longer exists after the memory is reset, it now has a missing reference which gets translated to null.

So to fix this you could save the sprites to separate files or you could just generate them on start. The type sprite can't be properly serialized so you'll have to store it in a different data type if you want to cache it.

This is how you can save a cache of the sprites in the project. Do note that you'd have to do some management of the sprites, to make sure the old ones get deleted.

Sprite[] MakeSprites(Texture2D[] baseTextures){

    Sprite[] output = new Sprite[baseTextures.Length];
    for (int i = 0; i < baseTextures.Length; i  )
    {
        output[i] = MakeSprite(baseTextures[i]);
    }
    
    //Now save the sprites to the project 
    List<Sprite> savedSprites = new List<Sprite>();
    foreach (Sprite sprite in output)
    {
        //Create a sprite name, make sure it's prefixed with Assets/ as the assetDatabase won't like it otherwise
        string spriteName = "Assets/"   Guid.NewGuid().ToString()   ".asset";
        //Create the asset
        AssetDatabase.CreateAsset(sprite, spriteName);
        //Load the asset back in so we have a reference to the asset that exists in the project and not the reference to the sprite in memory
        Sprite newSavedSprite = (Sprite)AssetDatabase.LoadAssetAtPath(spriteName, typeof(Sprite));
        savedSprites.Add(newSavedSprite);
    }

    return savedSprites.ToArray();
}
  • Related