Forum begins after the advertisement:
[Part 4] Cast and Heal problem
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 4] Cast and Heal problem
- This topic has 26 replies, 3 voices, and was last updated 3 weeks, 4 days ago by pepecry.
-
AuthorPosts
-
September 23, 2024 at 6:12 pm #15904::
Hello everyone. I just finish the skill system and when testing out, I got some problem.
First problem that is after casting the skill, the mana container doesn’t update. I must hit the enemy to update it.
Second problem is when i change the input system like the part 5 to using casting spell and heal to right mouse, event when I not input any direction button, the heal function doesn’t work. When I release the mouse, it auto cast the fireball.
Here is the input systemand here is my healing code
void Heal() { if (Input.GetButtonDown("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { 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; } }
And another problem is the fireball instance kill the enemy althought the enemy hp is higher than the skill damaged
Can you guys help me to fix this?
September 24, 2024 at 12:15 am #15907::Hi, for the Heal issue theres some things that should be checked, as the inputs in your project is not the issue
– InUpdate()
function,if (playerStateList.healing) return;
is not aboveHeal()
– Send in the entire PlayerController.cs script so that we know whether there actually is a problem with his update or not.For the fireball, I’ll suggest also checking this:
– The collision function isOnTriggerEnter2D
, if itsOnTriggerStay2D
, the function may be called repeatedly and one fireball might do damage repeatedly
– send the DarkBullet.cs script for the fireball as well.has upvoted this post. September 24, 2024 at 1:04 am #15908::Here is the player controller
using 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() { getInput(); UpdateJump(); if (playerStateList.dashing) return; Flip(); Moving(); Jump(); StartDash(); Attack(); RestoreTimeScale(); FlashWhenInvi(); Heal(); CastSpell(); } 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); } else { castOrhealTimer = 0; } } private void FixedUpdate() { 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; } } //player Attack void Attack() { timeSinceattk += Time.deltaTime; if (attk && timeSinceattk >= timeBetweenattck) { timeSinceattk = 0; animator.SetTrigger("Attacking"); if (yAxis == 0 || yAxis < 0 && isonGround()) { Hit(sideAttackTransform, sideAttackArea, ref playerStateList.recoilingX, recoilXSpeed); } else if (yAxis > 0) { Hit(upAttackTransform, upAttackArea, ref playerStateList.recoilingY, recoilYSpeed); } else if (yAxis < 0 && !isonGround()) { Hit(downAttackTransform, downAttackArea, ref playerStateList.recoilingY, recoilYSpeed); } } } //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 _recoilDir, float _recoilStrenght) { Collider2D[] objecttoHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); List<Enemy> hitonEnemies = new List<Enemy>(); if (objecttoHit.Length > 0) { _recoilDir = true; } for (int i = 0; i < objecttoHit.Length; i++) { Enemy e = objecttoHit[i].GetComponent<Enemy>(); if (e && !hitonEnemies.Contains(e)) { e.EnemyHit(playerDmg, (transform.position - objecttoHit[i].transform.position).normalized, _recoilStrenght); hitonEnemies.Add(e); 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() { 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) { Health -= Mathf.RoundToInt(_Dmg); 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.GetButtonDown("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { 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; } }
and this is the skill script
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DarkBullet : MonoBehaviour { [SerializeField] float Damaged; [SerializeField] float hitForce; [SerializeField] float Lifetime = 1; [SerializeField] int Speed; // Start is called before the first frame update void Start() { Destroy(gameObject, Lifetime); } private void FixedUpdate() { transform.position += Speed*transform.right; } private void OnTriggerEnter2D(Collider2D _other) { if(_other.tag == "Enemy") { _other.GetComponent<Enemy>().EnemyHit(Damaged,(_other.transform.position - transform.position).normalized, -hitForce); } } }
As you said, should I make the update in the player controller like this?
private void Update() { getInput(); UpdateJump(); if (playerStateList.dashing) return; Flip(); Moving(); Heal(); CastSpell(); if(playerStateList.healing) return; Jump(); StartDash(); Attack(); RestoreTimeScale(); FlashWhenInvi(); }
September 24, 2024 at 1:56 pm #15911::Hi, the code for Heal looks fine to me, it might be the inspector values?
– Check that time to heal is set and Mana to heal is setDo add print codes before and after some of the lines of code to see if they execute during play, if it fires then it works, if the print code after the lines does not fire, it means one of the lines failed. Something like this:
void Heal() { if (Input.GetButtonDown("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { print("Healing"); 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; } }
Do sort that Update() function in that manner as well, but some should be rearranged, like this:
if (pState.dashing) return; RestoreTimeScale(); FlashWhileInvincible(); Move(); Heal(); CastSpell(); if (pState.healing) return; Flip(); Jump(); StartDash(); Attack();
For the fireball script. it looks fine to me, the issue might be either in the enemy script or the inspector components. Do check these things
– Fireball has a trigger collider
– Enemy has the enemy tag
– Do send your enemy script as wellSeptember 24, 2024 at 6:00 pm #15916::So i check and change the code as you said like this
void Heal() { if (Input.GetButtonDown("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { print("Healing"); 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; } }
and the update is
private void Update() { if (playerStateList.dashing) return; RestoreTimeScale(); FlashWhenInvi(); Moving(); Heal(); CastSpell(); if (playerStateList.healing) return; Flip(); Jump(); StartDash(); Attack(); }
My character cannot moving or attack anymore. But i still can cast the spell. Yes it’s spell not healing. the console do not feedback anything. And I still oneshot the enemy
This is my Enemy controllerusing System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy : MonoBehaviour { [SerializeField] protected float health; [SerializeField] protected float recoilLength; [SerializeField] protected float recoilFactor; [SerializeField] protected bool isRecoilling = false; protected float recoinTimer; protected Rigidbody2D rb; [SerializeField] protected Move player; [SerializeField] protected float speed; [SerializeField] protected float dmgMake; // Start is called before the first frame update protected virtual void Start() { rb = GetComponent<Rigidbody2D>(); player = Move.Instance; } // Update is called once per frame protected virtual void Update() { if (health <= 0) { Destroy(gameObject); } if (isRecoilling) { if (recoinTimer < recoilLength) { recoinTimer += Time.deltaTime; } else { isRecoilling = false ; recoinTimer = 0 ; } } } public virtual void EnemyHit(float _DmgDone, Vector2 _hitDirection, float _hitForce) { health -= _DmgDone; if (!isRecoilling) { rb.AddForce(-_hitForce * recoilFactor * _hitDirection); isRecoilling=true ; } } protected void OnTriggerStay2D(Collider2D _other) { if (_other.CompareTag("Player") && !Move.Instance.playerStateList.Invi) { Attack(); Move.Instance.HitStopTime(0, 2, 0.5f); } } protected virtual void Attack() { Move.Instance.takeDmg(dmgMake); } }
For the img about the health and mana drain. Yes i did set the value. Here is it
I has tick the is trigger box
And my enemy has the enemy tag
September 24, 2024 at 8:49 pm #15917::Hi, your
Heal()
method should useGetButton()
instead ofGetButtonDown()
.
When you use onlyGetButtonDown()
, the method is only triggered by you pressing the heal button once.void Heal() { if (
Input.GetButtonDown("Cast/Heal")Input.GetButton("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) {print("Healing");playerStateList.healing = true; animator.SetBool("Healing", true); ... } else { ... } }
For your fireball killing the enemy instantly, in your picture you set your “Damaged” variable to “10”, which is most likely the damage you deal to enemies.
That means that it will deal 10 damage to enemies. So try reducing it to 1 or 5 or any value below the enemies’ health.
As for you not being able to move or attack, could you tell us when it happens? What did you do before the issue occurs?
September 24, 2024 at 9:51 pm #15918::Hello. For the damage of the bullet, i set it at 10 because i set the enemy health is 20 as you can see as the picture below.
and for the bug can not moving. I have no idea why it happen. Maybe because i change the update function like Choloe said of my player controller and change some function as the video part 5 start.September 25, 2024 at 2:06 pm #15920::Hi, here’s a few things i suggest to try out to see what is causing the issue
1. From the update script youve sent, I think GetInputs(); and UpdateJumpVariables(); are not being called in the update, thats why the player might not be moving
2. Put a print in Move() to see if the move function is working correctly
3. Check if Heal() has a code to that could be freezing the player’s rigidbody velocity or transform
4. Put the damage value for the spell at 1, then put a print in your ontriggerenter2d for the fireball, this is to check if the cause of the fireball instakill is due to the fireball constantly colliding and pushing back the enemy
5. After that, send a recording of you trying this out or send screenshots of the console, then we can observe what might be the issueThr full Update function should look like this
void Update() { GetInputs(); UpdateJumpVariables(); if (pState.dashing) return; RestoreTimeScale(); FlashWhileInvincible(); Move(); Heal(); CastSpell(); if (pState.healing) return; Flip(); Jump(); StartDash(); Attack(); }
September 25, 2024 at 3:25 pm #15921::Hi. So after i change the Update function like your recommend, it now work normally. for the healing. Now my function change to getbutton like joseph said. Here is the code
void Heal() { if (Input.GetButton("Cast/Heal") && castOrhealTimer > 0.05f && Health < MaxHealth && Mana > 0 && !playerStateList.jumping && !playerStateList.dashing) { 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; } }
But it still not healing my character even if i hold the button longer.
For the buttet, i put the print at the trigger2d like you saidprivate void OnTriggerEnter2D(Collider2D _other) { if(_other.tag == "Enemy") { print("GetHit"); _other.GetComponent<Enemy>().EnemyHit(Damaged,(_other.transform.position - transform.position).normalized, -hitForce); } }
When the bullet hit the enemy, nothing print out of my console.The enemy is just disapear and i realise that it instance kill the enemy because the dmg is multiply by 2. which mean when i hit the enemy with skill set at 10dmg. it will make 20dmg. and when i set at 1, it just 2 dmg.
I just stop the unity, open it, test it again and now the console is print 2 line of get hit. Which mean the enemy take dame 2 times. I dont now why. Maybe because of the box collider.
And another problem that is not solving that i mentioned it from start of the topic that is the mana container doesn’t update after using skill. It only update after i hit the enemy with my attack. Do you have any idea to fix this?September 25, 2024 at 9:19 pm #15922::Hi, for the mana drain issue, after looking through the playercontroller script you’ve sent, the issue might be calling the wrong mana variable which is being deducted by the mana spell cost
mana -= manaSpellcost;
it should be changed to this instead
Mana -= manaSpellcost;
as Mana is the one that is affecting the fillAmount in the UI
For the healing, test this out first
– When you have a full Mana meter and heal the player, does it still not heal? Looking at the values in your inspector, each successful attack will gain 0.1 mana while a heal will need 0.2 mana
– Add prints to your heal function:
– If your console returns the prints but it’s not restoring health, meaning the if statement has an issue.
– If the console does not print, then there is an issue with calling Heal()For the bullet, possible issues is that
– Theres 2 box colliders in the enemy, the enemy should have 1 trigger box collider and one box collider
– Theres 2 trigger box collider on the fireball
– Try to lower the damage to the enemy to check if its only a double hit
I’ll also suggest that if the porblem persists after that, you can try to use the same piece of code that the playercontroller uses in the hit function to ensure that the enemy will only take one damage at a time, which is in this link https://blog.terresquall.com/community/topic/part-3-missing-video-content-common-issues-fixes/has upvoted this post. September 25, 2024 at 10:29 pm #15924::I change my mana function like u said and it now can update after i using the skill.
And for the healing, i add the print into code like thisplayerStateList.healing = true; animator.SetBool("Healing", true); healTimer += Time.deltaTime; if (healTimer >= timetoHeal) { Health++; print("Healing"); healTimer = 0; }
but nothing print to the console even if i have full mana. it only print the get hit i put into the enemy code because i hold the mouse button and release and it cast the spell
for the enemy box collider. Yes it has 2 box collider but 1 is trigger and 1 is not as you can see the img below
September 25, 2024 at 10:59 pm #15925::Hi, could you do the following for us to assist you further?
IF POSIBLE, do take a video of your player’s inspector, the DarkBullet hitting the enemy, the console and prints, and you healing or any other bugs.
This will help us discern what is the possible issues efficiently.1. Post your Player script, DarkBullet script again.
2. Reduce your DarkBullet Damage to “1” and leave your Enemy at “20” health. Please do this so we can affirm if the enemy is still being killed by it or only being hit two times maximum.
3. Put a print into your
Heal()
method to see at what variables fail to work correctly: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 { print("Stopped Healing"); playerStateList.healing = false; animator.SetBool("Healing", false); healTimer = 0; } }
September 25, 2024 at 11:22 pm #15926::Ok so i recording it to you. Here is the video. Sorry because the imgur only take the video under 1 minute so i need to divide it to 2 videos. And i will also give 1 link and 1 img url of imgur for sure
The first video is change the code of the heal function and check the inspector
Inspector video
the second video is when i test the game
test function
For the scripts. here is player scriptsusing 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() { getInput(); UpdateJump(); if (playerStateList.dashing) return; RestoreTimeScale(); FlashWhenInvi(); Moving(); Heal(); CastSpell(); if (playerStateList.healing) return; 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); } else { castOrhealTimer = 0; } } private void FixedUpdate() { 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()) { Hit(sideAttackTransform, sideAttackArea, ref playerStateList.recoilingX, recoilXSpeed); } else if (yAxis > 0) { Hit(upAttackTransform, upAttackArea, ref playerStateList.recoilingY, recoilYSpeed); } else if (yAxis < 0 && !isonGround()) { Hit(downAttackTransform, downAttackArea, ref playerStateList.recoilingY, recoilYSpeed); } } } //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 _recoilDir, float _recoilStrenght) { Collider2D[] objecttoHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); List<Enemy> hitonEnemies = new List<Enemy>(); if (objecttoHit.Length > 0) { _recoilDir = true; } for (int i = 0; i < objecttoHit.Length; i++) { Enemy e = objecttoHit[i].GetComponent<Enemy>(); if (e && !hitonEnemies.Contains(e)) { e.EnemyHit(playerDmg, (transform.position - objecttoHit[i].transform.position).normalized, _recoilStrenght); hitonEnemies.Add(e); 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) { Health -= Mathf.RoundToInt(_Dmg); 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 { print("Stopped Healing"); 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; } }
and here is dark bullet script
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DarkBullet : MonoBehaviour { [SerializeField] float Damaged; [SerializeField] float hitForce; [SerializeField] float Lifetime = 1; [SerializeField] int Speed; // Start is called before the first frame update void Start() { Destroy(gameObject, Lifetime); } private void FixedUpdate() { transform.position += Speed*transform.right; } private void OnTriggerEnter2D(Collider2D _other) { if(_other.tag == "Enemy") { print("GetHit"); _other.GetComponent<Enemy>().EnemyHit(Damaged,(_other.transform.position - transform.position).normalized, -hitForce); } } }
If you need me to provide anything else. i will give it asap
September 25, 2024 at 11:58 pm #15927::Sorry, But i’m going to need you to try something else then.
1. Delete only the else statement print code since It will keep playing no matter what.
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 {
print("Stopped Healing");playerStateList.healing = false; animator.SetBool("Healing", false); healTimer = 0; } }
2. When you try to Heal, HOLD Down the Cast/Heal button.
From what i’ve seen, you’ve been casting which requires you to tap the button and not healing, either this was intentional to show the cast and not the healing, or you did not heal properly since it’s supposed to be held down instead of tapping.
3. Get rid of this part in your PlayerController.cs of
OnTriggerEnter2D()
, you won’t need to reset the timer here.private void OnTriggerEnter2D(Collider2D _other) { if (_other.GetComponent
() != null && playerStateList.castspell) { _other.GetComponent ().EnemyHit(spellDmg, (_other.transform.position - _other.transform.position).normalized, -recoilYSpeed); } else { castOrhealTimer = 0; }}
4. Make sure you only use
Mana
with a capital M instead ofmana
when referring to the variable.Here’s the only other value in your Player script that refers to it incorrectly.
void CastSpell() { if (Input.GetButtonUp("Cast/Heal") && castOrhealTimer <= 0.05f && timeSincecast >= timeBetweencast &&
manaMana >= 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; } }
5. Don’t worry about the double damage from the DarkBullet, after checking,
Follow Part 6 as that will be when we create enemies with only one boxcollider2D.Note that even though one of the boxes of the Enemy is a Trigger, the
OnTriggerEnter2D()
of the DarkBullet is just checking for BoxCollider2D’s to enter the trigger of the DarkBullet, not the enemy. Therefore both boxes of the Enemy touching the DarkBullet is causing the double damage.
Finally,
Please take another video again after following this, while you have all of these:
1. Console in view
2. Looking at the Players Health Inspector value
3. Holding the Heal button down and not letting it go, while having missing health to be healed and mana to heal.We need to do this to confirm if:
> You are actually healing as your Health will increase in the inspector if you are. Also meaning that your UI is a problem
> Your Heal method is functioning
> Your Heal variables are working as intended.September 26, 2024 at 11:18 pm #15930::Sorry for replying late. I got some stuff to do.
SO step by step. I has do what you said. Delete the print and change the mana to Mana. But there is some misunderstanding here. I holding the button in the test video i send you before. But for sure I will record another video of test after following your guide. My new video has the audio in it. If you want i’ll upload it to my youtube channel for better resolution and updating the link if you want.
test video -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: