I’ve just implemented the lazy values and it worked fine for everything except for base stats - I get this NullReferenceException:
NullReferenceException: Object reference not set to an instance of an object
RPG.Stats.BaseStats.GetLevel () (at Assets/Scripts/Stats/BaseStats.cs:81)
RPG.Stats.BaseStats.GetBaseStat (RPG.Stats.Stat stat) (at Assets/Scripts/Stats/BaseStats.cs:76)
RPG.Stats.BaseStats.GetModifiedStat (RPG.Stats.Stat stat) (at Assets/Scripts/Stats/BaseStats.cs:70)
RPG.Attributes.Health.GetInitialHealth () (at Assets/Scripts/Attributes/Health.cs:30)
GameDevTV.Utils.LazyValue`1[T].ForceInit () (at Assets/Asset Packs/GameDev.tv Assets/Scripts/Utils/LazyValue.cs:56)
GameDevTV.Utils.LazyValue`1[T].get_value () (at Assets/Asset Packs/GameDev.tv Assets/Scripts/Utils/LazyValue.cs:38)
RPG.Attributes.Health.Awake () (at Assets/Scripts/Attributes/Health.cs:25)
I have checked by rolling back just base stats and everything works fine. I am guessing it’s related to current level not being set for some reason… Any idea what might be causing it?
So it looks like GetLevel() is getting called -=before=- Awake sets the LazyValue.Init method.
Looking at the error chain, I see that it starts in Health.Awake()…
This means that it’s likely that the value of the LazyValue in Health is being checked in the Awake method instead of waiting until start. Paste in your Health.cs and we’ll take a look.
There’s your culprit. You’re setting currentHealth to maxHealth.value in Awake. This automatically triggers GetInitialHealth, which calls BaseStats and asks for the current health… but… there’s no guarantee that BaseStats will run it’s Awake before Health, and this sets up the race condition.
In this case, BaseStats is losing the race.
Try putting that last line (currentHealth = maxHealth.value) in Start()
Thank you for your help Brian, in hindsight that makes total sense.
The initial intention with having currentHealth set to maxhealth.value was to make sure I am storing both current and maxhealth as I feel it’s a bit more robust (and allowed me to keep levelup health values consistent rather than percentage increase) - moving the line to start means that it’s always resetting to max value on reload which is obviously not good. In order to fix this, I have put in some protection:
So I set the default value for currentHealth to -1 and it seems to be working for dead, alive, soft and hard reloads - is there anything I am not considering which might cause me issues later?
There actually isn’t really a need to cache maxHealth, as it’s always available from BaseStats (well… it’s always available from BaseStats everywhere but Awake)
The problem with caching maxHealth is that it introduces the risk that your cached maxHealth won’t be in sync with the actual maxHealth, which is BaseStats.GetStat(Stat.Health);
What I tend to do is to make MaxHealth a property (so you don’t have to keep typing BaseStats.GetStat:
public float MaxHealth => baseStats.GetModifiedStat(Stat.Health);
Then you can just use MaxHealth where needed and it will always be up to date, even if the gear changes and gives the character more or less MaxHealth.