Scriptable object corruption
We have structured our game so that data objects are instances of scriptable objects, but we have been having many occurrences of them becoming corrupt. When that happens we can simply make a small change to the scriptable object script and then the data asset will fix itself, but it is becoming increasingly common and wasting our team a huge amount of time.
This is a small, simplified sample of our code:
[CreateAssetMenu(fileName = "newVenue", menuName = "AG/Venue", order = 1)]
public class Venue : ScriptableObject
{
[Tooltip("Name that will be displayed for the venue.")]
public string m_name = "Venue name";
[Tooltip("Level at which the venue becomes available.")]
public int m_unlockLevel = 1;
// More fields....
public bool Enabled { get { return m_unlockLevel <= DataLookup.Level; } }
public float Reputation
{
get { return MoneyManager.Instance.GetReputation(m_name); }
set { MoneyManager.Instance.SetReputation(m_name, value); }
}
public string ReputationVarName { get { return m_name + "_Reputation"; } }
public void Init() // NOTE(AG_TL): Makes sure the vars are always properly initialized!
{
MoneyManager.Instance.GetReputation(this);
}
}
We drag and drop a Venue asset into UI components (like this one):
[RequireComponent(typeof(Button))]
public class VenueButton : MonoBehaviour
{
public Venue m_venue;
private Action<VenueButton> _onVenueButtonPressed;
private Button m_button;
public bool Enabled { get { return m_button.interactable; } }
void Awake()
{
m_button = GetComponent<Button>();
}
public void Init(Action<VenueButton> onVenueButtonPressed)
{
_onVenueButtonPressed = onVenueButtonPressed;
m_venue.Init();
}
}
Here's an example of the corrupted assets:
This only seems to happen when running from the editor where it is very common to Play/Stop the running game. Specifically it manifests when going through different scenes (we are loading them async if that makes a difference). We have it yet to happen if we Play/Stop from a single scene, although we can't assure it doesn't happen (since we haven't been able to get a 100% repro on this). Another interesting thing is, if Venue becomes corrupt, ALL its instances become corrupt, EXCEPT if you have any of them selected from the editor. For some reason, instances that are selected cannot become corrupt.
We are using this as a dirty workaround (select all scriptable objects from the inspector before Play).
We have already investigated quite a bit, so things that we've checked/tried are:
Scriptable objects should never share a file with other classes and the class name should match the file name (Venue -> Venue.cs).
Stopped using prefabs that would contain components that held a reference to scriptable objects.
The issue is very easily reproducible in our project (about 2/5 repro) if we run from a scene, load into another scene that has this kind of setup and then stop.
Any help would be deeply appreciated, since each time we run into this it takes 2 minutes to go into the script, make a change, go back into Unity, check that the assets are correct again, re-run the game...
I just got this issue too with the latest 2018.3.6 build. It seems it's not fixed :/
Answer by SanityIsOverrated · Aug 18, 2017 at 04:55 PM
Didn't know you get an email ping when you're mentioned in a third party post, that's cool.
Without delving deeper into your description, your 'correct' image has an issue too. Notice that the (disabled) field is empty? That shouldn't be the case and is an indication that Unity lost your script reference. For whatever reason, that can happen.
Try the following: Switch the inspector to debug view (via the small burger menu on the top, next to the lock), then you can edit the 'Script' field. Drag in your script. Go back to normal view, save everything and try whether your problem still occurs.
Behind the scenes: The unity editor actually uses two methods of retrieving script instances: It serializes a class reference with the asset, or it retrieves a script file with the same name as the class. Obviously, the second method works only within the editor and I think this is what is used when you keep the asset select. In a build the concept of script files (MonoScript) is lost. The first method will break if said script reference is lost.
Let me know if this changes anything.
That fixed it!! (sort of :D)
There's a single specific class that is not allowing me to assign the script, but everything else works flawlessly now.
Basically this class works as a powerup of sorts. Because I don't want to add code for each possible powerup inside my managers I ended up creating partial classes for them, and these 'contracts' are nested classes that hold up the needed functionality in a modular way. They are also scriptable objects so that we can have multiple instances of each type with slightly different parameters.
I had to post the code outside here because of the 3000 character limit (it's not that big, but still): https://codepad.co/snippet/vtegIlrF#
$$anonymous$$y first assumption is that either nested and/or partial classes are not currently supported. If you can confirm that this is the case I guess we will either need to move to a different setup for this, or find a workaround this limitation.
Thank you so so much for all the assistance so far, I was getting worried about the issue not getting traction at all (hence the direct ping).
Glad to help.
Yes, that setup if screwing up Unity. Generally, an abstract base class is fine in Unity, but you can only serialize concrete derived classes. Partial classes usually work, since this is not a feature of the runtime, the files are combine into a single class at compilation. Nested classes can be serialized by Unity too if it is a pure .NET/$$anonymous$$ono class.
However, $$anonymous$$onoBehaviors and ScriptableObjects must be declared in a file of the same name as their class! Which is not the case in your setup. No way around that. It's because unity refers to all assets (textures as well as scripts) by a fileId. Here, the fileId does not map to a single ScriptableObject. Which the ugly error message is meant to say.
If I have more time later, I might take another look at your code.
Thank you, I still have a bit to learn about the inner workings of Unity, so I guess I kinda shot myself in the foot trying to come up with a clever/modular approach to this problem.
I'll try to think of some other structure for this, although advice is more than welcome!
edit: I guess I could turn the abstract class into an interface, but then sadly I'll get quite a bit of code repetition in the implementations. I can look into moving most of that into a factory method and that would probably be fine.
Answer by ag_tomas · Aug 18, 2017 at 12:21 PM
Has anyone had any of these issues with Scriptable Objects in their own projects?
We are very happy with how clean our code got with the use of SOs, but it is also somewhat annoying (and very much an inefficient use of our time) to keep having to deal with this.
We looked into this other post (https://forum.unity3d.com/threads/prefab-loses-scriptable-object-reference.377804/) and although the result is similar (corrupted or unassignable SO asset) the setup is not exactly the same.
We have found, however, that we do in fact use SOs in a couple prefabs, but this is somewhat important in our particular setup. So, since we do use our SO assets in both prefabs and Scene objects, the issues mentioned might be related to this duality of use and how Unity's internal serialization works.
We will continue to investigate and update if we find a better way to deal with this, but a word from someone from Unity's team or anyone with more experience on the subject would be highly valuable.
@SanityIsOverrated seemed very well informed about this topic. Would you mind sharing your wisdom on the subject?
Answer by Prog_Meisters · Jun 06, 2018 at 08:26 AM
I was having a similar issue. It turned out that the script name had a space in it. Removing the space fixed it.
Thanks Prog. I have the same issue. I didn't recive errors, but scriptable objects was corrupted on load. Checking all scriptable object classes and file names I could detect a filename error .
Answer by Xavier78 · Sep 10, 2019 at 05:39 PM
I am having this issue, but none of the above worked, and not even editing the script helps. Getting this error beforehand "UnityException: GetCount is not allowed to be called during serialization, call it from OnEnable instead. Called from ScriptableObject " But I am not calling that in the script associated with the message.
I found out that unity scriptable objects can get corrupted if there are spaces in their name, and there is also another scriptable object that has the first part of the name. and example would be "Name 1" and "Name" one will be come corrupted. A possible fix would be if unity changed it so scriptable object inside the object had "" around the name, otherwise change what you call the files.
Your answer
Follow this Question
Related Questions
PROBLEM IN MOVING WITH C # 1 Answer
Question about Input.GetKey 1 Answer
Anyone wanna help? 0 Answers
How to have audio play once picked item up? 0 Answers
How do you create a game compatible or friendly for the oculus rift? 0 Answers