Forum begins after the advertisement:


[Part 7] SceneFader NullReferenceException and SaveData EndOfStreamException

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 7] SceneFader NullReferenceException and SaveData EndOfStreamException

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #13221
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Anonymes FRACTAL has helped us point out a few bugs on the project files for Part 7 of this series:

    This is better, I can now move my player, but always bugs. Respawn button is broken and i have 3 error in console :
    NullReferenceException: Object reference not set to an instance of an object
    SceneFader.SetColorImage (System.Single& _alpha, SceneFader+FadeDirection _fadeDirection) (at Assets/Scripts/SceneFader.cs:71)
    SceneFader+d__5.MoveNext () (at Assets/Scripts/SceneFader.cs:40)
    UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at :0)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    SceneTransition:Start() (at Assets/Scripts/SceneTransition.cs:26)
    There can be only one active Event System.
    UnityEngine.EventSystems.EventSystem:OnEnable () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:424)
    EndOfStreamException: Unable to read beyond the end of the stream.
    System.IO.BinaryReader.ReadByte () (at :0)
    System.IO.BinaryReader.Read7BitEncodedInt () (at :0)
    System.IO.BinaryReader.ReadString () (at :0)
    SaveData.LoadBench () (at Assets/Scripts/Singletons/SaveData.cs:71)
    GameManager.RespawnPlayer () (at Assets/Scripts/Singletons/GameManager.cs:64)
    UnityEngine.Events.InvokableCall.Invoke () (at :0)
    UnityEngine.Events.UnityEvent.Invoke () (at :0)
    UnityEngine.UI.Button.Press () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
    UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
    UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:57)
    UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
    UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)
    #13222
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Update 2 March 2024: Rewrote this part to document the solutions better.

    Fixing the EndOfStreamException

    Here are the fixes you’ll need to implement to fix the issues here. For starters, under SaveData.cs, we will need to ensure that LoadPlayerData(), LoadShadeData() and LoadBenchData() checks if the file is empty, so that it doesn’t cause an EndOfStreamException. The changes are highlighted below.

    SaveData.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.SceneManagement;
    
    
    [System.Serializable]
    public struct SaveData
    {
        public static SaveData Instance;
    
        //map stuff
        public HashSet<string> sceneNames;
    
        //bench stuff
        public string benchSceneName;
        public Vector2 benchPos;
    
        //player stuff
        public int playerHealth;
        public int playerHeartShards;
        public float playerMana;
        public int playerManaOrbs;
        public int playerOrbShard;
        public float playerOrb0fill, playerOrb1fill, playerOrb2fill;
        public bool playerHalfMana;
        public Vector2 playerPosition;
        public string lastScene;
    
        public bool playerUnlockedWallJump, playerUnlockedDash, playerUnlockedVarJump;
        public bool playerUnlockedSideCast, playerUnlockedUpCast, playerUnlockedDownCast;
    
        //enemies stuff
        //shade
        public Vector2 shadePos;
        public string sceneWithShade;
        public Quaternion shadeRot;
    
    
    
        public void Initialize()
        {
            if(!File.Exists(Application.persistentDataPath + "/save.bench.data")) //if file doesnt exist, well create the file
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.bench.data"));
            }
            if (!File.Exists(Application.persistentDataPath + "/save.player.data")) //if file doesnt exist, well create the file
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data"));
            }
            if (!File.Exists(Application.persistentDataPath + "/save.shade.data")) //if file doesnt exist, well create the file
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.shade.data"));
            }
    
            if (sceneNames == null)
            {
                sceneNames = new HashSet<string>();
            }
        }
        #region Bench Stuff
        public void SaveBench()
        {
            using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.bench.data")))
            {
                writer.Write(benchSceneName);
                writer.Write(benchPos.x);
                writer.Write(benchPos.y);
            }
        }
        public void LoadBench()
        {
            if(File.Exists(Application.persistentDataPath + "/save.bench.data"))
            string savePath = Application.persistentDataPath + "/save.bench.data";
            if(File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.bench.data")))
                {
                    benchSceneName = reader.ReadString();
                    benchPos.x = reader.ReadSingle();
                    benchPos.y = reader.ReadSingle();
                }
            }
            else
            {
                Debug.Log("Bench doesnt exist");
            }
        }
        #endregion
    
        #region Player stuff
        public void SavePlayerData()
        {
            using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data")))
            {
                playerHealth = PlayerController.Instance.Health;
                writer.Write(playerHealth);
                playerHeartShards = PlayerController.Instance.heartShards;
                writer.Write(playerHeartShards);
    
                playerMana = PlayerController.Instance.Mana;
                writer.Write(playerMana);
                playerHalfMana = PlayerController.Instance.halfMana;
                writer.Write(playerHalfMana);
                playerManaOrbs = PlayerController.Instance.manaOrbs;
                writer.Write(playerManaOrbs);
                playerOrbShard = PlayerController.Instance.orbShard;
                writer.Write(playerOrbShard);
                playerOrb0fill = PlayerController.Instance.manaOrbsHandler.orbFills[0].fillAmount;
                writer.Write(playerOrb0fill);
                playerOrb1fill = PlayerController.Instance.manaOrbsHandler.orbFills[1].fillAmount;
                writer.Write(playerOrb1fill);
                playerOrb2fill = PlayerController.Instance.manaOrbsHandler.orbFills[2].fillAmount;
                writer.Write(playerOrb2fill);
    
                playerUnlockedWallJump = PlayerController.Instance.unlockedWallJump;
                writer.Write(playerUnlockedWallJump);
                playerUnlockedDash = PlayerController.Instance.unlockedDash;
                writer.Write(playerUnlockedDash);
                playerUnlockedVarJump = PlayerController.Instance.unlockedVarJump;
                writer.Write(playerUnlockedVarJump);
    
                playerUnlockedSideCast = PlayerController.Instance.unlockedSideCast;
                writer.Write(playerUnlockedSideCast);
                playerUnlockedUpCast = PlayerController.Instance.unlockedUpCast;
                writer.Write(playerUnlockedUpCast);
                playerUnlockedDownCast = PlayerController.Instance.unlockedDownCast;
                writer.Write(playerUnlockedDownCast);
    
                playerPosition = PlayerController.Instance.transform.position;
                writer.Write(playerPosition.x);
                writer.Write(playerPosition.y);
    
                lastScene = SceneManager.GetActiveScene().name;
                writer.Write(lastScene);
            }
            Debug.Log("saved player data");
    
    
        }
        public void LoadPlayerData()
        {
            if(File.Exists(Application.persistentDataPath + "/save.player.data"))
            string savePath = Application.persistentDataPath + "/save.player.data";
            if(File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data")))
                {
                    playerHealth = reader.ReadInt32();
                    playerHeartShards = reader.ReadInt32();
                    playerMana = reader.ReadSingle();
                    playerHalfMana = reader.ReadBoolean();
                    playerManaOrbs = reader.ReadInt32();
                    playerOrbShard = reader.ReadInt32();
                    playerOrb0fill = reader.ReadSingle();
                    playerOrb1fill = reader.ReadSingle();
                    playerOrb2fill = reader.ReadSingle();
    
                    playerUnlockedWallJump = reader.ReadBoolean();
                    playerUnlockedDash = reader.ReadBoolean();
                    playerUnlockedVarJump = reader.ReadBoolean();
    
                    playerUnlockedSideCast = reader.ReadBoolean();
                    playerUnlockedUpCast = reader.ReadBoolean();
                    playerUnlockedDownCast = reader.ReadBoolean();
    
                    playerPosition.x = reader.ReadSingle();
                    playerPosition.y = reader.ReadSingle();
    
                    lastScene = reader.ReadString();
    
                    SceneManager.LoadScene(lastScene);
                    PlayerController.Instance.transform.position = playerPosition;
                    PlayerController.Instance.halfMana = playerHalfMana;
                    PlayerController.Instance.Health = playerHealth;
                    PlayerController.Instance.heartShards = playerHeartShards;
                    PlayerController.Instance.Mana = playerMana;
                    PlayerController.Instance.manaOrbs = playerManaOrbs;
                    PlayerController.Instance.orbShard = playerOrbShard;
                    PlayerController.Instance.manaOrbsHandler.orbFills[0].fillAmount = playerOrb0fill;
                    PlayerController.Instance.manaOrbsHandler.orbFills[1].fillAmount = playerOrb1fill;
                    PlayerController.Instance.manaOrbsHandler.orbFills[2].fillAmount = playerOrb2fill;
    
                    PlayerController.Instance.unlockedWallJump = playerUnlockedWallJump;
                    PlayerController.Instance.unlockedDash = playerUnlockedDash;
                    PlayerController.Instance.unlockedVarJump = playerUnlockedVarJump;
    
                    PlayerController.Instance.unlockedSideCast = playerUnlockedSideCast;
                    PlayerController.Instance.unlockedUpCast = playerUnlockedUpCast;
                    PlayerController.Instance.unlockedDownCast = playerUnlockedDownCast;
                }
                Debug.Log("load player data");
                Debug.Log(playerHalfMana);
            }
            else
            {
                Debug.Log("File doesnt exist");
                PlayerController.Instance.halfMana = false;
                PlayerController.Instance.Health = PlayerController.Instance.maxHealth;
                PlayerController.Instance.Mana = 0.5f;
                PlayerController.Instance.heartShards = 0;
    
                PlayerController.Instance.unlockedWallJump = false;
                PlayerController.Instance.unlockedDash = false;
                PlayerController.Instance.unlockedVarJump = false;
            }
        }
    
        #endregion
    
        #region enemy stuff
        public void SaveShadeData()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.shade.data")))
            {
                sceneWithShade = SceneManager.GetActiveScene().name;
                shadePos = Shade.Instance.transform.position;
                shadeRot = Shade.Instance.transform.rotation;
    
                writer.Write(sceneWithShade);
    
                writer.Write(shadePos.x);
                writer.Write(shadePos.y);
    
                writer.Write(shadeRot.x);
                writer.Write(shadeRot.y);
                writer.Write(shadeRot.z);
                writer.Write(shadeRot.w);
            }
        }
        public void LoadShadeData()
        {
            if (File.Exists(Application.persistentDataPath + "/save.shade.data"))
            string savePath = Application.persistentDataPath + "/save.shade.data";
            if(File.Exists(savePath) && new FileInfo(savePath).Length > 0)
            {
                using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.shade.data")))
                {
                    sceneWithShade = reader.ReadString();
                    shadePos.x = reader.ReadSingle();
                    shadePos.y = reader.ReadSingle();
    
                    float rotationX = reader.ReadSingle();
                    float rotationY = reader.ReadSingle();
                    float rotationZ = reader.ReadSingle();
                    float rotationW = reader.ReadSingle();
                    shadeRot = new Quaternion(rotationX, rotationY, rotationZ, rotationW);
                }
                Debug.Log("Load shade data");
            }
            else
            {
                Debug.Log("Shade doesnt exist");
            }
        }
        #endregion
    }
    

    Fixing the SceneFader issue

    Please refer to post #13223 below.

    Respawn issue

    With the above fixes, an error will still occur if you die before you first sit on a bench. This will be addressed in a future part of the series.

    Original post

    Below is the original post made by Anonymes:
    I have resolve some of your bug. Add verification 0 byte save file in the load data method and load bench method :
    if (File.Exists(filePath) && new FileInfo(filePath).Length > 0)

    In the bench load method add benchname and benchPos default position in the else condition for prevent respawn bug with new game.

    and for duplicate event system, in game manager add this function :

    
       private void EnsureEventSystem()
        {
            if (FindObjectOfType<EventSystem>() == null)
            {
                GameObject eventSystemGO = new GameObject("EventSystem");
                eventSystemGO.AddComponent<EventSystem>();
                eventSystemGO.AddComponent<StandaloneInputModule>();
            }
        }

    in Awake and delete the event system game object in canva prefab.

    Enjoy. ;)

    #13223
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    For the SceneFader issue at Line 71, it is caused by fadeOutUIImage being null when its function (SetColorImage()) is being called by SceneTransition.cs. This happens because the function is called in SceneTransition’s Start(), which runs before SceneFader’s Start() (where fadeOutUIImage is set).

    This can be fixed by changing the Start() in SceneFader.cs to Awake(), so that it is called before SceneTransition’s Start() runs, ensuring that fadeOutUIImage variable is set before SetColorImage() is called by SceneTransition in our game.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    public class SceneFader : MonoBehaviour
    {
        public float fadeTime;
    
        private Image fadeOutUIImage;
    
        public enum FadeDirection
        {
            In, 
            Out
        }
    
        // Start is called before the first frame update
        void StartAwake()
        {
            fadeOutUIImage = GetComponent<Image>();
        }
    
        // Update is called once per frame
        void Update()
        {
    
        }
    
        public IEnumerator Fade(FadeDirection _fadeDirection)
        {
            float _alpha = _fadeDirection == FadeDirection.Out ? 1 : 0;
            float _fadeEndValue = _fadeDirection == FadeDirection.Out ? 0 : 1;
    
            if(_fadeDirection == FadeDirection.Out)
            {
                while(_alpha >= _fadeEndValue)
                {
                    SetColorImage(ref _alpha, _fadeDirection);
    
                    yield return null;
                }
    
                fadeOutUIImage.enabled = false;
            }
            else
            {
                fadeOutUIImage.enabled = true;
    
                while (_alpha <= _fadeEndValue)
                {
                    SetColorImage(ref _alpha, _fadeDirection);
    
                    yield return null;
                }
            }
        }
    
        public IEnumerator FadeAndLoadScene(FadeDirection _fadeDirection, string _sceneToLoad)
        {
            fadeOutUIImage.enabled = true;
    
            yield return Fade(_fadeDirection);
    
            SceneManager.LoadScene(_sceneToLoad);
        }
    
        void SetColorImage(ref float _alpha, FadeDirection _fadeDirection)
        {
            fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, _alpha);
    
            _alpha += 0.02f * (_fadeDirection == FadeDirection.Out ? -1 : 1);
        }
    }
    #13225
    Aka
    Level 4
    Former Patron
    Helpful?
    Up
    0
    ::

    It’s rather this code :

    <code>    private void EnsureEventSystem()
        {
            if (FindObjectOfType<EventSystem>() == null)
            {
                GameObject eventSystemGO = new GameObject("EventSystem");
                eventSystemGO.AddComponent<EventSystem>();
                eventSystemGO.AddComponent<StandaloneInputModule>();
            }
        }</code>
    #13226
    Aka
    Level 4
    Former Patron
    Helpful?
    Up
    0
    ::

    for prevent respawn problem of position I add this in LoadPlayerData :

    
    LoadBench();
    
    if (benchName != null)
    {
        SceneManager.LoadScene(benchName );
    }
    else
    {
        SceneManager.LoadScene(lastScene);
    }
    
    if (benchPos != null)
    {
        PlayerController.Instance.transform.position = benchPos ;
    }
    else
    {
        PlayerController.Instance.transform.position = playerPosition;
    }
    #13262
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Sorry for taking some time to get back to you. Having a busy week.

    Will have a look through over the weekend and work it into Part 7 ASAP.

    Thanks for your code again!

Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: