Forum begins after the advertisement:


[Part 3] My player knockback wont trigger when i hit the enemy.

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity [Part 3] My player knockback wont trigger when i hit the enemy.

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #19259
    Toonblader
    Level 1
    Participant
    Helpful?
    Up
    0
    ::
    using System.Collections;
    using Unity.VisualScripting;
    using UnityEngine;
    
    public class PlayerController : MonoBehaviour
    {
        // Start is called once before the first execution of Update after the MonoBehaviour is created
    
        [Header("Horizontal Movement Settings")]
        private Rigidbody2D rb;
        [SerializeField] private float walkspeed = 1;
        [Space(5)]
    
    
        [Header("Vertical Movement Settings")]
        [SerializeField] private float jumpForce = 45;
        private float jumpBufferCounter = 0;
        [SerializeField] private float jumpBufferFrames;
        private float coyoteTimeCounter = 0;
        [SerializeField] private float coyoteTime;
        [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 Settingss")]
        bool attack = false;
        [SerializeField] private float timeBetweenAttack; 
        [SerializeField] float timeSinceAttack;
        [SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform;
        [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea;
        [SerializeField] LayerMask attackableLayer;
        [SerializeField] float damage;
        [SerializeField] GameObject slashEffect;
        [Space(5)]
    
        [Header("Recoil Settings:")]
        [SerializeField] private int recoilXSteps = 5; //how many FixedUpdates() the player recoils horizontally for
        [SerializeField] private int recoilYSteps = 5; //how many FixedUpdates() the player recoils vertically for 
        [SerializeField] private float recoilXSpeed = 100; //the speed of horizontal recoil
        [SerializeField] private float recoilYSpeed = 100; //the speed of vertical recoil
        private int stepsXRecoiled, stepsYRecoiled; //the no. of steps recoiled horizontally and verticall 
        [Space(5)]
    
        PlayerStateList pstate;
        private float gravity;
        Animator anim;
        private bool canDash = true;
        private bool dashed;
        private float xAxis, yAxis;
    
    
    
    
        public static PlayerController Instance;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject);
            }
            else
            {
                Instance = this;
            }
        }
    
    
        void Start()
        {
            pstate = GetComponent<PlayerStateList>();
            rb = GetComponent<Rigidbody2D>();
    
            anim = GetComponent<Animator>();
    
            gravity = rb.gravityScale;
    
        }
    
        // Update is called once per frame
        void Update()
        {
            GetInputs();
            UpdateJumpVariables();
    
            if (pstate.dashing) return;
            Flip();
            Move();
            Jump();
            StartDash();
            Attack();
            Recoil();
        }
    
    
        void GetInputs()
        {
    
            xAxis = Input.GetAxisRaw("Horizontal");
            yAxis = Input.GetAxisRaw("Vertical");
            attack = Input.GetMouseButtonDown(0);
        }
    
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea);
            Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea);
            Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea);
    
        }
    
        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.linearVelocity = new Vector2(walkspeed * xAxis, rb.linearVelocity.y);
            anim.SetBool("Walking", rb.linearVelocity.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;
            rb.linearVelocity = new Vector2(transform.localScale.x * 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, 80, UpAttackTransform);
                }
                else if (yAxis < 0 && !Grounded())
                {
                    Hit(DownAttackTransform, DownAttackArea, ref pstate.recoilingY, recoilYSpeed);
                    SlashEffectAtAngle(slashEffect, -90, DownAttackTransform);
                }
            }
        }
    
        private 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);
                }
            }
        }
    
        void Recoil()
        {
            if (pstate.recoilingX)
            {
                if (pstate.lookingRight)
                {
                    rb.linearVelocity = new Vector2(-recoilXSpeed, 0);
                }
                else
                {
                    rb.linearVelocity = new Vector2(recoilXSpeed, 0);
                }
            }
            if (pstate.recoilingY)
            {
                if(yAxis < 0)
                {
                    rb.gravityScale = 0;
                    rb.linearVelocity = new Vector2(rb.linearVelocity.x, recoilYSpeed);
                }
                else
                {
                    rb.linearVelocity = new Vector2(rb.linearVelocity.x, -recoilYSpeed);
                }
            }
            else
            {
                rb.gravityScale = gravity;
            }
    
            //stop recoil
            if(pstate.recoilingX && stepsXRecoiled < recoilXSteps)
            {
                stepsXRecoiled++;
            }
            {
                stopRecoilX();
            }
            if (pstate.recoilingY && stepsYRecoiled < recoilYSteps)
            {
                stepsYRecoiled++;
            }
            {
                stopRecoilY();
            }
    
            if (Grounded())
            {
                stopRecoilY();
            }
        }   
    
        void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform)
        {
            _slashEffect = Instantiate(_slashEffect, _attackTransform);
            _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle);
            _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y);
        }
    
        void stopRecoilX()
        {
            stepsXRecoiled = 0;
            pstate.recoilingX = false;
        }
    
        void stopRecoilY()
        {
            stepsYRecoiled = 0;
            pstate.recoilingY = 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()
        {
            {
                // 1. CUTTING THE JUMP
                if (Input.GetButtonUp("Jump") && rb.linearVelocity.y > 0)
                {
                   rb.linearVelocity = new Vector2(rb.linearVelocity.x, 0);
                   pstate.jumping = false;
                }
    
                // 2. TRIGGERING THE JUMP
                if (!pstate.jumping)
                {
                    if (jumpBufferCounter > 0 && coyoteTimeCounter > 0)
                    {
                        rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
                        pstate.jumping = true;
    
                        // CRITICAL: Reset these counters so the jump doesn't loop
                        jumpBufferCounter = 0;
                        coyoteTimeCounter = 0;
                    }
                }
    
                anim.SetBool("Jumping", !Grounded());
            }
        }
    
        void UpdateJumpVariables()
        {
            if (Grounded())
            {
                pstate.jumping = false;
                coyoteTimeCounter = coyoteTime;
            }
            else
            {
                coyoteTimeCounter -= Time.deltaTime;
            }
    
            if (Input.GetButtonDown("Jump"))
            {
                jumpBufferCounter = jumpBufferFrames;
    
            }
            else
            {
                jumpBufferCounter = jumpBufferCounter - Time.deltaTime * 10;
            }
        }
    }
    #19260
    Ser Apustaja
    Level 6
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    It looks like you’re working on a player controller script for a 2D character in Unity. The script is quite comprehensive and covers movement, jumping, dashing, attacking, and recoil mechanics.

    Here are a few suggestions to fix the issues and improve your code:

    1. Resetting Recoil Steps: In your Recoil() method, you’re stopping recoil immediately after incrementing the steps. This means the recoil might not behave as expected. You should only stop the recoil if the steps have reached the max limit:

       if (pstate.recoilingX && stepsXRecoiled = recoilXSteps)
       {
           stopRecoilX();
       }
      
       if (pstate.recoilingY && stepsYRecoiled = recoilYSteps)
       {
           stopRecoilY();
       }
    2. Ground Check Logic: Your ground check logic looks good, but ensure that the groundCheckPoint is positioned correctly in the Unity editor to properly detect ground.

    3. Check for Null References: In your Start() method, ensure that pstate, rb, and anim are not null after calling GetComponent(). This can help you avoid runtime errors if any of these components are missing.

    4. Dash Logic: Make sure that the dash effect (dashEffect) is correctly set in the Unity editor, or else it will not instantiate.

    5. Animation States: Ensure that you have the appropriate animator states set up in Unity to match the triggers you are calling in your code (like “Dashing”, “Attacking”, “Jumping”, etc.).

    6. Testing: After making the changes, thoroughly test each functionality (movement, jumping, dashing, attacking, recoil) to ensure they work as intended.

    Here’s a small snippet with some of the discussed changes applied:

    void Start()
    {
        pstate = GetComponent();
        rb = GetComponent();
        anim = GetComponent();
        gravity = rb.gravityScale;
    
        // Optional: Check for null references
        if (pstate == null || rb == null || anim == null)
        {
            Debug.LogError("Missing required components!");
        }
    }

    Make these adjustments, and your script should run more smoothly. If you encounter specific errors or issues after these changes, feel free to ask for further assistance!

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: