I did the health differently

… and I hope how I use the LazyValue inside it won’t fall on my feet later on…

(Also considering an answer from Brian in a previous discussion thread:
LazyValue implementation on BaseStats breaking something - #8 by Brian_Trotter)

I do keep a copy of the maxHealthPoints that I update whenever the baseStats health is queried, and I used it as a reference value in my RegenerateHealth(). In there I grab the current maxHealthPoints (which would be the value from the new level after leveling up), and use the cached one to get the difference, which I then add to the currentHealth.

So, when the player has 7 out of a maximum of 10, and then levels up and the new maximum becomes 15, I would add the difference and the player would now have 12. Then I check for the regeneration percentage and apply it in case the updated health is still below the regeneration threshold.

Now, the currentHealthPoints has become a LazyValue and is what is saved as the health’s state (just going through a portal to another scene doesn’t make the player heal, although one could come up with a game design where this would make sense, especially when having larger maps to explore and not just smaller scale “rooms” or “scenes”, like we do in the course).

So this meant, to keep my maxHealthPoints initialization consistent with what I had, I could have made it a LazyValue as well, but that would not really fit into it being the source for the initial value of the currentHealth in cases where currentHealth didn’t already get initialized from a saved state.

My solution eventually was doing a small change to the LazyValue. I refactored ForceInit() to reveal whether it made an initialization or not, and on that I can decide whether there are codependent values that need to be initialized right now along with it.

// in LazyValue.cs:
       public bool ForceInit()
        {
            if (!_initialized)
            {
                _value = _initializer();
                _initialized = true;
                return true;
            }
            return false;
        }

and

// in Health.cs:
        private void Start()
        {
            if (healthPoints.ForceInit())
            {
                SetLevelMaximumHealth();
            }
        }

In the Fighter I had a check in Start() if the weapon was set and otherwise equip the default weapon. With the lazy-fied currentWeapon now having SetDefaultWeapon() as initializer I found I actually didn’t need to explicitly do EquipWeapon(defaultWeapon); in Start() anymore, but I kept the if() on currentWeapon.ForceInit() for the (commented out) Debug.Log() I had there…

I actually like the idea of using a bool with LazyValue (though most often, I tend to find this construction more useful

public T ForceInit()
{
     if(!_initialized)
     {
         _value = _initializer();
         _initialized=true;
     } 
     return _value;
}

You could also simply expose _initialized

public bool IsInitialized=>_initialized;

The point of doing it that way was that it exposed when the ForceInit() just did its work. If you simply exposed IsInitialized that wouldn’t be the case.

One could still do something like this, of course:

if( !lazyVar.IsInitialized)
{
    lazyVar.ForceInit();
   // and do related things
}

Another option that comes to mind is using an out parameter, similar to things like TryGetComponent() that return a bool for using an if() and return the actual data that way…

Thus

public bool ForceInit(out T result)
{
  if(!_initialized)
  {
    _value = _initializer();
    _initialized = true;
    result = _value;
   return true;
  }
  return false;
}

That would be my preferred style.
All of that being said, it being my preferred style doesn’t make it the only option or necessarily the same solution. There are many ways to achieve the same goal.

1 Like

Privacy & Terms