- Home /
The question is answered, right answer was accepted
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.
Could you share some code? It is clear what the problem is, but it is a hard to reason about it and find the actual cause without seeing what you actually do.
Well, I can't share the code, but I can try to give a better insight on what I'm doing.
Here are some questions to get a bit more insight:
Did you change the hideFlags property on any of these scriptable objects?
Are you creating all these scriptable objects at runtime or do they already exist before running the scene (created through asset menu)?
When did the problem start? I'm thinking you didn't create this whole structure of chained scriptable objects from the get go and then tested it after to find this problem, maybe you had a different way of doing this before settling on the current architecture. If so, how's the current code structure different from the point where things "worked"?
1 - No, I didn't.
2 - They already exist.
3 - Eh... The code that worked before doesn't exist. This is the first iteration.
Problem is solved, I $$anonymous$$mviewed with him.
Nice!
I'm curious now, what was the issue? (If you can share of course)
Answer by Igor_Vasiak · Jul 25, 2018 at 05:09 PM
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.
Answer by Bunny83 · Jul 25, 2018 at 02:44 PM
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.
No, no Spaghetti code, Bunny! I'll try to give you more information regarding what you stated on your answer.
Also "some" of them can not be created with "new
Yeah... I'm not creating or instantiating any ScriptableObjects like that.
Some things might get destroyed when you load a new scene is the object belonged to the old scene.
Nope, not reloading or loading scenes. All the testing was done on the same 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.
No Destroy() is being called anywhere in the game, so I'm pretty sure I'm not destroying my ScriptableObject. I'm just passing a reference, man!
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.
That actually hurts... XD
Hopefully that helps you on helping me.
Follow this Question
Related Questions
Giving a ScriptableObject asset reference to all instances of a MonoBehavior? 0 Answers
Scriptable Object is getting set to null 0 Answers
Show on ScriptableObject all other ScriptableObjects that reference it 0 Answers
Duplicating Component with Some Scene References in Serialized Scriptable Object Class Reference 0 Answers