Forum begins after the advertisement:
Scene Transitions Not Working
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › Scene Transitions Not Working
- This topic has 14 replies, 4 voices, and was last updated 1 month, 1 week ago by Ferral Child.
-
AuthorPosts
-
November 6, 2024 at 1:13 pm #16264::
Hello,
I am having issues with getting my scene transition to work. As no matter how many times I run into what should be the transition ‘doorway’ to the next scene, nothing happens at all. Any advice would be greatly appreciated!
Code Below:
Player Controller
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class playerController : MonoBehaviour { [SerializeField] private float walkspeed = 1; [SerializeField] private float JumpForce = 45; private int JumpBufferCounter = 0; [SerializeField] private int JumpBufferFrames; private float CoyoteTimeCounter = 0; [SerializeField] private float CoyoteTime; private int AirJumpCounter = 0; [SerializeField] private int maxAirJumps; //Ground Check Settings [SerializeField] private Transform GroundCheckPoint; [SerializeField] private float GroundCheckY = 0.2f; [SerializeField] private float GroundCheckX = 0.5f; [SerializeField] private LayerMask WhatIsGround; //Dash Settings [SerializeField] private float dashSpeed; [SerializeField] private float dashTime; [SerializeField] private float dashCooldown; //Attack Settings [SerializeField] private Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; [SerializeField] private Vector2 SideAttackArea, UpAttackArea, DownAttackArea; [SerializeField] private LayerMask AttackableLayer; [SerializeField] private float timeBetweenAttack; [SerializeField] private float damage; private int stepsXRecoiled, stepsYRecoiled; // Recoil Settings [SerializeField] private int recoilXSteps = 5; [SerializeField] private int recoilYSteps = 5; [SerializeField] private float recoilXSpeed = 100; [SerializeField] private float recoilYSpeed = 100; //Health Settings public int health; public int maxHealth; [SerializeField] GameObject DamagePart; [SerializeField] float hitFlashSpeed; [HideInInspector] public PlayerStateList pState; public Rigidbody2D rb; private Animator anim; private SpriteRenderer sr; private float xAxis, yAxis; private float gravity; private bool canDash = true; private bool dashed; private bool attack = false; private float timeSinceAttack; bool restoreTime; float restoreTimeSpeed; public static playerController Instance; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } Health = maxHealth; } // Start is called before the first frame update void Start() { pState = GetComponent<PlayerStateList>(); rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); gravity = rb.gravityScale; sr = GetComponent<SpriteRenderer>(); } 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; GetInputs(); RestoreTimeScale(); UpdateJumpVariables(); if (pState.Dashing) return; Flip(); Move(); Jump(); StartDash(); Attack(); Recoil(); FlashWhileInvincible(); } private void FixedUpdate() { if (pState.Dashing || pState.cutscene) return; Recoil(); } void GetInputs() { xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); attack = Input.GetButtonDown("attack"); } void Flip() { if (xAxis < 0) { transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), transform.localScale.y); pState.lookingRight = false; } else if (xAxis > 0) { transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), 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; rb.velocity = new Vector2(transform.localScale.x * dashSpeed, 0); 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) { pState.invincible = true; //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.invincible = false; pState.cutscene = false; } void Attack() { timeSinceAttack += Time.deltaTime; if(attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attacking"); if(yAxis == 0 || yAxis < 0 && Grounded ()) { int _recoilLeftorRight = pState.lookingRight ? 1 : -1; Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, Vector2.right * _recoilLeftorRight, recoilXSpeed); } else if(yAxis > 0) { Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, Vector2.up, recoilYSpeed); } else if (yAxis < 0 && !Grounded()) { Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, Vector2.down, recoilYSpeed); } } } void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, Vector2 _recoilDir, float _recoilStrength) { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, AttackableLayer); List<Enemy> hitEnemies = new List<Enemy>(); if (objectsToHit.Length > 0) { _recoilBool = true; } for (int i = 0; i < objectsToHit.Length; i++) { Debug.Log("Hit"); Enemy e = objectsToHit[i].GetComponent<Enemy>(); if (objectsToHit[i].GetComponent<Enemy>() != null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir, _recoilStrength); } } } 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) { Health -= Mathf.RoundToInt(_damage); StartCoroutine(StopTakingDamage()); } IEnumerator StopTakingDamage() { pState.invincible = true; GameObject _DamagePartParticles = Instantiate(DamagePart, transform.position, Quaternion.identity); Destroy(_DamagePartParticles, 1.5f); anim.SetTrigger("TakeDamage"); yield return new WaitForSeconds(1f); pState.invincible = false; } void FlashWhileInvincible() { if (pState.cutscene) return; sr.material.color = pState.invincible ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.0f)) : Color.white; } void RestoreTimeScale() { if (restoreTime) { if (Time.timeScale < 1) { Time.timeScale += Time.unscaledDeltaTime * 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) { yield return new WaitForSecondsRealtime(_delay); restoreTime = true; } public int Health { get { return health; } set { if (health != value) { health = Mathf.Clamp(value, 0, maxHealth); } } } 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(Input.GetButtonUp("Jump") && rb.velocity.y > 0) { rb.velocity = new Vector2(rb.velocity.x, 0); pState.jumping = false; } anim.SetBool("Jumping", !Grounded()); if (!pState.jumping) { if (JumpBufferCounter >0 && CoyoteTimeCounter > 0) { rb.velocity = new Vector3(rb.velocity.x, JumpForce); pState.jumping = true; } else if(!Grounded() && AirJumpCounter < maxAirJumps && Input.GetButtonDown("Jump")) { pState.jumping = true; AirJumpCounter++; rb.velocity = new Vector3(rb.velocity.x, JumpForce); } } } void UpdateJumpVariables() { if (Grounded()) { pState.jumping = false; CoyoteTimeCounter = CoyoteTime; AirJumpCounter = 0; } else { CoyoteTimeCounter -= Time.deltaTime; } if (Input.GetButtonDown("Jump")) { JumpBufferCounter = JumpBufferFrames; } else { JumpBufferCounter--; } } }</code>
Game Manager:
<code>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); } } </code>
Scene Fader
<code>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); } } </code>
Scene Transition:
<code> 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 (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; playerController.Instance.pState.cutscene = true; playerController.Instance.pState.invincible = true; StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo)); } } } </code>
UI Manager
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class UIManager : MonoBehaviour { public SceneFader sceneFader; public static UIManager Instance; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); sceneFader = GetComponentInChildren<SceneFader>(); } } </code>
November 6, 2024 at 10:09 pm #16266November 7, 2024 at 2:43 am #16267November 7, 2024 at 11:20 am #16268::Ok, is your scenefader ui disabled in the hiearchy? need to enable it or there will be a null exception as the script cant find the inactive scenefader When your scene starts, there should be a fade out, then when moving to another scene it will fade in then out again to the new scene
has upvoted this post. November 8, 2024 at 5:57 am #16269::This may sound dumb, but how do I check to make sure something is enabled in the hierarchy? I tried clicking the crossed out eye with a dot and nothing is happening.
November 8, 2024 at 7:23 am #16271November 8, 2024 at 12:11 pm #16272::I checked- Scene Transition, Start Point, UI Manager, Canvas, and Scene Fader are all enabled.
November 8, 2024 at 2:54 pm #16273::Ok, in your recording it seems that your scenetransition script is failing to start the coroutine of the scene fading in the at start, which means that the UIManager is unable to find the scenefader to start the fade sequence, is the scenefader game object assigned in the inspector of your UIManager Script?
November 8, 2024 at 3:12 pm #16274::It was not… I added it and I didn’t get the massive list of errors when starting up the playtest mode anymore. However, when I went into the “loading box” I got the error:
NullReferenceException: Object reference not set to an instance of an object SceneTransition.OnTriggerEnter2D (UnityEngine.Collider2D _other) (at Assets/Scripts/SceneTransition.cs:32)
November 8, 2024 at 4:36 pm #16275November 8, 2024 at 4:58 pm #16276November 8, 2024 at 8:30 pm #16277November 9, 2024 at 3:03 am #16280::Okay, I screen recorded and it captured something I didn’t notice is that the UI Manager, Canvas, and Scene Fader disappear when I play the game.
View post on imgur.com
Thank you for being so patient with me ;<;
November 9, 2024 at 4:23 pm #16283::Hi FerralChild, have you seen this post by Joseph? It may be helpful with fixing the NullReferenceExceptions that you have: https://blog.terresquall.com/community/topic/part-5-article-changes-common-issues-bugfixes/
A lot of times, NullReferenceExceptions cause other issues because they block the rest of your function from running. Hence, whenever I have bugs, the first thing I look at are the NullReferenceExceptions.
November 9, 2024 at 4:49 pm #16285 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: