Forum begins after the advertisement:


Search Results for 'fader'

Home Forums Search Search Results for 'fader'

Viewing 15 results - 61 through 75 (of 77 total)
  • Author
    Search Results
  • #13614
    Terence
    Keymaster

    Hi Anuk, for the respawn button, have a read of this post and see if it helps fix your issue. It is related to the save issue with benches noted in this topic: https://blog.terresquall.com/community/topic/part-7-scenefader-nullreferenceexception-and-savedata-endofstreamexception/

    Update: put a event system on the canvas with stand alone input module and now, when i click button the game pauses and gives me the error “Coroutine couldn’t be started because the the game object ‘Game Manager’ is inactive!”

    For this issue, you will need to check your Hierarchy and see if the Game Manager object is inactive. If it is, what in your code is turning it inactive when the game is running?

    #13443
    Terence
    Keymaster

    Allan, you might find this helpful: https://blog.terresquall.com/community/topic/part-7-scenefader-nullreferenceexception-and-savedata-endofstreamexception/#post-13222

    It addresses most of the issues, except for the Respawn button not working without a benched saved (you need to sit on the bench once, then the respawn will work).

    We’ll fix the respawn issue later this month: https://www.patreon.com/posts/99646882

    #13441
    Allan Valin
    Participant

    Hey there, I finished watching the part 7 and got a couple issues with the save system (maybe it’d be more, but I decided not to bother implementing the mana cap, shade spawn and map since my uni project just needs to be playable for 10 minutes lol).

    So, the first is the timing for the death screen, even though I followed the same values and tried changing them around, there still this off-sync and teleport situation (see the video below).

    The second problem is that the interact key isn’t working (I changed in the input manager as shown), at 0:27 you can see I stop by the flag after having jumped on the respawn point I set on the platform, but it doesn’t work (the link to the bench file was leading to a site with malware, so I used something random, I tried making it so the animation changes when interacted, but with little success since I can’t interact lol). In the video I end up jumping on the platform again, but I tested without and it’s the same result.

    After I pause the game you can jump to 1:00 because the screen capture didn’t get the IDE window. What I did was remove the comment of //SaveData.Instance.LoadPlayerData(); in PlayerController.cs‘s Start() method. For whatever reason, calling this method destroys the player’s ability to move.

    My player health is bugged as well, but the hearts are filled after the death… any ideas?

    Another problem is that the respawn button only works once (I forgot to record on the first video, check below another one). After I stop the testing you can skip to 00:41, dying while the SaveData.Instance.LoadPlayerData(); is active in the code gets the other behavior going.

    Using P to save manually in another scene works when the code is uncommented.

    Instead of Bench I used “Savepoint”

    Savepoint.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class Savepoint : MonoBehaviour
    {
        public bool interacted;
        private Animator anim;
    
        // Start is called before the first frame update
        void Start()
        {
            anim = GetComponent<Animator>();
            //anim.Play("Flag_Down");
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    
        private void OnTriggerStay2D(Collider2D collision)
        {
            if(collision.CompareTag("Player") && Input.GetButtonDown("Interact"))
            {
                interacted = true;
                //anim.Play("Flag_Up");
    
                SaveData.Instance.savepointSceneName = SceneManager.GetActiveScene().name;
                SaveData.Instance.savepointPos = new Vector2(gameObject.transform.position.x, gameObject.transform.position.y);
                SaveData.Instance.StoreSavepoint();
                SaveData.Instance.SavePlayerData();
            }
        }
    
        private void OnTriggerExit2D(Collider2D collision)
        {
            if (collision.CompareTag("Player") && Input.GetButtonDown("Interact"))
            {
                interacted = false;
            }
        }
    }

    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;
    
        // save current scene name
        public HashSet<string> sceneNames;
    
        // save point
        public string savepointSceneName;
        public Vector2 savepointPos;
    
        // player data
        public int playerHealth;
        public float playerMana;
        public Vector2 playerPosition;
        public string lastScene;
    
        public void Initialize()
        {
            if(!File.Exists(Application.persistentDataPath + "/save.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.data"));
            }
            if (!File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data"));
            }
    
            if (sceneNames == null) sceneNames = new HashSet<string>();
        }
    
        public void StoreSavepoint()
        {
            using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.data")))
            {
                writer.Write(savepointSceneName);
                //writer.Write((int)savepointPos.x);
                writer.Write(savepointPos.x);
                writer.Write(savepointPos.y);
    
            }
        }
    
        public void LoadSavepoint()
        {
            if (File.Exists(Application.persistentDataPath + "/save.data"))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.data")))
                {
                    savepointSceneName = reader.ReadString();
                    savepointPos.x = reader.ReadSingle();
                    savepointPos.y = reader.ReadSingle();
                }
            }
        }
    
        public void SavePlayerData()
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data")))
            {
                playerHealth = PlayerController.Instance.Health;
                writer.Write(playerHealth);
                playerMana = PlayerController.Instance.Mana;
                writer.Write(playerMana);
    
                playerPosition = PlayerController.Instance.transform.position;
                writer.Write(playerPosition.x);
                writer.Write(playerPosition.y);
    
                lastScene = SceneManager.GetActiveScene().name;
                writer.Write(lastScene);
            }
        }
    
        public void LoadPlayerData()
        {
            if (File.Exists(Application.persistentDataPath + "/save.player.data"))
            {
                using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data")))
                {
                    playerHealth = reader.ReadInt32();
                    playerMana = reader.ReadSingle();
                    playerPosition.x = reader.ReadSingle();
                    playerPosition.y = reader.ReadSingle();
                    lastScene = reader.ReadString();
    
                    SceneManager.LoadScene(lastScene);
                    PlayerController.Instance.transform.position = playerPosition;
                    PlayerController.Instance.Health = playerHealth;
                    PlayerController.Instance.Mana = playerMana;
                    
                }
            }
            else
            {
                Debug.Log("File doesn't exist");
                PlayerController.Instance.Health = PlayerController.Instance.maxHealth;
                PlayerController.Instance.Mana = 0.5f;
            }
        }
    }

    GameManager.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : MonoBehaviour
    {
        public string transitionedFromScene;
    
        public Vector2 platformingRespawnPoint;
        public Vector2 respawnPoint;
        [SerializeField] Savepoint savepoint;
    
        public static GameManager Instance { get; private set; }
        private void Awake()
        {
            SaveData.Instance.Initialize();
    
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            SaveScene();
    
            DontDestroyOnLoad(gameObject);
            savepoint = FindObjectOfType<Savepoint>();
        }
    
        public void RespawnPlayer()
        {
            SaveData.Instance.LoadSavepoint();
    
            if(SaveData.Instance.savepointSceneName != null)
            {
                SceneManager.LoadScene(SaveData.Instance.savepointSceneName);
            }
            
            if(SaveData.Instance.savepointPos != null)
            {
                respawnPoint = SaveData.Instance.savepointPos;
            }
            else
            {
                respawnPoint = platformingRespawnPoint;
            }
            /*
            if (savepoint != null)
            {
                if (savepoint.interacted)
                {
                    respawnPoint = savepoint.transform.position;
                }
                else
                {
                    respawnPoint = platformingRespawnPoint;
                }
            }
            else
            {
                respawnPoint = platformingRespawnPoint;
            }
            */
    
            PlayerController.Instance.transform.position = respawnPoint;
            StartCoroutine(UIManager.Instance.DeactivateDeathScreen());
            PlayerController.Instance.Respawned();
        }
    
        private void Update() 
        {
            if(Input.GetKeyDown(KeyCode.P)) // for testing 
            {
                SaveData.Instance.SavePlayerData();
            }
        }
    
        public void SaveScene()
        {
            string currentSceneName = SceneManager.GetActiveScene().name;
            SaveData.Instance.sceneNames.Add(currentSceneName);
        }
    }

    UIManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting;
    using UnityEngine;
    
    public class UIManager : MonoBehaviour
    {
        public static UIManager Instance;
        public SceneFader sceneFader;
    
        [SerializeField] GameObject deathScreen;
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
    
            
        }
    
        private void Start()
        {
            sceneFader = GetComponentInChildren<SceneFader>();
        }
    
        public IEnumerator ActivateDeathScreen()
        {
            yield return new WaitForSecondsRealtime(0.8f);
            StartCoroutine(sceneFader.Fade(SceneFader.FadeDirection.In));
            yield return new WaitForSecondsRealtime(0.8f);
            deathScreen.SetActive(true);
    
        }
    
        public IEnumerator DeactivateDeathScreen()
        {
            yield return new WaitForSecondsRealtime(0.5f);
            deathScreen.SetActive(false);
            StartCoroutine(sceneFader.Fade(SceneFader.FadeDirection.Out));
        }
    
    }

    PlayerController.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using System.Net.Sockets;
    using UnityEngine;
    using UnityEngine.UIElements;
    using UnityEngine.UI;
    
    public class PlayerController : MonoBehaviour
    {
        [Header("Horizontal Movement Settings:")]
        [SerializeField] private float walkSpeed = 1; //sets the players movement speed on the ground
        [Space(5)]
    
        [Header("Vertical Movement Settings")]
        [SerializeField] private float jumpForce = 45f; //sets how hight the player can jump
    
        private int jumpBufferCounter = 0; //stores the jump button input
        [SerializeField] private int jumpBufferFrames; //sets the max amount of frames the jump buffer input is stored
    
        private float coyoteTimeCounter = 0; //stores the Grounded() bool
        [SerializeField] private float coyoteTime; ////sets the max amount of frames the Grounded() bool is stored
    
        private int airJumpCounter = 0; //keeps track of how many times the player has jumped in the air
        [SerializeField] private int maxAirJumps; //the max no. of air jumps
    
        private float gravity; //stores the gravity scale at start
        [Space(5)]
    
        [Header("Ground Check Settings:")]
        [SerializeField] private Transform groundCheckPoint; //point at which ground check happens
        [SerializeField] private float groundCheckY = 0.2f; //how far down from ground chekc point is Grounded() checked
        [SerializeField] private float groundCheckX = 0.5f; //how far horizontally from ground chekc point to the edge of the player is
        [SerializeField] private LayerMask whatIsGround; //sets the ground layer
        [Space(5)]
    
        [Header("Dash Settings")]
        [SerializeField] private float dashSpeed; //speed of the dash
        [SerializeField] private float dashTime; //amount of time spent dashing
        [SerializeField] private float dashCooldown; //amount of time between dashes
        [SerializeField] GameObject dashEffect;
        private bool canDash = true, dashed;
        [Space(5)]
    
        [Header("Attack Settings:")]
        [SerializeField] private Transform SideAttackTransform; //the middle of the side attack area
        [SerializeField] private Vector2 SideAttackArea; //how large the area of side attack is
    
        [SerializeField] private Transform UpAttackTransform; //the middle of the up attack area
        [SerializeField] private Vector2 UpAttackArea; //how large the area of side attack is
    
        [SerializeField] private Transform DownAttackTransform; //the middle of the down attack area
        [SerializeField] private Vector2 DownAttackArea; //how large the area of down attack is
    
        [SerializeField] private LayerMask attackableLayer; //the layer the player can attack and recoil off of
    
        private float timeBetweenAttack, timeSinceAttck;
    
        [SerializeField] private float damage; //the damage the player does to an enemy
    
        [SerializeField] private GameObject slashEffect; //the effect of the slashs
        [Space(5)]
    
        bool restoreTime;
        float restoreTimeSpeed;
        [Space(5)]
    
        [Header("Recoil Settings:")]
        [SerializeField] private int recoilXSteps = 5; //how many FixedUpdates() the player recoils horizontally for
        [SerializeField] private int recoilYSteps = 5; //how many FixedUpdates() the player recoils vertically for
    
        [SerializeField] private float recoilXSpeed = 100; //the speed of horizontal recoil
        [SerializeField] private float recoilYSpeed = 100; //the speed of vertical recoil
    
        private int stepsXRecoiled, stepsYRecoiled; //the no. of steps recoiled horizontally and verticall
        [Space(5)]
    
        [Header("Health Settings")]
        public int health;
        public int maxHealth;
        [SerializeField] GameObject bloodSpurt;
        [SerializeField] float hitFlashSpeed;
        public delegate void OnHealthChangedDelegate();
        [HideInInspector] public OnHealthChangedDelegate onHealthChangedCallback;
        float healTimer;
        [SerializeField] float timeToHeal;
        [Space(5)]
    
        [Header("Mana Settings")]
        [SerializeField] UnityEngine.UI.Image manaStorage;
        [SerializeField] float mana;
        [SerializeField] float manaDrainSpeed;
        [SerializeField] float manaGain;
        [Space(5)]
    
        [Header("Spell Settings")]
        //spell stats
        [SerializeField] float manaSpellCost = 0.3f;
        [SerializeField] float timeBetweenCast = 0.5f;
        [SerializeField] float spellDamage; //upspellexplosion and downspellfireball
        [SerializeField] float downSpellForce; // desolate dive only
        //spell cast objects
        [SerializeField] GameObject sideSpellFireball;
        [SerializeField] GameObject upSpellExplosion;
        [SerializeField] GameObject downSpellFireball;
        float timeSinceCast;
        float castOrHealTimer;
        [Space(5)]
    
        [HideInInspector] public PlayerStateList pState;
        private Animator anim;
        public Rigidbody2D rb;
        private SpriteRenderer sr;
    
        //Input Variables
        private float xAxis, yAxis;
        private bool attack = false;
        private bool canFlash = true;
    
        public static PlayerController Instance;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
            
        }
    
        // Start is called before the first frame update
        void Start()
        {
            pState = GetComponent<PlayerStateList>();
            rb = GetComponent<Rigidbody2D>();
            sr = GetComponent<SpriteRenderer>();
            anim = GetComponent<Animator>();
    
            //SaveData.Instance.LoadPlayerData();
            gravity = rb.gravityScale;
    
            Mana = mana;
            manaStorage.fillAmount = Mana;
            Health = maxHealth;
        }
    
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea);
            Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea);
            Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea);
        }
    
        // Update is called once per frame
        void Update()
        {
            if (pState.cutscene) return;
            if(pState.alive)
            {
                GetInputs();
            }
            
            UpdateJumpVariables();
            RestoreTimeScale();
    
            if (pState.dashing) return;
            if (pState.alive)
            {
                Flip();
                Move();
                Jump();
                StartDash();
                Attack();
                Heal();
                CastSpell();
            }
            FlashWhileInvincible();
    
        }
        private void OnTriggerEnter2D(Collider2D _other) //for up and down cast spell
        {
            if (_other.GetComponent<Enemy>() != null && pState.casting)
            {
                _other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position - transform.position).normalized, -recoilYSpeed);
            }
        }
    
        private void FixedUpdate()
        {
            if (pState.cutscene) return;
    
            if (pState.dashing) return;
            Recoil();
        }
    
        void GetInputs()
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetButtonDown("Attack");
    
            if (Input.GetButton("Cast/Heal"))
            {
                castOrHealTimer += Time.deltaTime;
            }
            else
            {
                castOrHealTimer = 0;
            }
        }
    
        void Flip()
        {
            if (xAxis < 0)
            {
                transform.localScale = new Vector2(-1, transform.localScale.y);
                pState.lookingRight = false;
            }
            else if (xAxis > 0)
            {
                transform.localScale = new Vector2(1, transform.localScale.y);
                pState.lookingRight = true;
            }
        }
    
        private void Move()
        {
            rb.velocity = new Vector2(walkSpeed * xAxis, rb.velocity.y);
            anim.SetBool("Walking", rb.velocity.x != 0 && Grounded());
        }
    
        void StartDash()
        {
            if (Input.GetButtonDown("Dash") && canDash && !dashed)
            {
                StartCoroutine(Dash());
                dashed = true;
            }
    
            if (Grounded())
            {
                dashed = false;
            }
        }
    
        IEnumerator Dash()
        {
            canDash = false;
            pState.dashing = true;
            anim.SetTrigger("Dashing");
            rb.gravityScale = 0;
            int _dir = pState.lookingRight ? 1 : -1;
            rb.velocity = new Vector2(_dir * dashSpeed, 0);
            if (Grounded()) Instantiate(dashEffect, transform);
            yield return new WaitForSeconds(dashTime);
            rb.gravityScale = gravity;
            pState.dashing = false;
            yield return new WaitForSeconds(dashCooldown);
            canDash = true;
        }
        public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay)
        {
            //If exit direction is upwards
            if (_exitDir.y > 0)
            {
                rb.velocity = jumpForce * _exitDir;
            }
    
            //If exit direction requires horizontal movement
            if (_exitDir.x != 0)
            {
                xAxis = _exitDir.x > 0 ? 1 : -1;
    
                Move();
            }
    
            Flip();
            yield return new WaitForSeconds(_delay);
            pState.cutscene = false;
        }
        void Attack()
        {
            timeSinceAttck += Time.deltaTime;
            if (attack && timeSinceAttck >= timeBetweenAttack)
            {
                int _recoilLeftOrRight = pState.lookingRight ? 1 : -1;
                timeSinceAttck = 0;
                anim.SetTrigger("Attacking");
    
                if (yAxis == 0 || yAxis < 0 && Grounded())
                {
                    Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, Vector2.right * _recoilLeftOrRight, recoilXSpeed);
                    Instantiate(slashEffect, SideAttackTransform);
                }
                else if (yAxis > 0)
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, Vector2.up, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, 80, UpAttackTransform);
                }
                else if (yAxis < 0 && !Grounded())
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, Vector2.down, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, -90, DownAttackTransform);
                }
            }
    
        }
        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, Vector2 _recoilDir, float _recoilStrength)
        {
            Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
    
            if (objectsToHit.Length > 0)
            {
                _recoilBool = true;
            }
            for (int i = 0; i < objectsToHit.Length; i++)
            {
                if (objectsToHit[i].GetComponent<Enemy>() != null)
                {
                    objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir, _recoilStrength);
    
                    if (objectsToHit[i].CompareTag("Enemy"))
                    {
                        Mana += manaGain;
                    }
                }
            }
        }
        void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform)
        {
            _slashEffect = Instantiate(_slashEffect, _attackTransform);
            _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
        void Recoil()
        {
            if (pState.recoilingX)
            {
                if (pState.lookingRight)
                {
                    rb.velocity = new Vector2(-recoilXSpeed, 0);
                }
                else
                {
                    rb.velocity = new Vector2(recoilXSpeed, 0);
                }
            }
    
            if (pState.recoilingY)
            {
                rb.gravityScale = 0;
                if (yAxis < 0)
                {
                    rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed);
                }
                else
                {
                    rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed);
                }
                airJumpCounter = 0;
            }
            else
            {
                rb.gravityScale = gravity;
            }
    
            //stop recoil
            if (pState.recoilingX && stepsXRecoiled < recoilXSteps)
            {
                stepsXRecoiled++;
            }
            else
            {
                StopRecoilX();
            }
            if (pState.recoilingY && stepsYRecoiled < recoilYSteps)
            {
                stepsYRecoiled++;
            }
            else
            {
                StopRecoilY();
            }
    
            if (Grounded())
            {
                StopRecoilY();
            }
        }
        void StopRecoilX()
        {
            stepsXRecoiled = 0;
            pState.recoilingX = false;
        }
        void StopRecoilY()
        {
            stepsYRecoiled = 0;
            pState.recoilingY = false;
        }
        public void TakeDamage(float _damage)
        {
            if (pState.alive)
            {
                Health -= Mathf.RoundToInt(_damage);
                if(Health <= 0)
                {
                    Health = 0;
                    StartCoroutine(Death());
                }
                else
                {
                    StartCoroutine(StopTakingDamage());
                }
            }
            
            
        }
        IEnumerator StopTakingDamage()
        {
            pState.invincible = true;
            GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity);
            Destroy(_bloodSpurtParticles, 1.5f);
            anim.SetTrigger("TakeDamage");
            yield return new WaitForSeconds(1f);
            pState.invincible = false;
        }
    
        IEnumerator Flash()
        {
            sr.enabled = !sr.enabled;
            canFlash = false;
            yield return new WaitForSeconds(0.1f);
            canFlash = true;
        }
        void FlashWhileInvincible()
        {
            if (pState.invincible)
            {
                if (Time.timeScale > 0.2 && canFlash)
                {
                    StartCoroutine(Flash());
                }
            }
            else
            {
                sr.enabled = true;
            }
        }
    
        void RestoreTimeScale()
        {
            if (restoreTime)
            {
                if (Time.timeScale < 1)
                {
                    Time.timeScale += Time.deltaTime * restoreTimeSpeed;
                }
                else
                {
                    Time.timeScale = 1;
                    restoreTime = false;
                }
            }
        }
    
        public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
        {
            restoreTimeSpeed = _restoreSpeed;
            if (_delay > 0)
            {
                StopCoroutine(StartTimeAgain(_delay));
                StartCoroutine(StartTimeAgain(_delay));
            }
            else
            {
                restoreTime = true;
            }
            Time.timeScale = _newTimeScale;
        }
    
        IEnumerator StartTimeAgain(float _delay)
        {
            restoreTime = true;
            yield return new WaitForSeconds(_delay);
        }
    
        IEnumerator Death()
        {
            pState.alive = false;
            Time.timeScale = 1f;
            GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity);
            Destroy(_bloodSpurtParticles, 1.5f);
            anim.SetTrigger("Death");
            yield return new WaitForSecondsRealtime(0.9f);
    
            StartCoroutine(UIManager.Instance.ActivateDeathScreen());
    
            //Respawned();
        }
    
        public void Respawned()
        {
            if(!pState.alive)
            {
                pState.alive = true;
                Health = maxHealth;
                anim.Play("Player_Idle");
                mana = 0;
            }
        }
    
        public int Health
        {
            get { return health; }
            set
            {
                if (health != value)
                {
                    health = Mathf.Clamp(value, 0, maxHealth);
    
                    if (onHealthChangedCallback != null)
                    {
                        onHealthChangedCallback.Invoke();
                    }
                }
            }
        }
    
        void Heal()
        {
            if (Input.GetButton("Cast/Heal") && castOrHealTimer > 0.05f && Health < maxHealth && Mana > 0 && !pState.jumping && !pState.dashing)
            {
                pState.healing = true;
                anim.SetBool("Healing", true);
    
                //healing
                healTimer += Time.deltaTime;
                if (healTimer >= timeToHeal)
                {
                    Health++;
                    healTimer = 0;
                }
    
                //drain mana
                Mana -= Time.deltaTime * manaDrainSpeed;
            }
            else
            {
                pState.healing = false;
                anim.SetBool("Healing", false);
                healTimer = 0;
            }
        }
    
        public float Mana
        {
            get { return mana; }
            set
            {
                //if mana stats change
                if (mana != value)
                {
                    mana = Mathf.Clamp(value, 0, 1);
                    manaStorage.fillAmount = Mana;
                }
            }
        }
    
        void CastSpell()
        {
            if (Input.GetButtonUp("Cast/Heal") && castOrHealTimer <= 0.05f && timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost)
            {
                pState.casting = true;
                timeSinceCast = 0;
                StartCoroutine(CastCoroutine());
            }
            else
            {
                timeSinceCast += Time.deltaTime;
            }
    
            if (Grounded())
            {
                //disable downspell if on the ground
                downSpellFireball.SetActive(false);
            }
            //if down spell is active, force player down until grounded
            if (downSpellFireball.activeInHierarchy)
            {
                rb.velocity += downSpellForce * Vector2.down;
            }
        }
    
        IEnumerator CastCoroutine()
        {
            anim.SetBool("Casting", true);
            yield return new WaitForSeconds(0.15f); // check on Animator if it matches
    
            //side cast
            if (yAxis == 0 || (yAxis < 0 && Grounded()))
            {
                GameObject _fireBall = Instantiate(sideSpellFireball, SideAttackTransform.position, Quaternion.identity);
    
                //flip fireball
                if (pState.lookingRight)
                {
                    _fireBall.transform.eulerAngles = Vector3.zero; // if facing right, fireball continues as per normal
                }
                else
                {
                    _fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180);
                    //if not facing right, rotate the fireball 180 deg
                }
                pState.recoilingX = true;
            }
    
            //up cast
            else if (yAxis > 0)
            {
                Instantiate(upSpellExplosion, transform);
                rb.velocity = Vector2.zero;
            }
    
            //down cast
            else if (yAxis < 0 && !Grounded())
            {
                downSpellFireball.SetActive(true);
            }
    
            Mana -= manaSpellCost;
            yield return new WaitForSeconds(0.35f); // check Animator
            anim.SetBool("Casting", false);
            pState.casting = false;
        }
        public bool Grounded()
        {
            if (Physics2D.Raycast(groundCheckPoint.position, Vector2.down, groundCheckY, whatIsGround)
                || Physics2D.Raycast(groundCheckPoint.position + new Vector3(groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround)
                || Physics2D.Raycast(groundCheckPoint.position + new Vector3(-groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    
        void Jump()
        {
            if (!pState.jumping && jumpBufferCounter > 0 && coyoteTimeCounter > 0)
            {
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
    
                pState.jumping = true;
            }
            
            if (!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump"))
            {
                pState.jumping = true;
    
                airJumpCounter++;
    
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
            }
    
            if (Input.GetButtonUp("Jump") && rb.velocity.y > 3)
            {
                rb.velocity = new Vector2(rb.velocity.x, 0);
    
                pState.jumping = false;
            }
    
            anim.SetBool("Jumping", !Grounded());
        }
    
        void UpdateJumpVariables()
        {
            if (Grounded())
            {
                pState.jumping = false;
                coyoteTimeCounter = coyoteTime;
                airJumpCounter = 0;
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
    
            if (Input.GetButtonDown("Jump"))
            {
                jumpBufferCounter = jumpBufferFrames;
            }
            else
            {
                jumpBufferCounter--;
            }
        }
    }
    Terence
    Keymaster

    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();
        }
    
        // 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);
        }
    }
    Terence
    Keymaster

    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. ;)

    Terence
    Keymaster

    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 :

    1.

    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)

    2.

    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)

    3.

    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)
    #13197
    Allan Valin
    Participant

    Hey, I’m here again.

    Changing the Enemy script to use Start() instead of Awake() isn’t mentioned on the blog post (after section 2.c).

    Section 2.d on the video tells to change “this” to “gameObject”, but previous code already had that. Health = maxHealth; is moved from Awake() to Start(), but that isn’t shown on the code (it was just removed), and while it’s shown on the video, it’s very fast and not mentioned.

    The 2D DarkCave Assets pack is said to be in the description of the video, but it isn’t. Though it is on the blog post.

    On Section 3, I noticed that my camera settings weren’t the same as shown in the video, values for Position, Projection, and MSAA were different (I think I followed the tutorial pretty closely, so it was either not mentioned or too fast for me to notice the changes). Also, the Offset value on my script was -20 and the Inspector on the video shows -15.

    I don’t think it was mentioned to remove the previous “Floor” objects from testing.

    About wall placement, video and blog say -1, but a card on the video says 1.

    When adding the fog, I can only see the column if the alpha value is under 20, somehow.
    At the beginning of the video it’s said to make the player a prefab, what about the enemy?

    When adding vegetation, the blog says to use the Multiple mode (and shows a Single image), while the videos doesn’t tell to change that. I tried Multiple and with that you can’t drag and drop the image on the scene, so I assume it was just a copy and paste gone wrong there.

    At 29:22 it’s said to go to Edit > Build Settings, but for me it is File > Build Settings.

    In the blog post, the part about the transition manager doesn’t have a semicolon on “SceneManager.LoadScene(transitionTo)”.

    The public IEnumerator WalkIntoNewScene() has a “<” out of place around line 281 on the Player Controller (blog post). On the blog it shows the line “if (_exitDir.y != 0)” while the video shows “if (_exitDir.y > 0)”.

    The code for PlayerStateList adding “cutscene” isn’t on the blog. Adding “if (pState.cutscene) return;” in Update() and FixedUpdate() isn’t shown as well.

    I think “PlayerController.Instance.pState.cutscene = true;” on the SceneTransition trigger wasn’t mentioned (isn’t on the blog).

    At 34:11 the Exit Time was changed offscreen from 0.25 to 0.5.

    The use of Prefabs for the Scene Transition seems to cause me bugs. Even though I changed the name of the destination, it still uses the value of the prefab instead, e.g. I’d make a prefab of the cave_2 transition, use it on cave_2 and assign it to cave_2, and every time I’d exit cave_2, I’d enter it. At some point I even went to cave_3 when I removed the scene name from the prefab (even though it was still assigned after dragging the prefab to the hierarchy).

    At 35:51 there are multiple steps that aren’t mentioned, and even with the blog description I’m not sure I got it after even watching at 0,25x speed. A script was added, the scene fader was dragged to the prefab… in 10 seconds there are at least 7 steps.
    The part at 36:11 was clearly an edit problem, it shows a previous state of the Scene Fader object, when it didn’t have a script and was still white and not stretched.

    using UnityEngine.UI; isn’t shown on the code for SceneFader.cs on the blog.
    fadeOutUIImage has line break where the serialize field should’ve been removed.
    The code for FadeDirection was skipped and isn’t shown on later codes.
    [SerializeField] private float fadeTime; isn’t declared.
    On UIManager.cs, only sceneFader = GetComponentInChildren<SceneFader>(); is declared, without variable or Start() function shown on the blog.
    The first code block of SceneFader names the class as “UIManager” instead of SceneFader.

    Trying to copy the drag and drop to supposedly update the Prefab I ended up fucking everything up and basically replacing the HP and mana with the scene fader… which made the player prefab lose the reference to mana and I can’t seem to update it correctly.

    When fixing the flashing effect, the variable canFlash isn’t in the code.

    After fixing the flashing effect the player object is shown as lower quality and the camera is zoomed in for whatever reason. It’s like it went to 140p video quality. (restarting the editor fixed it).

    For whatever reason, the heart containerts won’t appear filled up anymore, without me changing anything.

    Starting on part 6 I’ll probably just ask questions if I can’t solve the problems I find.

    Terence
    Keymaster

    Hi Haydn,

    The error should be here:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class SceneTransition : MonoBehaviour
    {
    
        [SerializeField] private string transitionTo;
    
        [SerializeField] private Transform startPoint;
    
        [SerializeField] private Vector2 exitDirection;
    
        [SerializeField] private float exitTime;
    
        private void Start()
        {
            if(transitionTo == GameManager.instance.transitionFromScene)
            {
                PlayerController.instance.transform.position = startPoint.position;
    
                StartCoroutine(PlayerController.instance.WalkIntoNewScnene(exitDirection, exitTime));
            }
            StartCoroutine(UiManager.instance.sceneFader.fade(ScreenFader.fadeDirection.Out));
    
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.instance.transitionFromScene = SceneManager.GetActiveScene().name;
    
                PlayerController.instance.pState.transition = true;
    
                StartCoroutine(UiManager.instance.sceneFader.FadeAndLoadScene(ScreenFader.fadeDirection.In, transitionTo));
                Debug.Log(UiManager.instance);
                Debug.Log(UiManager.instance.sceneFader);
            }
        }
    }

    Can you put the lines highlighted in green above and show me the Console output? And show me your Inspector for the UIManager as well.

    Haydn V
    Participant

    there is an error that happens when my player touches the scene transition in makes the player not enter the next scene
    the error points out the NullReferenceException: Object reference not set to an instance of an object
    SceneTransition.OnTriggerEnter2D (UnityEngine.Collider2D _other) (at Assets/scripts/SceneTransition.cs:37)

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class SceneTransition : MonoBehaviour
    {
    
        [SerializeField] private string transitionTo;
    
        [SerializeField] private Transform startPoint;
    
        [SerializeField] private Vector2 exitDirection;
    
        [SerializeField] private float exitTime;
    
        private void Start()
        {
            if(transitionTo == GameManager.instance.transitionFromScene)
            {
                PlayerController.instance.transform.position = startPoint.position;
    
                StartCoroutine(PlayerController.instance.WalkIntoNewScnene(exitDirection, exitTime));
            }
            StartCoroutine(UiManager.instance.sceneFader.fade(ScreenFader.fadeDirection.Out));
    
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.instance.transitionFromScene = SceneManager.GetActiveScene().name;
    
                PlayerController.instance.pState.transition = true;
    
                StartCoroutine(UiManager.instance.sceneFader.FadeAndLoadScene(ScreenFader.fadeDirection.In, transitionTo));
    
            }
        }
    }
    #12643
    Niyam Shah
    Participant

    It’s referring to the UI managers scene fader coroutine. I’ve added debuglog.actives and it’s returning false in the respawn function but true if I place it anywhere else

    #12570
    Terence
    Keymaster

    Hi Niyam,

    If a game freezes when you run code, usually its because you have a loop that does not terminate. Try commenting your while loops out in SceneFader and see if the freeze still happens.

        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;
                    }
                }
    
        }
    

    If this fixes the issue, look into your while loop’s condition and try to find out what is causing the condition to never be met.

    #12568
    Niyam Shah
    Participant

    not sure why – no compiler or any errors occuring – when i touchs spikes, my game will freeze. ive set debug logs in every stage of the IEnumerator and it seens to not work when the scene fader occurs (it works during scene transitions)
    ive set a respawner in my game and all script stuff is proper

    reference for my spike script – debug log will print – 1, 2, 3, 4, respawn, takes damage

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Spikes : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                StartCoroutine(RespawnPoint());
                Debug.Log("respawn");
            }
        }
    
        IEnumerator RespawnPoint()
        {
            PlayerController.Instance.pState.cutscene = true;
            Debug.Log("1");
            PlayerController.Instance.pState.invincible = true;
            Debug.Log("2");
            PlayerController.Instance.rb.velocity = Vector2.zero;
            Debug.Log("3");
            Time.timeScale = 0;
            StartCoroutine(UIManager.Instance.sceneFader.Fade(SceneFader.FadeDirection.In));
            Debug.Log("4");
            PlayerController.Instance.TakeDamage(1);
            Debug.Log("Takes damage");
            yield return new WaitForSeconds(1);
            Debug.Log("5");
            PlayerController.Instance.transform.position = GameManager.Instance.platformingRespawnPoint;
            Debug.Log("6");
            StartCoroutine(UIManager.Instance.sceneFader.Fade(SceneFader.FadeDirection.Out));
            Debug.Log("7");
            yield return new WaitForSeconds(UIManager.Instance.sceneFader.fadeTime);
            Debug.Log("8");
            PlayerController.Instance.pState.cutscene = false;
            PlayerController.Instance.pState.invincible = false;
            Debug.Log("9");
            Time.timeScale = 1;
        }
    }
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    public class SceneFader : MonoBehaviour
    {
        [SerializeField] public float fadeTime;
    
        private Image fadeOutUIImage;
    
        public enum FadeDirection
        {
            In,
            Out
        }
    
        private void Awake()
        {
            
                fadeOutUIImage = GetComponent<Image>();
        }
    
        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 += Time.deltaTime * (1 / fadeTime) * (_fadeDirection == FadeDirection.Out ? -1 : 1);
    
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    public class SceneTransition : MonoBehaviour
    {
        [SerializeField] private string transitionTo; //Represents the scene to transition to
    
        [SerializeField] private Transform startPoint; //Defines the player's entry point in the scene
    
        [SerializeField] private Vector2 exitDirection; //Specifies the direction for the player's exit
    
        [SerializeField] private float exitTime; //Determines the time it takes for the player to exit the scene transition
    
    
    
        // Start is called before the first frame update
        private void Start()
    
        {
            if (GameManager.Instance.transitionedFromScene == transitionTo)
            {
                PlayerController.Instance.transform.position = startPoint.position;
    
                StartCoroutine(PlayerController.Instance.WalkIntoNewScene(exitDirection, exitTime));
            }
    
            StartCoroutine(UIManager.Instance.sceneFader.Fade(SceneFader.FadeDirection.Out));
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.Instance.transitionedFromScene = SceneManager.GetActiveScene().name;
    
                SceneManager.LoadScene(transitionTo);
    
                PlayerController.Instance.pState.cutscene = true;
    
                StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo));
    
            }
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class GameManager : MonoBehaviour
    {
        public string transitionedFromScene;
    
        public Vector2 platformingRespawnPoint;
    
        public static GameManager Instance { get; private set; }
        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
            DontDestroyOnLoad(gameObject);
        }
    
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlatformRespawnPoint : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
    
        }
    
        // Update is called once per frame
        void Update()
        {
    
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.Instance.platformingRespawnPoint = transform.position;
            }
        }
    }
    #12536
    Niyam Shah
    Participant

    i tried using a debug log and it works but im still getting these errors at random places:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    public class SceneTransition : MonoBehaviour
    {
        [SerializeField] private string transitionTo; //Represents the scene to transition to
    
        [SerializeField] private Transform startPoint; //Defines the player's entry point in the scene
    
        [SerializeField] private Vector2 exitDirection; //Specifies the direction for the player's exit
    
        [SerializeField] private float exitTime; //Determines the time it takes for the player to exit the scene transition
    
        // Start is called before the first frame update
        private void Start()
    
        {
            if (GameManager.Instance.transitionedFromScene == transitionTo)
            {
                PlayerController.Instance.transform.position = startPoint.position;
    
                StartCoroutine(PlayerController.Instance.WalkIntoNewScene(exitDirection, exitTime));
            }
    
            StartCoroutine(UIManager.Instance.sceneFader.Fade(SceneFader.FadeDirection.Out));
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.Instance.transitionedFromScene = SceneManager.GetActiveScene().name;
    
                SceneManager.LoadScene(transitionTo);
    
                PlayerController.Instance.pState.cutscene = true;
    
                StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo));
    
                Debug.Log("scene fader works");
            }
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class PlayerController : MonoBehaviour
    {
        [Header("Horizontal Movement Settings")]
        [SerializeField] private float walkSpeed = 1;
        [Space(5)]
    
        [Header("Dash Settings")]
        [SerializeField] private float dashSpeed;
        [SerializeField] private float dashTime;
        [SerializeField] private float dashCooldown;
        [SerializeField] GameObject dashEffect;
        private bool canDash = true;
        private bool dashed;
        [Space(5)]
    
        [Header("Vertical Movement Settings")]
        [SerializeField] private float jumpForce = 45;
        private int jumpBufferCounter = 0;
        [SerializeField] private int jumpBufferFrames;
        private float coyoteTimeCounter = 0;
        [SerializeField] private float coyoteTime;
        [SerializeField] private float airJumpsCounter = 0;
        [SerializeField] private int maxAirJumps;
        [Space(5)]
    
        [Header("recoil")]
        [SerializeField] int recoilXSteps = 5;
        [SerializeField] int recoilYSteps = 5;
        [SerializeField] float recoilXSpeed = 100; 
        [SerializeField] float recoilYSpeed = 100;
        private int stepsXRecoiled, stepsYRecoiled;
        [Space(5)]
    
        [Header("Attack Settings")]
        private bool attack = false;
        private float timeBetweenAttack, timeSinceAttack;
        [SerializeField] Transform SideAttackTransform, BehindAttackTransform, UpAttackTransform, DownAttackTransform;
        [SerializeField] Vector2 SideAttackArea, BehindAttackArea, UpAttackArea, DownAttackArea;
        [SerializeField] LayerMask attackableLayer;
        [SerializeField] float damage;
        [SerializeField] GameObject slashEffect;
        [SerializeField] GameObject upSlashEffect;
        [SerializeField] GameObject downSlashEffect;
        bool restoreTime;
        float restoreTimeSpeed;
        [Space(5)]
    
        [Header("Ground Check Settings")]
        [SerializeField] private Transform groundCheckPoint;
        [SerializeField] private float groundCheckY = 0.2f;
        [SerializeField] private float groundCheckX = 0.5f;
        [SerializeField] private LayerMask whatIsGround;
        [Space(5)]
    
        [Header("Health")]
        public int health;
        public int maxHealth;
        [SerializeField] GameObject bloodSpurt;
        [SerializeField] GameObject crackeffect;
        [SerializeField] float hitFlashSpeed;
        public delegate void OnHealthChangedDelegate(); // delegate voids can be used on multiple methods
        [HideInInspector] public OnHealthChangedDelegate onHealthChangedCallBack;
        float healTimer;
        [SerializeField] float timeToHeal;
        [Space(5)]
    
        [Header("Mana settings")]
        [SerializeField] UnityEngine.UI.Image manaStorage;
        [SerializeField] float mana;
        [SerializeField] float manaDrainSpeed;
        [SerializeField] float manaGain;
        [Space(5)]
    
        [Header("Spell Settings")]
        //spell stats
        [SerializeField] float manaSpellCost;
        [SerializeField] float timeBetweenCast = 0.5f;
        float timeSinceCast;
        [SerializeField] float spellDamage; 
        //spell cast objects
        [SerializeField] GameObject upSpellExplosion;
        [Space(5)]
    
        [HideInInspector] public playerStatesList pState;
    
        private float xAxis, yAxis;
        private Rigidbody2D rb;
        private float gravity;
        private Animator anim;
        private SpriteRenderer sr;
    
    
    
        public static PlayerController Instance;
    
        private void Awake()
        {
            if(Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
    
            DontDestroyOnLoad(gameObject);
        }
    
    
    
        // Start is called before the first frame update
        void Start()
        {
            pState = GetComponent<playerStatesList>();
    
            rb = GetComponent<Rigidbody2D>();
    
            anim = GetComponent<Animator>();
    
            sr = GetComponent<SpriteRenderer>();
    
            gravity = rb.gravityScale;
    
            Mana = mana;
    
            manaStorage.fillAmount = Mana;
    
            Health = maxHealth;
    
        }
    
    
    
        //draw out hitboxes for basic attacks
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea);
            Gizmos.DrawWireCube(BehindAttackTransform.position, BehindAttackArea);
            Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea);
            Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea);
        }
    
    
    
        // Update is called once per frame
        void Update()
        {
            if (pState.cutscene) return;
    
            GetInputs();
            updateJumpVariable();
            RestoreTimeScale();
    
            if (pState.dashing) return;
            Flip();
            Move();
            Jump();
            StartDash();
            Attack();
            FlashWhileInvincible();
            Heal();
            CastSpell();
        }
        private void OnTriggerEnter2D(Collider2D _other) //for up and down cast spell
        {
            if (_other.GetComponent<Enemy>() != null && pState.casting)
            {
                _other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position - transform.position).normalized, -recoilYSpeed);
            }
        }
        private void FixedUpdate()
        {
            if (pState.cutscene) return;
    
            if (pState.dashing) return;
            Recoil();
        }
    
        //basic inputs for movement
        void GetInputs()
        {
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetButtonDown("Attack");
        }
    
    
    
        // flip player on x axis when left or right is picked
        void Flip()
        {
            if(xAxis < 0)
            {
                transform.localScale = new Vector2(-1, transform.localScale.y);
                pState.lookingRight = false;
            }
    
            else if(xAxis > 0)
            {
                transform.localScale = new Vector2(1, transform.localScale.y);
                pState.lookingRight = true;
            }
    
            
        }
    
    
    
        //move left and right
        private void Move()
        {
            rb.velocity = new Vector2(walkSpeed * xAxis, rb.velocity.y);
            anim.SetBool("walking", rb.velocity.x != 0 && Grounded());
        }
    
    
    
        //allows for dashing and air dashing
        void StartDash()
        {
            if(Input.GetButtonDown("Dash") && canDash && !dashed)
            {
                StartCoroutine(Dash());
                dashed = true;
            }
    
            if (Grounded())
            {
                dashed = false;
            }
        }
        //stops middair infinite dashing
        IEnumerator Dash()
        {
            canDash = false;
    
            pState.dashing = true;
    
            anim.SetTrigger("Dashing");
    
            rb.gravityScale = 0;
    
            int _dir = pState.lookingRight ? 1 : -1;
    
            rb.velocity = new Vector2(_dir * dashSpeed, 0);
            if (Grounded()) Instantiate(dashEffect, transform);
    
            yield return new WaitForSeconds(dashTime);
    
            rb.gravityScale = gravity;
    
            pState.dashing = false;
    
            yield return new WaitForSeconds(dashCooldown);
    
            canDash = true;
        }
    
    
        public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay)
        {
            //if you exit upwards
            if(_exitDir.y > 0)
            {
                rb.velocity = jumpForce * _exitDir;
            }
    
            //horizontal exiting
            if(_exitDir.x != 0)
            {
                xAxis = _exitDir.x > 0 ? 1 : -1;
    
                Move();
            }
    
            Flip();
            yield return new WaitForSeconds(_delay);
            pState.cutscene = false;
        }
    
    
        //create hitboxes for attacks
        void Attack()
        {
            timeSinceAttack += Time.deltaTime;
            if(attack && timeSinceAttack >= timeBetweenAttack)
            {
    
                timeSinceAttack = 0;
    
    
                //side attack
                if (yAxis == 0 || yAxis < 0 && Grounded())
                {
                    Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingx, recoilXSpeed);
                    Instantiate(slashEffect, SideAttackTransform);
                    anim.SetTrigger("Attacking");
    
                }
    
                //up air attack
                else if (yAxis > 0 && !Grounded())
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingy, recoilYSpeed);
                    Instantiate(upSlashEffect, UpAttackTransform);
                    anim.SetTrigger("Attacking");
                }
    
                //down air attak
                else if (yAxis < 0 && !Grounded())
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingy, recoilYSpeed);
                    Instantiate(downSlashEffect, DownAttackTransform);
                    anim.SetTrigger("Attacking");
                }
    
                if (yAxis > 0 && Grounded())
                {
                    Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingy, recoilYSpeed);
                    Instantiate(upSlashEffect, UpAttackTransform);
                    anim.SetTrigger("upAttack");
                }
            }
    
        }
    
    
    
        //hitting the enemy and adding recoil to attacks
        void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength)
        {
            Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
    
            if(objectsToHit.Length > 0)
            {
                _recoilDir = true;  
            }
    
            for(int i = 0; i < objectsToHit.Length; i++)
            {
                if (objectsToHit[i].GetComponent<Enemy>() != null)
                {
                    objectsToHit[i].GetComponent<Enemy>().EnemyHit
                        (damage, (transform.position - objectsToHit[i].transform.position).normalized, _recoilStrength);
    
                    if (objectsToHit[i].CompareTag("Enemy"))
                    {
                        Mana += manaGain;
                    }
                }
            }
        }
    
    
    
        //changes the direction of attack based on aerial positions
        void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform)
        {
            _slashEffect = Instantiate(_slashEffect, _attackTransform);
            _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
    
        void UpSlashEffectAtAngle(GameObject _upSlashEffect, int _effectAngle, Transform _attackTransform)
        {
            _upSlashEffect = Instantiate(_upSlashEffect, _attackTransform);
            _upSlashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _upSlashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
    
        void DownSlashEffectAtAngle(GameObject _downSlashEffect, int _effectAngle, Transform _attackTransform)
        {
            _downSlashEffect = Instantiate(_downSlashEffect, _attackTransform);
            _downSlashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _downSlashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
    
    
    
        //directional player recoil based on main slash attacks, x and y axis movement
        void Recoil()
        {
            //recoiling in the x axis
            if (pState.recoilingx)
            {
                if (pState.lookingRight)
                {
                    rb.velocity = new Vector2(-recoilXSpeed, 0);
                }
                else
                {
                    rb.velocity = new Vector2(recoilXSpeed, 0);
                }
            }
    
            //recoil in the y axis
            if (pState.recoilingy)
            {
                rb.gravityScale = 0;
                if (yAxis < 0)
                {
                    rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed);
                }
                else
                {
                    rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed);
                }
                airJumpsCounter = 0;
            }
            else
            {
                rb.gravityScale = gravity;
            }
    
            //stop recoiling
    
            if (pState.recoilingx && stepsXRecoiled < recoilXSteps)
            {
                stepsXRecoiled++;
            }
    
            else
            {
                StopRecoilX();
            }
    
            if (pState.recoilingy && stepsYRecoiled < recoilYSteps)
            {
                stepsYRecoiled++;
            }
    
            else
            {
                StopRecoilY();
            }
    
            if (Grounded())
            {
                StopRecoilY();
            }
        }
        
    
        //stop recoiling forever
        void StopRecoilX()
        {
            stepsXRecoiled = 0;
            pState.recoilingx = false;
        }
        void StopRecoilY()
        {
            stepsYRecoiled = 0;
            pState.recoilingy = false;
        }
    
    
    
        //take damage, set health and gives some invinicibility frames
        public void TakeDamage(float _damage)
        {
            Health -= Mathf.RoundToInt(_damage);
            StartCoroutine(StopTakingDamage());
        }
    
        IEnumerator StopTakingDamage()
        {
            pState.invincible = true;
            anim.SetTrigger("takeDamage");
            GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity);
            GameObject _crack = Instantiate(crackeffect, transform.position, Quaternion.identity);
            Destroy(_crack, 0.1f);
            Destroy(_bloodSpurtParticles, 1.5f);
            yield return new WaitForSeconds(1f);
            pState.invincible = false;
        }
    
        //flash black and white when hit for i frames
        void FlashWhileInvincible()
        {
            sr.material.color = pState.invincible ?
                Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.5f)) : Color.white;
        }
    
        void RestoreTimeScale()
        {
            if (restoreTime)
            {
                if (Time.timeScale < 1)
                {
                    Time.timeScale += Time.deltaTime * restoreTimeSpeed;
                }
                else
                {
                    Time.timeScale = 1;
                    restoreTime = false;
                }
            }
        }
    
    
        //create invincibility frames
        public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
        {
            restoreTimeSpeed = _restoreSpeed;
            Time.timeScale = _newTimeScale;
    
            if (_delay > 0)
            {
                StopCoroutine(StartTimeAgain(_delay));
                StartCoroutine(StartTimeAgain(_delay));
            }
            else
            {
                restoreTime = true;
            }
        }
    
    
        //begin taking damage again
        IEnumerator StartTimeAgain(float _delay)
        {
            restoreTime = true;
            yield return new WaitForSeconds(_delay);
        }
    
    
        //health and removing health
        public int Health
        {
            get { return health; }
            set
            {
                if (health != value)
                {
                    health = Mathf.Clamp(value, 0, maxHealth);
    
    
                    if (onHealthChangedCallBack != null)
                    {
                        onHealthChangedCallBack.Invoke();
                    }
                }
            }
        }
    
    
    
        //heal with mana with a
        void Heal()
        {
            if (Input.GetButton("Heal") && Health < maxHealth && Mana > 0 && !pState.jumping && !pState.dashing)
            {
                pState.healing = true;
                anim.SetBool("Healing", true);
    
                //healing
                healTimer += Time.deltaTime;
                if (healTimer >= timeToHeal)
                {
                    Health++;
                    healTimer = 0;
                }
    
                //drain mana
                Mana -= Time.deltaTime * manaDrainSpeed;
            }
            else
            {
                pState.healing = false;
                anim.SetBool("Healing", false);
                healTimer = 0;
            }
        }
    
        //create mana
        float Mana
        {
            get { return mana; }
            set
            {
                //if mana stats change
                if (mana != value)
                {
                    mana = Mathf.Clamp(value, 0, 1);
                    manaStorage.fillAmount = Mana;
                }
            }
        }
    
       
    
        //cast up spell explosion when u have enough mana and its been a long time since your last kaboom
        void CastSpell()
        {
    
            if (Input.GetButtonDown("CastSpell") && timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost && yAxis > 0)
            {
                pState.casting = true;
                timeSinceCast = 0;
                StartCoroutine(CastCoroutine());
            }
            else
            {
                timeSinceCast += Time.deltaTime;
            }
        }
        IEnumerator CastCoroutine()
        {
            anim.SetBool("Casting", true);
            yield return new WaitForSeconds(0.2f);
            //up cast
            if (yAxis > 0)
            {
                Instantiate(upSpellExplosion, transform);
                rb.velocity = Vector2.zero;
            }
    
            Mana -= manaSpellCost;
            yield return new WaitForSeconds(0.2f);
            anim.SetBool("Casting", false);
            pState.casting = false;
        }
    
    
        //checks if you are grounded
        public bool Grounded()
        {
            if(Physics2D.Raycast(groundCheckPoint.position, Vector2.down, groundCheckY, whatIsGround) 
                || Physics2D.Raycast(groundCheckPoint.position + new Vector3(groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround)
                ||Physics2D.Raycast(groundCheckPoint.position + new Vector3(-groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround))
            {
                return true;
            }
            else
            {
                return false;   
            }
        }
    
    
    
        //makes you jump when pressing spacebar
        void Jump()
        {
    
            if (jumpBufferCounter > 0 && coyoteTimeCounter > 0 && !pState.jumping)
            {
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
    
                pState.jumping = true;
            }
            
            if (!Grounded() && airJumpsCounter < maxAirJumps && Input.GetButtonDown("Jump"))
            {
                pState.jumping = true;
    
                airJumpsCounter++;
    
                rb.velocity = new Vector3(rb.velocity.x, jumpForce);
    
            }
    
            if (Input.GetButtonUp("Jump") && rb.velocity.y > 3)
            {
                rb.velocity = new Vector2(rb.velocity.x, 0);
    
                pState.jumping = false;
            }
    
            //animate jumping double jump and falling animation
    
            anim.SetBool("jumping", !Grounded() && rb.velocity.y > -15 && airJumpsCounter == 0);
    
            anim.SetBool("doublejump", !Grounded() && airJumpsCounter == 1);
    
    
            anim.SetBool("falling", !Grounded() && rb.velocity.y < -10);
    
        }
    
    
    
        //coyote time and jump buffering
        void updateJumpVariable()
        {
            //coyote time
            if (Grounded())
            {
                pState.jumping = false;
                coyoteTimeCounter = coyoteTime;
                airJumpsCounter = 0;
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
    
    
            //jump buffering
            if (Input.GetButtonDown("Jump"))
            {
                jumpBufferCounter = jumpBufferFrames;
            }
            else
            {
                jumpBufferCounter--;
            }
        }
    }
    #12531
    Terence
    Keymaster

    For your scene transition, it seems like this is the line that is the problem:

    StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo));

    You can do a debug log of UIManager.Instance and UIManager.Instance.sceneFader to see which is null.

    For your mana issue, check your character’s mana stat on the Inspector when playing. Does it decrease? If it does, then it means the mana stat is not properly linked to the UI on the new scene.

    #12528
    Niyam Shah
    Participant

    just so you know those errors i showed for my scene transition script arent happening anymore – still have a broken mana meter though

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    public class SceneTransition : MonoBehaviour
    {
        [SerializeField] private string transitionTo; //Represents the scene to transition to
    
        [SerializeField] private Transform startPoint; //Defines the player's entry point in the scene
    
        [SerializeField] private Vector2 exitDirection; //Specifies the direction for the player's exit
    
        [SerializeField] private float exitTime; //Determines the time it takes for the player to exit the scene transition
    
        // Start is called before the first frame update
        private void Start()
        {
            if (GameManager.Instance.transitionedFromScene == transitionTo)
            {
                PlayerController.Instance.transform.position = startPoint.position;
    
                StartCoroutine(PlayerController.Instance.WalkIntoNewScene(exitDirection, exitTime));
            }
            StartCoroutine(UIManager.Instance.sceneFader.Fade(SceneFader.FadeDirection.Out));
        }
    
        private void OnTriggerEnter2D(Collider2D _other)
        {
            if (_other.CompareTag("Player"))
            {
                GameManager.Instance.transitionedFromScene = SceneManager.GetActiveScene().name;
    
                SceneManager.LoadScene(transitionTo);
    
                PlayerController.Instance.pState.cutscene = true;
    
                StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo));
            }
        }
    }
Viewing 15 results - 61 through 75 (of 77 total)

Advertisement below: