LazyValue exceptions on RestoreState

When restoring from a saved state, I am getting exceptions the RestoreState() for classes where I’ve implemented the LazyValue. It seems that RestoreState() is occurring before Awake, but I’m not sure how that can be.

For instance, in Health.cs… the exception will show _health to be null in the following snippet. And this is resolved by setting _health to a new LazyValue, but that seems to defeat the purpose of this lesson.

        public void RestoreState(object state)
        {
            _health.value = (float)state;
            // Check for death
            if (_health.value <= Mathf.Epsilon)
            {
                Die();
            }
        }

Here is the Heatlh.Awake()…

        private LazyValue<float> _health;
        private Animator _animator;
        private GameObject _instigator;

        public State HealthState { get; set; }

        public float HealthPoints
        {
            get { return _health.value; }
        }

        private void Awake()
        {
            _animator = GetComponent<Animator>();
            _health = new LazyValue<float>(InitializeHealth);
        }

Thanks!

Can you post the full script? I’d like a peek at your implementation of InitializeHealth() and CaptureState().

namespace RPG.Resources
{
    public class Health : MonoBehaviour, ISaveable
    {
        public enum State
        {
            Alive,
            Dead
        }

        private static readonly int Death = Animator.StringToHash("Death");
        [Range(0,1)] [SerializeField] private float _regenerateHealthPercentage = 1f;
        private LazyValue<float> _health;
        private Animator _animator;
        private GameObject _instigator;

        public State HealthState { get; set; }

        public float HealthPoints
        {
            get { return _health.value; }
        }

        private void Awake()
        {
            _animator = GetComponent<Animator>();
            _health = new LazyValue<float>(InitializeHealth);
        }

        private void Start()
        {
            _health.ForceInit();
        }


        private float InitializeHealth()
        {
            HealthState = State.Alive;
            return GetComponent<BaseStats>().GetStat(Stat.Health);
        }


        private void OnEnable()
        {
            GetComponent<BaseStats>().OnLevelUp += RegenerateHealth;
        }


        private void OnDisable()
        {
            GetComponent<BaseStats>().OnLevelUp -= RegenerateHealth;
        }


        private void RegenerateHealth()
        {
            _health.value = Mathf.Max(HealthPoints,
                                     GetComponent<BaseStats>().GetStat(Stat.Health) * _regenerateHealthPercentage);
        }


        public void TakeDamage(GameObject instigator, float damage)
        {

            print($"{gameObject.name} + took damage: {damage}");

            // Can't kill what's already dead
            if (HealthState == State.Dead)
            {
                return;
            }

            _instigator = instigator;

            // Apply damage
            _health.value = Mathf.Max(HealthPoints - damage, 0f);

            // Check for death
            if (HealthPoints <= Mathf.Epsilon)
            {
                Die();
                AwardXp(_instigator);
            }
        }

        private void Die()
        {
            HealthState = State.Dead;
            _animator.SetTrigger(Death);

            // Disable NavMeshAgent
            GetComponent<ActionScheduler>()?.CancelCurrentAction();
            NavMeshAgent navMeshAgent = GetComponent<NavMeshAgent>();
            if (navMeshAgent != null)
            {
                navMeshAgent.enabled = false;
            }

            // ReSharper disable once LocalVariableHidesMember
            Collider collider = GetComponent<Collider>();
            if (collider != null)
            {
                collider.enabled = false;
            }
        }


        /// <summary>
        /// Reward instigator for kill with experience based on level
        /// </summary>
        private void AwardXp(GameObject instigator)
        {
            int xpAward = Mathf.RoundToInt(GetComponent<BaseStats>().GetStat(Stat.ExperienceReward));
            var experience = instigator.GetComponent<Experience>();
            if (experience != null)
            {
		        experience.GainExperience(xpAward);
            }
        }


        public float GetMaxHealth()
        {
            return GetComponent<BaseStats>()?.GetStat(Stat.Health) ?? 0;
        }

        public object CaptureState()
        {
            return _health.value;
        }

        public void RestoreState(object state)
        {
            _health.value = (float)state;
            // Check for death
            if (_health.value <= Mathf.Epsilon)
            {
                Die();
            }
        }
    }
}

I think I tracked it down. Forgot to change

        public IEnumerator LoadLastScene(string saveFile)
        {
            Dictionary<string, object> state = LoadFile(saveFile);
            int buildIndex = SceneManager.GetActiveScene().buildIndex;
            if (state.ContainsKey("lastSceneBuildIndex"))
            {
                buildIndex = (int)state["lastSceneBuildIndex"];
            }

            if (buildIndex != SceneManager.GetActiveScene().buildIndex)
            {
                yield return SceneManager.LoadSceneAsync(buildIndex);
            }

            RestoreState(state);
        }

to

        public IEnumerator LoadLastScene(string saveFile)
        {
            Dictionary<string, object> state = LoadFile(saveFile);
            int buildIndex = SceneManager.GetActiveScene().buildIndex;
            if (state.ContainsKey("lastSceneBuildIndex"))
            {
                buildIndex = (int)state["lastSceneBuildIndex"];
            }

            yield return SceneManager.LoadSceneAsync(buildIndex);
            RestoreState(state);
        }

Thanks for the help though!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms