Race Condition in Fader - another rename Start to Awake fix

I’ve noticed another race condition, this time in our Fader.cs. In particular, a null reference in Fader.FadeOutImmediate, invoked by SavingWrapper.LoadLastScene. This became a persistent issue for me when I added a few enemies to the Sandbox 2 scene, went through the portal from the main scene, and then quit. Playing the game from this save state would cause the exception and mis-load the scene, reverting to the original scene instead.

The fix, I think, is to change Start to Awake in Fader.cs so that the canvasGroup reference is properly obtained before FadeOutImmediate is invoked by SavingWrapper (which after this lecture now happens even earlier).

I notice by the time the course gets to the “Awake vs Start” video, this change has already been made, so maybe I missed it earlier, or it was changed behind-the-scenes. Either way, it’s eventually fixed.

Very much appreciated your post as I was seeing the exact same issue.

This particular issue has hung up many a student, I can assure you. It is mentioned, though I can’t quite point to where it is. As a rule of thumb:

Cache all references in Awake()
Subscribe to events on other objects in OnEnable()
Actually reference other objects in Start() or Update()

I am having issue with not able to save health percentage that was from early gameplay, if i go other portal my health percentage is full and whenever i save the game the health percentage is not coming from save, its coming with full percentage while other everything works fine…

Do i need to build my own saving system?

here is the code,

using UnityEngine;

using RPG.Saving;

using RPG.Stats;

using RPG.Core;

using System;

namespace RPG.Resources

{

public class Health : MonoBehaviour, ISaveable

{

    [SerializeField] float regenerationPercentage = 60;

    float healthPoints = -1f;

    bool isDead = false;

   private void Start()

   {

       GetComponent<BaseStats>().onLevelUp += RegenerateHealth;

       if (healthPoints < 0)

       {

            healthPoints = GetComponent<BaseStats>().GetStat(Stat.Health);

       }

       

   }

    public bool IsDead()

    {

        return isDead;

    }

   

    public void TakeDamage(GameObject instigator, float damage)

    {

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

        healthPoints = Mathf.Max(healthPoints - damage, 0);

        if(healthPoints == 0)

        {

            Die();

            AwardExperience(instigator);

        }

    }

    public float GetHealthPoints()

    {

        return healthPoints;

    }

    public float GetMaxHealthPoints()

    {

       return GetComponent<BaseStats>().GetStat(Stat.Health);

    }

    public float GetPercentage()

    {

        return 100 * (healthPoints / GetComponent<BaseStats>().GetStat(Stat.Health));

    }

   

    private void Die()

    {

        if (isDead) return;

        isDead = true;

        GetComponent<Animator>().SetTrigger("die");

        GetComponent<ActionScheduler>().CancelCurrentAction();

    }

   

    private void AwardExperience(GameObject instigator)

    {

        Experience experience = instigator.GetComponent<Experience>();

        if (experience == null) return;

        experience.GainExperience(GetComponent<BaseStats>().GetStat(Stat.ExperienceReward));

    }

    private void RegenerateHealth()

    {

        float regenHealthPoints = GetComponent<BaseStats>().GetStat(Stat.Health) * (regenerationPercentage / 100);

        healthPoints = Math.Max(healthPoints, regenHealthPoints);

    }

    public object CaptureState()

    {

        return healthPoints;

    }

    public void RestoreState(object state)

    {

        healthPoints = (float) state;

        

        if (healthPoints <= 0)

        {

            Die();

        }

    }

}

}

There’s no need to build your own saving system. The issue is likely the UI not properly updating after the state is restored.

Let’s add some Debugs to confirm that RestoreState() is working properly…

In CaptureState(), add this to the beginning:

Debug.Log($"{name} is capturing Health - > {healthPoints}");

In RestoreState(), add this after healthPoints = (float) state;

Debug.Log($"{name} is restoring Health - > {healthPoints}");
1 Like

Thanks Brian it worked!! :grin: