Forum begins after the advertisement:
Player do not take damage & make time very slow when jump off
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › Player do not take damage & make time very slow when jump off
- This topic has 7 replies, 2 voices, and was last updated 2 weeks, 3 days ago by Chloe Lim.
-
AuthorPosts
-
October 28, 2024 at 6:30 pm #16198::
Hello guy. So i’m on start of part 7. I was making death function but my player doesn’t take dmg even though the take damage method I just added an if statement like in the video. Otherwise everything else i just copy and paste. When i got hit, the time is freeze which mean i got take dmg but for some reason the health is not minus like in the video
Record of test dmg function and errors
And as the below video, you guyys could see that the game will be very slowly if I got hit by the moster and try to jump up. This is not happend before the death function is added. For the code, here is my player controller codeusing System; using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEditor.Tilemaps; using UnityEngine; using UnityEngine.UI; [RequireComponent(typeof(Rigidbody2D))] public class Move : MonoBehaviour { //animation and physic [Header("physic of player")] private Rigidbody2D rbd2; [SerializeField] public float speed = 2.0f; private float xAxis, yAxis; [SerializeField] private float jumpForce = 10; private int dbJumpCounter; [SerializeField] private int maxdbJump = 1; [Space(5)] [Header("Groundcheck setting")] [SerializeField] private Transform groundcheck; [SerializeField] private float groundchecky = 0.2f; [SerializeField] private float groundcheckx = 0.5f; [SerializeField] private LayerMask isground; Animator animator; [HideInInspector] public PlayerStateList playerStateList; [Space(5)] [Header("Coyotetime setting")] [SerializeField] private float coyoteTime = 0.1f; private float coyoteTimeCounter = 0; [Space(5)] [Header("Attacking setting")] [SerializeField] private float timeBetweenattck; [SerializeField] Transform sideAttackTransform, upAttackTransform, downAttackTransform; [SerializeField] Vector2 sideAttackArea, upAttackArea, downAttackArea; [SerializeField] LayerMask attackableLayer; [SerializeField] float playerDmg; private float timeSinceattk; bool attk = false; [Space(5)] [Header("Health Setting")] [SerializeField] public int health; [SerializeField] 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("Dash setting")] [SerializeField] private float dashSpeed; [SerializeField] private float dashTime; [SerializeField] private float dashCD; private bool canDash = true; private bool dashed; [Space(5)] [Header("Recoil")] [SerializeField] int recoilXSteps = 5; [SerializeField] int recoilYSteps = 5; [SerializeField] float recoilXSpeed = 100; [SerializeField] float recoilYSpeed = 100; int stepXrecoiled, stepYrecoiled; [Space(5)] [Header("Mana Setting")] [SerializeField] float mana; [SerializeField] float manaDrain; [SerializeField] float manaGain; [SerializeField] UnityEngine.UI.Image manaStorage; [Space(5)] [Header("Spellcasting")] [SerializeField] float manaSpellcost = 0.3f; [SerializeField] float timeBetweencast = 0.5f; float timeSincecast; float castOrhealTimer; [SerializeField] float spellDmg; //dark rising and skyfall only [SerializeField] float skyfallForce; // force for skyfall [SerializeField] GameObject sideSpellDarkBall; [SerializeField] GameObject upSpellDarkRising; [SerializeField] GameObject downSpellSkyFall; [Space(5)] bool restoreTime; float restoreTimeSpeed; private SpriteRenderer spriteRenderer; public static Move Instance; private float gravity; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); } // Start is called before the first frame update private void Start() { //player game object playerStateList = GetComponent<PlayerStateList>(); rbd2 = GetComponent<Rigidbody2D>(); animator = GetComponent<Animator>(); spriteRenderer = GetComponent<SpriteRenderer>(); gravity = rbd2.gravityScale; Mana = mana; manaStorage.fillAmount = Mana; Health = MaxHealth; } private void OnDrawGizmos() { Gizmos.color = Color.yellow; Gizmos.DrawWireCube(sideAttackTransform.position, sideAttackArea); Gizmos.DrawWireCube(upAttackTransform.position, upAttackArea); Gizmos.DrawWireCube(downAttackTransform.position, downAttackArea); } // Update is called once per frame private void Update() { if (playerStateList.incutscene) return; if (playerStateList.alive) { getInput(); } UpdateJump(); RestoreTimeScale(); if (playerStateList.dashing || playerStateList.healing) return; FlashWhenInvi(); if (playerStateList.alive) { Moving(); Heal(); CastSpell(); Flip(); Jump(); StartDash(); Attack(); } } private void OnTriggerEnter2D(Collider2D _other) { if (_other.GetComponent<Enemy>() != null && playerStateList.castspell) { _other.GetComponent<Enemy>().EnemyHit(spellDmg, (_other.transform.position - _other.transform.position).normalized, -recoilYSpeed); } } private void FixedUpdate() { if (playerStateList.incutscene) return; if (playerStateList.dashing) return; Recoil(); } void getInput() { xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); attk = Input.GetButtonDown("Attack"); if (Input.GetButton("Cast/Heal")) { castOrhealTimer += Time.deltaTime; } else { castOrhealTimer = 0; } } //player Attack void Attack() { timeSinceattk += Time.deltaTime; if (attk && timeSinceattk >= timeBetweenattck) { timeSinceattk = 0; animator.SetTrigger("Attacking"); if (yAxis == 0 || yAxis < 0 && isonGround()) { int _recoilLeftorRight = playerStateList.lookingRight ? 1 : -1; Hit(sideAttackTransform, sideAttackArea, ref playerStateList.recoilingX, recoilXSpeed, Vector2.right * _recoilLeftorRight); } else if (yAxis > 0) { Hit(upAttackTransform, upAttackArea, ref playerStateList.recoilingY, recoilYSpeed, Vector2.up); } else if (yAxis < 0 && !isonGround()) { Hit(downAttackTransform, downAttackArea, ref playerStateList.recoilingY, recoilYSpeed, Vector2.down); } } } //Using for the character recoil void Recoil() { if (playerStateList.recoilingX) { if (playerStateList.lookingRight) { rbd2.velocity = new Vector2(-recoilXSpeed, 0); } else { rbd2.velocity = new Vector2(recoilXSpeed, 0); } } if (playerStateList.recoilingY) { rbd2.gravityScale = 0; if (yAxis < 0) { rbd2.velocity = new Vector2(rbd2.velocity.x, recoilYSpeed); } else { rbd2.velocity = new Vector2(rbd2.velocity.x, -recoilYSpeed); } dbJumpCounter = 0; } else { rbd2.gravityScale = gravity; } //Stop recoil X if (playerStateList.recoilingX && stepXrecoiled < recoilXSteps) { stepXrecoiled++; } else { StopRecoilX(); } //Stop recoil Y if (playerStateList.recoilingY && stepYrecoiled < recoilYSteps) { stepYrecoiled++; } else { StopRecoilY(); } // if hit ground stop recoil Y if (isonGround()) { StopRecoilY(); } } void StopRecoilX() { stepXrecoiled = 0; playerStateList.recoilingX = false; } void StopRecoilY() { stepYrecoiled = 0; playerStateList.recoilingY = false; } //player hit check void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, float _recoilStrenght, Vector2 _recoilDir) { Collider2D[] objecttoHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); if (objecttoHit.Length > 0) { _recoilBool = true; } for (int i = 0; i < objecttoHit.Length; i++) { if (objecttoHit[i].GetComponent<Enemy>() != null) { objecttoHit[i].GetComponent<Enemy>().EnemyHit(playerDmg, _recoilDir, _recoilStrenght); if (objecttoHit[i].CompareTag("Enemy")) { Mana += manaGain; } } } } //change dir of the player void Flip() { if (xAxis < 0) { transform.localScale = new Vector2(-1, transform.localScale.y); playerStateList.lookingRight = false; } else if (xAxis > 0) { transform.localScale = new Vector2(1, transform.localScale.y); playerStateList.lookingRight = true; } } // start the dash void StartDash() { if (Input.GetButtonDown("Dash") && canDash && !dashed) { StartCoroutine(Dash()); dashed = true; } if (isonGround()) { dashed = false; } } IEnumerator Dash() { canDash = false; playerStateList.dashing = true; animator.SetTrigger("Dashing"); rbd2.gravityScale = 0; int _dir = playerStateList.lookingRight ? 1 : -1; rbd2.velocity = new Vector2(_dir * dashSpeed, 0); yield return new WaitForSeconds(dashTime); rbd2.gravityScale = gravity; playerStateList.dashing = false; yield return new WaitForSeconds(dashCD); canDash = true; } //moving of player private void Moving() { //if(playerStateList.healing) rbd2.velocity = new Vector2(0,0); rbd2.velocity = new Vector2(speed * xAxis, rbd2.velocity.y); animator.SetBool("Walking", rbd2.velocity.x != 0 && isonGround()); } //check if player is on the ground or not public bool isonGround() { if (Physics2D.Raycast(groundcheck.position, Vector2.down, groundchecky, isground) || Physics2D.Raycast(groundcheck.position + new Vector3(groundcheckx, 0, 0), Vector2.down, groundchecky, isground) || Physics2D.Raycast(groundcheck.position + new Vector3(-groundcheckx, 0, 0), Vector2.down, groundchecky, isground) ) { return true; } else { return false; } } //player jump void Jump() { if (Input.GetButtonDown("Jump") && coyoteTimeCounter > 0) { rbd2.velocity = new Vector3(rbd2.velocity.x, jumpForce); /* playerStateList.Jumping = true;*/ } else if (!isonGround() && dbJumpCounter < maxdbJump && Input.GetButtonDown("Jump")) { dbJumpCounter++; rbd2.velocity = new Vector3(rbd2.velocity.x, jumpForce); } animator.SetBool("Jumping", !isonGround()); } void UpdateJump() { if (isonGround()) { /* playerStateList.Jumping = false;*/ coyoteTimeCounter = coyoteTime; dbJumpCounter = 0; } else { coyoteTimeCounter -= Time.deltaTime; } } //stop taking dmg when player get hit IEnumerator StopTakingDmg() { playerStateList.Invi = true; GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity); Destroy(_bloodSpurtParticles, 1f); animator.SetTrigger("TakeDmg"); yield return new WaitForSeconds(1f); playerStateList.Invi = false; } //player health calculated public int Health { get { return health; } set { if (health != value) { health = Mathf.Clamp(value, 0, MaxHealth); if (onHealthChangedCallback != null) { onHealthChangedCallback.Invoke(); } } } } float Mana { get { return mana; } set { if (mana != value) { mana = Mathf.Clamp(value, 0, 1); manaStorage.fillAmount = Mana; } } } //take dmg function public void takeDmg(float _Dmg) { if (!playerStateList.alive) { Health -= Mathf.RoundToInt(_Dmg); if(Health <= 0) { Health = 0; StartCoroutine(Death()); } else { StartCoroutine(StopTakingDmg()); } } } //time delay for player when get hit public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay) { restoreTimeSpeed = _restoreSpeed; Time.timeScale = _newTimeScale; if (_delay > 0) { StopCoroutine(TimeStartAgain(_delay)); StartCoroutine(TimeStartAgain(_delay)); } else { restoreTime = true; } } void RestoreTimeScale() { if (restoreTime) { if (Time.timeScale < 1) { Time.timeScale += Time.unscaledDeltaTime * restoreTimeSpeed; } else { Time.timeScale = 1; restoreTime = false; } } } IEnumerator TimeStartAgain(float _delay) { yield return new WaitForSecondsRealtime(_delay); restoreTime = true; } void FlashWhenInvi() { spriteRenderer.material.color = playerStateList.Invi ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashspeed, 0.5f)) : Color.white; } void Heal() { if (Input.GetButton("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { print("Healing: " + playerStateList.healing + ", Timer:" + healTimer + ", Mana:" + Mana); playerStateList.healing = true; animator.SetBool("Healing", true); healTimer += Time.deltaTime; if (healTimer >= timetoHeal) { Health++; healTimer = 0; } //using mana to heal Mana -= Time.deltaTime * manaDrain; } else { playerStateList.healing = false; animator.SetBool("Healing", false); healTimer = 0; } } void CastSpell() { if (Input.GetButtonUp("Cast/Heal") && castOrhealTimer <= 0.05f && timeSincecast >= timeBetweencast && Mana >= manaSpellcost) { playerStateList.castspell = true; timeSincecast = 0; StartCoroutine(CastCoroutine()); } else { timeSincecast += Time.deltaTime; } if (isonGround()) { //cannot using skyfall if on ground downSpellSkyFall.SetActive(false); } //force the player to fall down if cast skyfall if (downSpellSkyFall.activeInHierarchy) { rbd2.velocity += skyfallForce * Vector2.down; } } IEnumerator CastCoroutine() { animator.SetBool("Casting", true); yield return new WaitForSeconds(0.12f); //sidecast/darkball if(yAxis == 0 || (yAxis < 0 && isonGround())) { GameObject _darkball = Instantiate(sideSpellDarkBall, sideAttackTransform.position, Quaternion.identity); //flip skill if (playerStateList.lookingRight) { _darkball.transform.eulerAngles = Vector3.zero; // ban spell nhu bth } else { _darkball.transform.eulerAngles = new Vector2(_darkball.transform.eulerAngles.x, 100); } playerStateList.recoilingX = true; } //upcast/Dark Rising else if(yAxis > 0) { Instantiate(upSpellDarkRising, transform); rbd2.velocity = Vector2.zero; } //downcasr/ Sky Fall else if (yAxis < 0 && !isonGround()) { downSpellSkyFall.SetActive(true); } Mana -= manaSpellcost; yield return new WaitForSeconds(0.35f); animator.SetBool("Casting", false); playerStateList.castspell = false; } public IEnumerator WalktonewScene(Vector2 _exitDir, float _delay) { if(_exitDir.y > 0) { rbd2.velocity = jumpForce * _exitDir; } if (_exitDir.x != 0) { xAxis = _exitDir.x > 0 ? 1 : -1; Moving(); } Flip(); yield return new WaitForSeconds(_delay); playerStateList.incutscene = false; } IEnumerator Death() { playerStateList.alive = false; Time.timeScale = 1f; animator.SetTrigger("Death"); yield return new WaitForSeconds(0.9f); StartCoroutine(UIManager.Instance.ActiveDeathScreen()); } }
Here is the UI Manager script
using System.Collections; using System.Collections.Generic; using UnityEngine; public class UIManager : MonoBehaviour { // Start is called before the first frame update public static UIManager Instance; [SerializeField] GameObject deathSceen; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); } public SceneFading sceneFading; public void Start() { sceneFading = GetComponentInChildren<SceneFading>(); } public IEnumerator ActiveDeathScreen() { yield return new WaitForSeconds(0.8f); StartCoroutine(sceneFading.Fade(SceneFading.fadeDirection.In)); yield return new WaitForSeconds(0.9f); deathSceen.SetActive(true); } }
The only path I change in the enemy script is here
protected void OnCollisionStay2D(Collision2D _other) { if (_other.gameObject.CompareTag("Player") && !Move.Instance.playerStateList.Invi && !Move.Instance.playerStateList.Invi && health > 0) { Attack(); if (Move.Instance.playerStateList.alive) { Move.Instance.HitStopTime(0, 2, 0.5f); } } }
Tks you guys.
October 29, 2024 at 9:42 am #16201October 29, 2024 at 6:20 pm #16202::Ok it work but i found 2 other problem.
The first error is happend with the input (again) that when i change the update to match with the video liek thisprivate void Update() { if (playerStateList.incutscene) return; if (playerStateList.alive) { getInput(); } UpdateJump(); RestoreTimeScale(); if (playerStateList.dashing || playerStateList.healing) return; FlashWhenInvi(); if (playerStateList.alive) { Moving(); Heal(); CastSpell(); Flip(); Jump(); StartDash(); Attack(); } }
When i hold to heal, it stuck at the heal status but the heal doesn’t grow up. And you can’t moving or doing anything. Just stuck at healing forever without any health get. The Mana didn’t minus to heal the player.
The second problem with the player is when player death but the enemy is to close, the player got push very far and there is 2 possibilities will happen. The first one is player got hit very far and the second is slowly go off. It’s kinda hard to explain but you can see in the record below. And the death scene doesn’t work perfectly. Sometime it just a black sceen and sometime it a death scene. Both of that is in the record i show you below
Death scene error record
So how can i make my player stop at where it dead and make the death scene work perfectly everytime. tks you for helping me.October 29, 2024 at 8:07 pm #16203::Heal() and cast() needs to be above the order of
if(pState.healing) return
, it should generally be like thisvoid Update() { if (playerStateList.incutscene) return; if(playerStateList.alive) { GetInputs(); } UpdateJump(); RestoreTimeScale(); if (playerStateList.dashing) return; FlashWhileInvi(); if (!playerStateList.alive) return; Move(); Heal(); CastSpell(); if (playerStateList.healing) return; Flip(); Jump(); StartDash(); Attack(); }
October 30, 2024 at 6:11 pm #16208::Tks. The heal work again but do you have any idea to fix the death scene? It got kinda random. Sometime it like the record I sent you before. Sometime it could look like this
And any idea to fix the player so he would stand still but not being floating when death?October 31, 2024 at 12:12 pm #16210::Try to add
rb.constraints = RigidbodyContraints2D.FreezePosition
to your player death function
for the gameover screen, ensure that the gameove text and button is below in the hiearchy of the scene fader, like thisOctober 31, 2024 at 4:33 pm #16214::Ok so it work to fix both the problem but then I figure out that the respawn just don’t work. I go to the altar and spam the f key alot but when i died and click respawn button. It just spawn me in the middle of the room and my character freeze
Respawn codepublic void respawnPlayer() { if(alter != null) { if (alter.Interacted) { respawnpoint = alter.transform.position; } else { respawnpoint = platformRespawnpoint; } } else { respawnpoint = platformRespawnpoint; } Move.Instance.transform.position = respawnpoint; StartCoroutine(UIManager.Instance.DeactiveDeathScreen()); Move.Instance.Respawn(); }
Alter code
private void OnTriggerStay2D(Collider2D _collision) { if (_collision.CompareTag("Player") && Input.GetButtonDown("Interact")) { Interacted = true; } }
Respawn freeze video
As you can see in the video. After get respawn i’m stuck and can’t move anywhere. Abd also sometime I respawn at the alter and sometime even if I interact with the alter. It still repsawn me in middle of the room like the img. but we could just skip the respawn point for now. How should I fix the free after respawn? thank youOctober 31, 2024 at 8:08 pm #16215::Ok, i forgot to add that since the rb constraints are frozen when the player dies, you definitely need to remove the constraints when the player respawns
rb.constraints = RigidbodyConstraints2D.None; rb.constraints = RigidbodyConstraints2D.FreezeRotation;
put this code in the respawnplayerfunction
Also for the altar, put the code in theUpdate()
function instead of theOnTriggerStay2D()
function, i think it might help -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: