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

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #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–;
    }
    }
    }

    #13836
    Joseph 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

Viewing 2 posts - 1 through 2 (of 2 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: