Forum begins after the advertisement:
[Part 5] abilities not working after bug fixes
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 5] abilities not working after bug fixes
- This topic has 1 reply, 2 voices, and was last updated 4 months, 4 weeks ago by
Joseph Tang.
-
AuthorPosts
-
April 11, 2024 at 11:16 pm #13834
Ethan
Participant::been trying to implement the bug fixes and am having issues with my abilities working correctly ive left my code down below so if there are any issues with it you can tell me but at the moment the up and down attacks dont work .
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;public class PlayerController : MonoBehaviour
{
[Header(“Horizontal Movement”)]
[SerializeField] private float walkSpeed = 20;
[Space(5)][Header(“vertical movement”)]
[SerializeField] private float jumpForce = 35f;
private int jumpBufferCounter=0;
[SerializeField] private int jumpBufferFrames;
private float coyoteTimeCounter = 0;
[SerializeField] private float coyoteTime;
private int airJumpCounter = 0;
[SerializeField] private int maxAirJumps;
[Space(5)][Header(“Ground check Settings”)]
[SerializeField] private Transform groundCheckPoint;
[SerializeField] private float groundCheckY = 0.2f;
[SerializeField] private float groundCheckX = 0.5f;
[SerializeField] private LayerMask whatIsGround;
[Space(5)][Header(“dash settings”)]
[SerializeField] private float dashSpeed;
[SerializeField] private float dashTime;
[SerializeField] private float dashCooldown;
[SerializeField] GameObject dashEffect;
[Space(5)][Header(“Attack Settings”)]
bool attack = false;
float timeBetweenAttack, timeSinceAttack;
[SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform;
[SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea;
[SerializeField] LayerMask attackableLayer;
[SerializeField] float damage;
[SerializeField] GameObject slashEffect;
[Space(5)][Header(“Recoil”)]
[SerializeField] int recoilXSteps = 5;
[SerializeField] int recoilYSteps = 5;
[SerializeField] float recoilXSpeed = 100;
[SerializeField] float recoilYSpeed = 100;
int stepsXRecoiled, stepsYRecoiled;
[Space(5)][Header(“Health Settings”)]
public int health;
public int maxHealth;
[SerializeField] GameObject bloodSpurt;
[SerializeField] float hitFlashSpeed;
public delegate void OnHealthChangedDelegate();
[HideInInspector] public OnHealthChangedDelegate OnHealthChangedCallback;
[Space(5)]bool restoreTime;
float restoreTimeSpeed;
[Space(5)][Header(“Healing”)]
float healTimer;
[SerializeField] float timeToHeal;
[Space(5)][Header(“Mana Settings”)]
[SerializeField] Image manaStorage;
[SerializeField] float mana;
[SerializeField] float manaDrainSpeed;
[SerializeField] float manaGain;
[Space(5)][Header(“Spell Casting”)]
//spell stats
[SerializeField] float manaSpellCost = 0.3f;
[SerializeField] float timeBetweenCast = 0.5f;
[SerializeField] float spellDamage;//upspell explosion and downspell fireball
[SerializeField] float downSpellForce; //dive only
//spellcast objects
[SerializeField] GameObject sideSpellFireball;
[SerializeField] GameObject upSpellExplosion;
[SerializeField] GameObject downSpellFireball;
float timeSinceCast;
float castOrHealtimer;
[Space(5)][HideInInspector] public PlayerStateList pState;
private Rigidbody2D rb;
private SpriteRenderer sr;
private float xAxis,yAxis;
private float gravity;
Animator anim;
private bool canDash = true;
private bool dashed;public static PlayerController Instance;
private void Awake()
{
if(Instance != null && Instance != this)
{
Destroy(gameObject);
}
else
{
Instance = this;
}
DontDestroyOnLoad(gameObject);
}// Start is called before the first frame update
void Start()
{
pState = GetComponent<PlayerStateList>();rb = GetComponent<Rigidbody2D>();
sr = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
gravity = rb.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
void Update()
{
GetInputs();
UpdateJumpVariables();
if (pState.dashing) return;
Flip();
Move();
Jump();
StartDash();
Attack();
RestoreTimeScale();
FlashWhileInvincible();
Heal();
CastSpell();
}private void OnTriggerEnter2D(Collider2D _other)
{
if (_other.GetComponent<Enemy>() != null && pState.casting)
{
_other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position – transform.position).normalized, -recoilYSpeed);
}
}private void FixedUpdate()
{
if(pState.dashing) return;
Recoil();
}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 Flip()
{
if(xAxis < 0)
{
transform.localScale = new Vector2(-1, transform.localScale.y);
pState.lookingRight = false;
}
else if(xAxis > 0)
{
transform.localScale = new Vector2(1, 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;
int _dir =pState.lookingRight ? 1 : -1;
rb.velocity = new Vector2(_dir * dashSpeed, 0);
if (Grounded()) Instantiate(dashEffect, transform);
yield return new WaitForSeconds(dashTime);
rb.gravityScale = gravity;
pState.dashing = false;
yield return new WaitForSeconds(dashCooldown);
canDash = true;
}void Attack()
{
timeSinceAttack += Time.deltaTime;
if(attack && timeSinceAttack >= timeBetweenAttack)
{
timeSinceAttack = 0;
anim.SetTrigger(“Attacking”);if(yAxis ==0 || yAxis <0 && Grounded())
{
Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX,recoilXSpeed);
Instantiate(slashEffect, SideAttackTransform);
}
else if(yAxis>0)
{
Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, recoilYSpeed);
SlashEffectAtAngle(slashEffect, 90, UpAttackTransform);
}
else if (yAxis < 0 && !Grounded())
{
Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, recoilYSpeed);
SlashEffectAtAngle(slashEffect,-90,DownAttackTransform);
}
}
}void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength)
{
Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);if(objectsToHit.Length > 0 )
{
_recoilDir = true;
}
for(int i=0; i<objectsToHit.Length; i++)
{
if (objectsToHit[i].GetComponent<Enemy>() != null)
{
objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage,(transform.position- objectsToHit[i].transform.position).normalized,_recoilStrength);if (objectsToHit[i].CompareTag(“Enemy”))
{
Mana += manaGain;
}
}
}
}void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle,Transform _attackTransform)
{
_slashEffect = Instantiate(_slashEffect, _attackTransform);
_slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
_slashEffect.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y);
}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 _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position,Quaternion.identity);
Destroy(_bloodSpurtParticles,1.5f);
anim.SetTrigger(“TakeDamage”);
yield return new WaitForSeconds(1f);
pState.invincible=false;
}
void FlashWhileInvincible()
{
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.deltaTime * restoreTimeSpeed;
}
else
{
Time.timeScale = 1;
restoreTime = false;
}
}
}
public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay)
{
restoreTimeSpeed = _restoreSpeed;
Time.timeScale = _newTimeScale;if(_delay > 0)
{
StopCoroutine(StartTimeAgain(_delay));
StartCoroutine(StartTimeAgain(_delay));
}
else
{
restoreTime = true;
}
}
IEnumerator StartTimeAgain(float _delay)
{
restoreTime = true;
yield return new WaitForSeconds(_delay);
}
public int Health
{
get { return health; }
set
{
if(health != value)
{
health = Mathf.Clamp(value, 0, maxHealth);if(OnHealthChangedCallback != null)
{
OnHealthChangedCallback.Invoke();
}
}
}
}void Heal()
{
if(Input.GetButton(“Cast/Heal”)&& castOrHealtimer > 0.05f && Health < maxHealth && Mana >0 && !pState.jumping && !pState.dashing)
{
pState.healing = true;
anim.SetBool(“Healing”, true);//healing
healTimer += Time.deltaTime;
if(healTimer >= timeToHeal)
{
Health++;
healTimer = 0;
}
//drains mana overtime
Mana -= Time.deltaTime * manaDrainSpeed;
}
else
{
pState.healing= false;
anim.SetBool(“Healing”, false);
healTimer = 0;
}}
float Mana
{
get { return mana; }
set
{
//if mana stats change
if (mana != value)
{
mana = Mathf.Clamp(value, 0, 1);
manaStorage.fillAmount = Mana;
}
}
}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(Grounded())
{
//disable downspell if on the ground
downSpellFireball.SetActive(false);
}
//if down spell is active,force player downwards till it reaches a grounded layer
if(downSpellFireball.activeInHierarchy)
{
rb.velocity += downSpellForce * Vector2.down;
}}
IEnumerator CastCoroutine()
{
anim.SetBool(“Casting”, true);
yield return new WaitForSeconds(0.15f);//side cast
if(yAxis == 0 || (yAxis <0 && Grounded()))
{
GameObject _fireBall= Instantiate(sideSpellFireball, SideAttackTransform.position,Quaternion.identity);//flip fireball
if(pState.lookingRight)
{
_fireBall.transform.eulerAngles = Vector3.zero;
}
else
{
_fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180);//if not looking right rotate the fireball sprite
}
pState.recoilingX = true;
}
//up cast
else if(yAxis >0)
{
Instantiate(upSpellExplosion, transform);
rb.velocity = Vector2.zero;
}
//down cast
else if(yAxis <0 && !Grounded())
{
downSpellFireball.SetActive(true);
}
Mana -= manaSpellCost;
yield return new WaitForSeconds(0.35f);
anim.SetBool(“Casting”, false);
pState.casting = false;
}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 (!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);
}
}
if (Input.GetButtonUp(“Jump”) && rb.velocity.y > 0)
{
rb.velocity = new Vector2(rb.velocity.x, 0);
pState.jumping = false;
}
anim.SetBool(“Jumping”, !Grounded());
}void UpdateJumpVariables()
{
if(Grounded())
{
pState.jumping = false;
coyoteTimeCounter = coyoteTime;
airJumpCounter = 0;
}
else
{coyoteTimeCounter -= Time.deltaTime;
}
if(Input.GetButtonDown(“Jump”))
{
jumpBufferCounter = jumpBufferFrames;
}
else
{
jumpBufferCounter–;
}
}
}April 11, 2024 at 11:54 pm #13836Joseph Tang
Moderator::Sorry, but at a glance, i am unable to find the issue with the code.
Could you send a video [Or screenshots] of:
1) The Player attacking
2) The Scene Console after attacking
3) The Player’s Inspector
4) A quick scroll through your PlayerController.cs code -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: