Search Results for 'fader'
-
Search Results
-
This is a supplementary post written for Part 6 of our Metroidvania series, and it aims to address 1 thing:
- Missing information in the video.
For convenience, below are the links to the article and the video:
- Article Link: https://blog.terresquall.com/2023/07/creating-a-metroidvania-like-hollow-knight-part-6/
- Video Link: https://youtu.be/4J1c5LHk2TE
Table of Contents
Missing Information in the Video
- Bat doesn’t stop chasing and gets recoiled infinitely.
- Hit by spikes freezes the game.
- Bat corpse can be attacked and recoils player.
Missing Information in the Video
1. Bat doesn’t stop chasing and gets recoiled infinitely.
[This is caused by missing code that lets the Bat return to an Idle state from Chase state. Furthermore, there is no velocity change code to change the Bat’s recoil velocity after being recoiled.]
- In Bat.cs script, add
if(_dist > chaseDistance) {ChangeState(EnemyStates.Bat_Idle);}
undercase EnemyStates.Bat_Chase:
beforebreak;
. This should change the Bat to an Idle state if the player exits it’s chaseDistance. - Add a code under
case EnemyStates.Bat_Idle
to reset it’s velocity,rb.velocity = new Vector2(0, 0);
. This will let the Bat stabilize its velocity and stop recoiling without needing to be stopped by a collision.
case EnemyStates.Bat_Idle: rb.velocity = new Vector2(0, 0); if(_dist < chaseDistance) { ChangeState(EnemyStates.Bat_Chase); } break; case EnemyStates.Bat_Chase: rb.MovePosition(Vector2.MoveTowards(transform.position, PlayerController.Instance.transform.position, Time.deltaTime * speed)); FlipBat(); if(_dist > chaseDistance) { ChangeState(EnemyStates.Bat_Idle); } break;
2. Hit by spikes freezes the game.
[This is caused by “IEnumerator RespawnPoint()” in Spikes.cs scaling time to 0 and using “WaitForSeconds”]
- Change all “WaitForSeconds” codes in Spikes.cs to “WaitForSecondsRealTime”. This allows the enumerator to continue as intended while in stopped time without freezing the game.
IEnumerator RespawnPoint() { ... yield return new
WaitForSecondsWaitForSecondsRealtime(1f); ... yield return newWaitForSecondsWaitForSecondsRealtime(UIManager.Instance.sceneFader.fadeTime); ... }
3. Bat corpse can be attacked and recoils player.
[There are different ways to solve this bug but the simplest is to change the Bat’s layer to anything other than “Attackable”.]
- First, create a new layer, and call it “Ignore Player” or something identifiable.
- In Bat.cs, under
ChangeCurrentAnimation()
addint LayerIgnorePlayer = LayerMask.NameToLayer("Ignore Player"); gameObject.layer = LayerIgnorePlayer;
to the If statement for the death state: - Go to Edit > Project Settings > Physics2D > Layer Collision Matrix. Here you can set collision based on Layers.
- Then, turn off collision for both “Default”, “Attackable” & “Ignore Player”. This prevents the corpse from blocking enemies, stacking on each other and player’s movement and attacks.
protected override void ChangeCurrentAnimation() { anim.SetBool("Idle", GetCurrentEnemyState == EnemyStates.Bat_Idle); anim.SetBool("Chase", GetCurrentEnemyState == EnemyStates.Bat_Chase); anim.SetBool("Stunned", GetCurrentEnemyState == EnemyStates.Bat_Stunned); if(GetCurrentEnemyState == EnemyStates.Bat_Death) { anim.SetTrigger("Death"); int LayerIgnorePlayer = LayerMask.NameToLayer("Ignore Player"); gameObject.layer = LayerIgnorePlayer; } }
Note: Depending on your game setup, change the Layers to however you feel is needed.
That will be all for Part 6. 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.
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.
Hi, I have a trouble on part 6.
I just followed “Camera Bounds” & “Dynamic Camera Y Damping” and when I try to run the game, it just pauses and there are repetitive error msg on console.
NullReferenceException: Object reference not set to an instance of an object SceneTransition.Start () (at Assets/Scripts/SceneTransition.cs:27)
NullReferenceException: Object reference not set to an instance of an object PlayerController.UpdateCameraYDampForPlayerFall () (at Assets/Scripts/PlayerController.cs:253) PlayerController.Update () (at Assets/Scripts/PlayerController.cs:184)
NullReferenceException: Object reference not set to an instance of an object PlayerController.UpdateCameraYDampForPlayerFall () (at Assets/Scripts/PlayerController.cs:258) PlayerController.Update () (at Assets/Scripts/PlayerController.cs:184)
I don’t know how to deal with “NUllReferenceException” error.
this is my error console img below.
“Scenetransition.cs”
<code>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; PlayerController.Instance.pState.cutscene = true; SceneManager.LoadScene(transitionTo); StartCoroutine(UIManager.Instance.sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, transitionTo)); } } } </code>
“PlayerController.cs”
<code> void Update() { if (pState.cutscene) return; GetInputs(); UpdateJumpVariables(); UpdateCameraYDampForPlayerFall(); RestoreTimeScale(); if (pState.dashing) return; Flip(); Move(); Jump(); StartDash(); Attack(); FlashWhileInvincible(); Heal(); CastSpell(); } void UpdateCameraYDampForPlayerFall() { //if falling past a certain speed threshold if(rb.velocity.y < playerFallSpeedThreshold && !CameraManager.Instance.isLerpingYDamping && !CameraManager.Instance.hasLerpedYDamping) { StartCoroutine(CameraManager.Instance.LerpYDamping(true)); } //if standing still or moving up if(rb.velocity.y >= 0 && !CameraManager.Instance.isLerpingYDamping && CameraManager.Instance.hasLerpedYDamping) { //reset camera function CameraManager.Instance.hasLerpedYDamping = false; StartCoroutine(CameraManager.Instance.LerpYDamping(false)); } } </code>