c# – Unity 2D Character Animation Stuck While Moving


Why is my character floating in the air instead of standing on the ground in Unity?
I’m working on a Unity project and have implemented a character with a Rigidbody and Collider for physics interactions. However, I noticed that my character is floating or hanging in the air instead of standing properly on the ground.

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour
{
    public float walkSpeed = 5f; // Walking speed
    public float runSpeedMultiplier = 1.5f; // Running speed multiplier
    public float jumpImpulse = 10f; // Jump force

    private Vector2 moveInput; // Movement input
    private Rigidbody2D rb; // Rigidbody2D component
    private Animator animator; // Animator component
    private SpriteRenderer spriteRenderer; // SpriteRenderer component

    public LayerMask groundLayer; // Ground detection layer
    private bool isGrounded = false; // Is the player grounded?

    public float CurrentMoveSpeed => IsRunning ? walkSpeed * runSpeedMultiplier : walkSpeed; // Current movement speed
    private bool IsRunning => Keyboard.current != null && Keyboard.current.shiftKey.isPressed; // Check if Shift is pressed

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    private void Update()
    {
        CheckGrounded(); // Update grounded state
        UpdateAnimations(); // Update animations
        UpdateSpriteDirection(); // Update sprite direction based on movement
    }

    private void FixedUpdate()
    {
        ApplyMovement(); // Apply movement based on input
    }

    public void OnMove(InputAction.CallbackContext context)
    {
        // Read movement input
        if (context.performed || context.started)
        {
            moveInput = context.ReadValue<Vector2>();
        }
        else if (context.canceled)
        {
            moveInput = Vector2.zero;
        }
    }

    public void OnJump(InputAction.CallbackContext context)
    {
        // Jump if grounded
        if (context.started && isGrounded)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpImpulse); // Apply jump force
            animator.SetTrigger("jump"); // Trigger jump animation
        }
    }

    private void CheckGrounded()
    {
        // Ground detection using OverlapCircle
        Vector2 groundCheckPosition = new Vector2(transform.position.x, transform.position.y - 0.5f);
        float checkRadius = 0.2f; // Slightly larger radius for consistent detection
        Collider2D groundCheck = Physics2D.OverlapCircle(groundCheckPosition, checkRadius, groundLayer);
        isGrounded = groundCheck != null;

        // Debug visualization
        Debug.DrawRay(groundCheckPosition, Vector2.down * 0.1f, isGrounded ? Color.green : Color.red);

        // Reset falling/jumping states when grounded
        if (isGrounded)
        {
            animator.SetBool("isFalling", false);
            animator.SetBool("isJumping", false);
        }
    }

    private void ApplyMovement()
    {
        // Apply movement with current speed
        Vector2 targetVelocity = new Vector2(moveInput.x * CurrentMoveSpeed, rb.velocity.y);
        rb.velocity = targetVelocity;
    }

    private void UpdateAnimations()
    {
        // Handle jump and fall animations
        if (!isGrounded)
        {
            animator.SetFloat("yVelocity", rb.velocity.y); // Track y velocity for transitions
            animator.SetBool("isFalling", rb.velocity.y < -0.1f); // Falling if downward velocity
            animator.SetBool("isJumping", rb.velocity.y > 0.1f); // Jumping if upward velocity
        }
        else
        {
            animator.SetBool("isFalling", false);
            animator.SetBool("isJumping", false);
        }

        // Handle running and movement animations
        animator.SetBool("isMoving", Mathf.Abs(moveInput.x) > 0.1f); // Ignore very small movements
        animator.SetBool("isRunning", IsRunning);
    }

    private void UpdateSpriteDirection()
    {
        // Flip sprite based on movement direction
        if (moveInput.x != 0)
        {
            spriteRenderer.flipX = moveInput.x < 0;
        }
    }

    private void OnDrawGizmos()
    {
        // Visualize the ground detection radius in the scene view
        Gizmos.color = isGrounded ? Color.green : Color.red;
        Gizmos.DrawWireSphere(new Vector2(transform.position.x, transform.position.y - 0.5f), 0.2f);
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *