Forum begins after the advertisement:


Animator/Game freezes when trying to dash towards an enemy

Home Forums Video Game Tutorial Series Creating a Metroidvania in Unity Animator/Game freezes when trying to dash towards an enemy

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #19269
    MPGD
    Level 1
    Participant
    Helpful?
    Up
    0
    ::

    When trying to dash towards an enemy, the game freezes. The unity console didn’t show any error message.

    The animator is pretty much the same at the one in the video. Part 4 of the series

    Animator transitions:

    View post on imgur.com

    Here’s my code for the player script:

    using System.Collections; using System.Collections.Generic; using UnityEngine;

    using UnityEngine.SceneManagement; using UnityEngine.Events;

    public class PlayerController : MonoBehaviour { [Header(“Ground/Animations Configs”)] public float fallMultiplier = 4f; public float lowJumpMultiplier = 3f;

    [Header("Horizontal Movement Settings")]
    [SerializeField] private float walkSpeed = 1;
    [Space(5)]
    
    [SerializeField] private float jumpForce = 6; //Default 6
    private float jumpBufferCounter = 0;
    [SerializeField] private float jumpBufferFrames;
    private float graceTimeCounter = 0;
    [SerializeField] private float graceTime;
    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 option")]
    [SerializeField] private float dashSpeed;
    [SerializeField] private float dashTime;
    [SerializeField] private float dashCooldown;
    [SerializeField] GameObject dashEffect;
    [Space(5)]
    
    
    [Header("Attack settings")]
    private bool attack = false;
    
    private float timeSinceAttack;
    [SerializeField] Transform SideAttackTransform;
    [SerializeField] Vector2 SideAttackArea;
    [SerializeField] Transform UpAttackTransform;
    [SerializeField] Vector2 UpAttackArea;
    [SerializeField] Transform DownAttackTransform;
    [SerializeField] Vector2 DownAttackArea;
    [SerializeField] LayerMask attackableLayer;
    [SerializeField] float damage;
    [SerializeField] private float timeBetweenAttack;
    [SerializeField] GameObject slashEffect;
    [SerializeField] GameObject slashEffectUp;
    [SerializeField] GameObject slashEffectDown;
    
    
    bool restoreTime;
    float restoreTimeSpeed;
    [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;
    [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;
        }
        Health = maxHealth;
    }
    
    void Start()
    {
        pState = GetComponent<PlayerStateList>();
    
        rb = GetComponent<Rigidbody2D>();
        sr = GetComponent<SpriteRenderer>();
    
        anim = GetComponent<Animator>();
    
        gravity = rb.gravityScale;
    }
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        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();
        ApplyBetterJumpPhysics();
    
        StartDash();
        Attack();
        RestoreTimeScale();
        FlashWhileInvincible();
    
    
    //Testing
        //Respawn();
    }
    private void FixedUpdate()
    {
        if (pState.dashing) return;
        Recoil();
    }
    
    void GetInputs()
    {
        xAxis = Input.GetAxisRaw("Horizontal");
        yAxis = Input.GetAxisRaw("Vertical");
        attack = Input.GetButtonDown("Attack");
    
    }
    
    void Flip()
    {
        if (xAxis < 0)
        {
            transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), transform.localScale.y);
            //transform.eulerAngles = new Vector2(0, 180);
            pState.lookingRight = false;
        }
        else if (xAxis > 0)
        {
            transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), transform.localScale.y);
            //transform.eulerAngles = new Vector2(0, 0);
            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, 90, UpAttackTransform);
                Instantiate(slashEffectUp, UpAttackTransform);
            }
            else if (yAxis < 0 && !Grounded())
            {
                Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, recoilYSpeed);
                //SlashEffectAtAngle(slashEffectUp, -90, DownAttackTransform);
                Instantiate(slashEffectDown, DownAttackTransform);
            }
        }
    }
    private void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength)
    {
        Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer);
        List<Enemy> hitEnemies = new List<Enemy>();
    
        if (objectsToHit.Length > 0)
        {
            //Debug.Log("Hit");
            _recoilDir = true;
        }
        for (int i=0; i < objectsToHit.Length; i++)
        {
            Enemy e = objectsToHit[i].GetComponent<Enemy>();
            if (e && !hitEnemies.Contains(e))
            {
                e.EnemyHit(damage, (_attackTransform.position - objectsToHit[i].transform.position).normalized, _recoilStrength);
                hitEnemies.Add(e);
            }
        }
    }
    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 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);
            }
            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, UpAttackTransform.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);
            }
        }
    }
    
    //Placeholder
    //void Respawn()
    //{
    //    if (Health <= 0)
    //    {
    //        UnityEngine.SceneManagement.SceneManager.LoadScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex);
    //    }
    //}
    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 (Input.GetButtonUp("Jump")&& rb.linearVelocity.y > 0) 
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, 0);
    
            pState.jumping = false;
            //pState.falling = false;
        }
        if(!pState.jumping)
        {
            if (jumpBufferCounter > 0 && graceTimeCounter > 0) 
            {
                rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce);
    
                pState.jumping = true;
            }
            else if(!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump"))
            {
                pState.jumping = true;
    
                airJumpCounter++;
    
                rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce);
            }
        }
        anim.SetBool("Jumping", !Grounded());
    
    // Diferenciar entre subida y caída con un umbral
    //*if (!Grounded())
    //{
     //   if (rb.linearVelocity.y < -4f) // <- umbral para evitar caídas muy pequeñas
     //   {
    //        anim.SetBool("Falling", true); // animación de caída
    //    }
    //    else
    //    {
    ///        anim.SetBool("Falling", false); // aún en subida o caída leve
    //    }
    //}
    //else
    //{
    //    anim.SetBool("Jumping", false);
    
    //}

    // }

    void UpdateJumpVariables()
    {
        if (Grounded())
        {
            pState.jumping = false;
            graceTimeCounter = graceTime;
            airJumpCounter = 0;
        }
        else
        {
            graceTimeCounter -= Time.deltaTime;
        }
    
        if (Input.GetButtonDown("Jump"))
        {
            jumpBufferCounter = jumpBufferFrames;
        }
        else
        {
            jumpBufferCounter = jumpBufferCounter - Time.deltaTime * 10;
        }
    }
    
        void ApplyBetterJumpPhysics()
    {
        // Si está cayendo, aplicar multiplicador de caída
        if (rb.linearVelocity.y < 0)
        {
            rb.linearVelocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
        }
        // Si está subiendo pero se soltó el botón de salto, aplicar lowJumpMultiplier
        else if (rb.linearVelocity.y > 0 && !Input.GetButton("Jump"))
        {
            rb.linearVelocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
        }
    }

    }

    #19270
    Ser Apustaja
    Level 6
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    It sounds like you’re experiencing a freeze in your game when you attempt to dash towards an enemy, but there are no error messages in the Unity console. Here are some potential issues and solutions you can look into:

    1. Animator State Issues

    Verify that your animator transitions are correctly set up. If the Dashing trigger is set, but the animator does not have a corresponding transition or the transition conditions are incorrect, it may cause the game to freeze. Ensure that:

    • The Dashing state has an exit transition back to the idle or walking state.
    • The transitions are not set to Has Exit Time unless you want them to wait until the animation finishes.

    2. Coroutine Issues

    Make sure that the Dash coroutine is being correctly executed. If you attempt to start the dash while it is already running, it could cause issues. You can add debug logs to see if the coroutine is being called multiple times unintentionally.

    For example, modify your StartDash method:

    void StartDash()
    {
        if (Input.GetButtonDown("Dash") && canDash && !dashed)
        {
            Debug.Log("Starting Dash");
            StartCoroutine(Dash());
            dashed = true;
        }
    
        if (Grounded())
        {
            dashed = false;
        }
    }

    3. Rigidbody2D Properties

    Check the Rigidbody2D component on your player object:

    • Ensure that Is Kinematic is not checked. A kinematic Rigidbody will not react to forces and could cause unexpected behavior.
    • Check the Gravity Scale. It should be a positive value to ensure your character falls.

    4. Layer Masks and Collision

    Make sure that the whatIsGround layer mask is set up correctly. If the ground check is not functioning properly, it could lead to unexpected behavior when dashing. Check if dashing interacts with any colliders that could potentially cause issues.

    5. Dash Speed and Dash Time

    Make sure that your dashSpeed and dashTime values are reasonable. If dashSpeed is too high, it could cause the player to move through colliders, potentially leading to a freeze if the player continuously collides with the enemy or other objects.

    6. Debugging Collisions

    Add some debug logs to the Dash method to see if the player is colliding with anything during the dash:

    IEnumerator Dash()
    {
        canDash = false;
        pState.dashing = true;
        anim.SetTrigger("Dashing");
        rb.gravityScale = 0;
        rb.velocity = 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;
        Debug.Log("Dash completed");
    }

    7. Check for Infinite Loops

    Make sure that you do not have any infinite loops in your code, especially in the Hit method or during collision checks. If you are spawning enemies or effects during the dash, ensure that their spawning logic does not interact with the player’s dash state in a way that could cause a loop.

    Conclusion

    If you’ve checked all of the above and the issue persists, try to isolate the problem by simplifying your dash logic or temporarily disabling other features to see if the problem goes away. Additionally, using debug logs throughout your code can help you find where the freeze occurs.

    Let me know if you need 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: