Forum begins after the advertisement:
[Part 5 Bug Fixes] SceneFader Error, Scene Transition Issues, Cast after Heal
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 5 Bug Fixes] SceneFader Error, Scene Transition Issues, Cast after Heal
- This topic has 4 replies, 4 voices, and was last updated 2 weeks, 4 days ago by
Mewvern.
-
AuthorPosts
-
May 10, 2024 at 3:17 pm #14602::
This is a supplementary post written for Part 5 of our Metroidvania series, and it aims to address 2 things:
- Missing information in the video, and;
- Address common issues / questions that readers run into, and possible solutions for these questions.
For convenience, below are the links to the article and the video:
- Article Link: https://blog.terresquall.com/2023/06/creating-a-metroidvania-like-hollow-knight-part-5/
- Video Link: https://youtu.be/cL6soESPqOc
Table of Contents
Missing Information in the Video
Common Issues
Missing Information in the Video
1. SceneFader NullReferenceException Error
[Caused by the SceneTransition script firing before the SceneFader is initialised.]
- In SceneFader.cs script, change
Start()
toAwake()
while leaving the codefadeOutUIImage = GetComponent<Image>();
the same.
private void
StartAwake() { fadeOutUIImage = GetComponent<Image>(); }
2. Player hit while in scene transition stops cutscene
[Caused by inefficient code that does not prevent player from taking damage while in cutscene]
- In PlayerController.cs
WalkIntoNewScene()
, setpState.invincibility
to true in the first line, and set to false in the last. - Then, add a line in
FlashWhileInvincible()
to prevent the player from flashing whilepState.cutscene
is true. - Finally, in SceneTransition.cs
OnTriggerEnter2D()
, set the player’spState.invincible
to true before loading scene.
PlayerController.cs
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 FlashWhileInvincible() { if (pState.invincible && !pState.cutscene) { if(Time.timeScale > 0.2 && canFlash) { StartCoroutine(Flash()); } } else { sr.enabled = true; } }
SceneTransition.cs
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)); } }
Common Issues
3. Errors after transitioning scenes.
[There are multiple problems that can arise from this, some listed here, that can be solved by a note found in the Forum Post:]
- 1. Mana isn’t being reset
- 2. Invincibility and time stop is non-functional
- 3. Hearts healed to full
Make sure that there is only one scene containing your Prefabs with singleton scripts such as the Player and Canvas. Entering a scene with a second singleton instance will cause NullReferenceException errors as scripts cannot differentiate or single out one script instance from multiple.
4. Player casts after healing.
[This is addressed in [Part 6] Article of the Metroidvania, but not in the video.]
In our PlayerController Script, let’s make a change to reset the castOrHealTimer in the
CastSpell()
method instead ofGetInputs()
. This relocation ensures that the timer doesn’t reset when you release the Cast/Heal button beforeCastSpell()
is called.PlayerController.cs
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 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 (!Input.GetButton("Cast/Heal")) { castOrHealTimer = 0; } if(Grounded()) { //disable downspell if on the ground downSpellFireball.SetActive(false); } ... }Note: Change the if parameter of both Heal() and CastSpell() to a higher float value if needed as 0.05f is a very limited time for a player to tap and fire a spell.
That will be all for Part 5. Hopefully this can help you on any issues you may have. However, if you find that your issues weren’t addressed or is a unique circumstance, you can submit a forum post to go into detail on your problem for further assistance.
March 11, 2025 at 6:36 am #17502March 11, 2025 at 6:46 am #17503::When a canvas in Unity gets destroyed upon loading a new scene, it usually indicates that the canvas is part of the scene that is being unloaded. Here are a few strategies you can use to prevent your canvas from being destroyed on scene load:
1. Use
DontDestroyOnLoad
If you want your canvas to persist across scene loads, you can use the
DontDestroyOnLoad
function in your script. Here’s how you can do this:- Create a new script (e.g.,
CanvasManager.cs
). - Attach this script to your canvas GameObject.
- In the
Start
method, callDontDestroyOnLoad
.
using UnityEngine; public class CanvasManager : MonoBehaviour { private void Awake() { DontDestroyOnLoad(gameObject); } }
2. Singleton Pattern
If you want to ensure that only one instance of your canvas exists, you can implement a singleton pattern. This can help avoid issues if you accidentally load the scene multiple times.
using UnityEngine; public class CanvasManager : MonoBehaviour { private static CanvasManager instance; private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } }
3. Loading the Canvas in the New Scene
If the canvas is meant to be scene-specific, consider creating its prefab and instantiate it in each scene. You can do this in the
Start
method of a GameManager or similar script.4. Scene Management
Make sure you are not accidentally destroying the canvas through your scene management logic. If you are loading scenes additively or using
SceneManager.LoadScene
, ensure you know how your scenes are set up and how they interact with each other.5. Check Scene Settings
If you’re using Unity’s UI system, ensure that the canvas is set up correctly in the scene hierarchy. Sometimes issues can arise from the way GameObjects are structured or how prefab instances are managed.
Debugging Steps
- Check the Console for any errors or warnings that might indicate why the canvas is being destroyed.
- Ensure that the canvas is not being referenced or destroyed through other scripts unintentionally.
After applying these strategies, test your game again to see if the canvas persists as expected. If you continue to face issues, please share more details about your scene setup and any relevant scripts.
March 11, 2025 at 7:26 am #17504::When I load into other scenes, the “cutscene” bool stays true and never goes back to false
March 11, 2025 at 7:30 am #17505::It sounds like you’re dealing with a boolean variable that controls whether a cutscene is currently active in your game. If the variable stays
true
when you load new scenes, it might be due to how you’re managing the state of that variable across scene transitions. Here are a few steps you can take to troubleshoot and resolve the issue:-
Check where you set the boolean: Ensure that you are setting the “cutscene” boolean back to
false
at the appropriate time. This could be in a script that runs when you enter a new scene or after the cutscene has finished. -
Use a Singleton or Persistent Object: If you want to maintain the state of the “cutscene” boolean across scenes, consider using a singleton (a single instance of a class that persists) or a script that is marked to not be destroyed on load. This way, you can control when the boolean is set to
false
.Here is an example of how you could set up a simple singleton:
public class CutsceneManager : MonoBehaviour { public static CutsceneManager Instance { get; private set; } public bool cutsceneActive = false; private void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } public void StartCutscene() { cutsceneActive = true; } public void EndCutscene() { cutsceneActive = false; } }
-
Resetting the Boolean: If you are using
DontDestroyOnLoad
, make sure that you explicitly reset the boolean when you want to end the cutscene. You can do this in the scene loading logic or in theStart
orAwake
methods of your scene scripts. -
Debugging: Add debug logs to check the state of the boolean when you load a new scene. This will help you to see whether the boolean is being set as expected.
private void Update() { Debug.Log("Cutscene Active: " + CutsceneManager.Instance.cutsceneActive); }
-
Scene Management: If you are using Unity’s scene management, ensure that the logic for setting the
cutscene
boolean is not being skipped or ignored when transitioning to the new scene. -
Check for Errors: Look through the console for any errors or warnings that might indicate what’s going wrong with the scene transition or the boolean management.
If you can provide a bit more detail about how and where you’re managing the “cutscene” variable, I’d be happy to help further!
March 11, 2025 at 4:29 pm #17510March 14, 2025 at 7:46 am #17538 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: