ScriptableObject reference resetted after a method is finished.


Context

This is for an Inventory System, and I’m trying to make items the more automatically-ish way possible.

This is an issue regarding Weapons spawning bullets. Sort of.

Let’s say I have a ScriptableObject called ItemData. It is a pretty generic ScriptableObject, one that holds only data.

Now, let’s say that I have it assigned to a Script called Item. Also, let’s say that I have another ScriptableObject assigned as one of the various ItemData’s fields. This one is called MWActions. It is a slightly more complex SO, containing not only data, but Methods as well. It inherits another SO which is called WeaponActions, which inherits Actions which inherits ScriptableObject.

Now, as needed, I have yet another ScriptableObject assigned to this MWAction one. Lets call it… ProjectileData.

Just to recap, this “assigned var on assigned var” goes like this:

ProjectileData is assigned to MWActions which is assigned to ItemData, which finally is assigned to Item, the caller MonoBehaviour.

I also have a Script on my player that is used as an intermediate between the visual part of the items (i.e. being displayed on the player’s hand, or the position of the bullets depending on each item). Lets call it CharacterInventory.


Issue

Now, I’m trying to pass the reference of the ProjectileData to the CharacterInventory from the MWAction ScriptableObject.

The player selects the weapon and clicks to fire. The MWAction “assigns” (or sets) the projectileData variable inside CharacterInventory and spawns a bullet.

The bullet was supposed to get that ProjectileData and use it, but unfortunatelly that won’t happen. The projectileData var is null by then.

The issue is, after this “assignment” method finishes running, the reference on that other Script becomes null. Now, why is this even a thing? Is it a bug?


Some Notes

The reference on the other script keeps true to the PData passed to it until the method finishes. It only gets null after that.

The assignment isn’t the only thing happenning on that method, but nothing breaks the reference to the PData.

That crazy inheritance above is needed because I’m trying to make use of the very flexible Polymorphism Serialization that Scriptable Objects have. I’ve already tried with MonoBehaviours, but… No success.

ScriptableObject, just like any other UnityEngine.Object derived type, has its roots in the native c++ engine core. Those objects are not garbage collected but have to be destroyed explicitly using Destroy (DestroyImmediate in the editor). Also “some” of them can not be created with “new”. This applies mainly to Components (which include MonoBehaviours as well) and ScriptableObjects. Components can only be created with AddComponent, ScriptableObjects with CreateObject().

Now since objects derived from UnityEngine.Object actually have a native code counterpart, this native instance need to be explicitly destroyed if you want to get rid of it. Some things might get destroyed when you load a new scene is the object belonged to the old scene. Generally when the native part of a UnityEngine.Object is destroyed or just missing for some reason (you created it with new) the managed instance will become a fake null object. Since managed objects can’t be explicitly destroyed the managed part just fakes that it is null because it’s native part is missing. Fot that Unity has overloaded the == operator of UnityEngine.Object to catch this case.

If a reference to such an object suddenly turns “null” or appears to be null it most likely got destroyed.

Before you scream “BUG” you may want to create a minimal reproduction case and share it here. If you can’t reproduce this issue in an isolated case it just means your either not very good at debugging your code or your code is not structured enough (Spaghetti code? Big ball of mud? or some other anti pattern?)

You have presented this problem in a very abstract fashion. So there’s no way for us to actually follow what is happening and fully understand where the issue might come from.

Okay, found it! As @ShadyProductions stated while helping me with this issue, lazy loading creates its own variables when working with ScriptableObjects, so I was creating a totally new CharacterInventory while doing the whole process, and assigned the ProjectileData to it.

Lazy loading may look like this¹, for those ones who doesn’t know:

private MyClass _myVar;
public MyClass MyVar => _myVar ?? (_myVar = //Get the component);

And I was using it. So, just a tip, avoid lazy loading on ScriptableObjects, okay?


Thanks for everyone who took their time to try to help me.


¹Lazy loading structures may vary depending on what you’d do. The one above is just an example, but Lazy loaded variables are, in general, getter Properties initialized only when called by something. They return a cached value if not null or cache the value needed and return it.