You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity跳跃动画冻结问题求助:按下空格键后动画无法正常过渡

跳跃动画冻结,状态过渡失效问题排查与解决

问题描述

作为Unity新手,遇到跳跃动画异常:按下空格键后动画正常播放,但随后冻结,Animator窗口可见状态过渡未生效,已排查数日无果。

相关代码

PlayerMovement脚本

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

public class PlayerMovement : MonoBehaviour
{
    public CharacterController2D controller;
    public Animator animator;

    public float runSpeed = 40f;

    float horizontalMove = 0f;
    bool jump = false;

    void Update()
    {
        // Get horizontal input and set speed
        horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
        animator.SetFloat("Speed", Mathf.Abs(horizontalMove));

        // Check for jump input
        if (Input.GetButtonDown("Jump"))
        {
            jump = true;
            animator.SetBool("IsJumping", true);
            Debug.Log("Jump button pressed. IsJumping set to true.");
        }

        // Debug animator's current state and parameter values
        Debug.Log($"Current Animator State: {animator.GetCurrentAnimatorStateInfo(0).IsName("Player_Idle")}");
        Debug.Log($"IsJumping: {animator.GetBool("IsJumping")}");
    }

    public void OnLanding()
    {
        // Called when the player lands
        animator.SetBool("IsJumping", false);
        Debug.Log("Player landed. IsJumping set to false.");
    }

    void FixedUpdate()
    {
        // Move the character and reset the jump flag
        controller.Move(horizontalMove * Time.fixedDeltaTime, false, jump);
        jump = false;

        // Debug movement and jumping values
        Debug.Log($"HorizontalMove: {horizontalMove}, Jump: {jump}");
    }
}

CharacterController2D脚本

using UnityEngine;
using UnityEngine.Events;

public class CharacterController2D : MonoBehaviour
{
    [SerializeField] private float m_JumpForce = 400f;                          // Amount of force added when the player jumps.
    [Range(0, 1)] [SerializeField] private float m_CrouchSpeed = .36f;          // Amount of maxSpeed applied to crouching movement. 1 = 100%
    [Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f;  // How much to smooth out the movement
    [SerializeField] private bool m_AirControl = false;                         // Whether or not a player can steer while jumping;
    [SerializeField] private LayerMask m_WhatIsGround;                          // A mask determining what is ground to the character
    [SerializeField] private Transform m_GroundCheck;                           // A position marking where to check if the player is grounded.
    [SerializeField] private Transform m_CeilingCheck;                          // A position marking where to check for ceilings
    [SerializeField] private Collider2D m_CrouchDisableCollider;                // A collider that will be disabled when crouching

    const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded
    private bool m_Grounded;            // Whether or not the player is grounded.
    const float k_CeilingRadius = .2f; // Radius of the overlap circle to determine if the player can stand up
    private Rigidbody2D m_Rigidbody2D;
    private bool m_FacingRight = true;  // For determining which way the player is currently facing.
    private Vector3 m_Velocity = Vector3.zero;

    [Header("Events")]
    [Space]

    public UnityEvent OnLandEvent;

    [System.Serializable]
    public class BoolEvent : UnityEvent<bool> { }

    public BoolEvent OnCrouchEvent;
    private bool m_wasCrouching = false;

    private void Awake()
    {
        m_Rigidbody2D = GetComponent<Rigidbody2D>();

        if (OnLandEvent == null)
            OnLandEvent = new UnityEvent();

        if (OnCrouchEvent == null)
            OnCrouchEvent = new BoolEvent();
    }

    private void FixedUpdate()
    {
        bool wasGrounded = m_Grounded;
        m_Grounded = false;

        // The player is grounded if a circlecast to the groundcheck position hits anything designated as ground
        // This can be done using layers instead but Sample Assets will not overwrite your project settings.
        Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround);
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].gameObject != gameObject)
            {
                m_Grounded = true;
                if (!wasGrounded)
                    OnLandEvent.Invoke();
            }
        }
    }
}

已进行的排查操作

  • 过渡未设置Exit Time
  • 过渡时长设为0秒
  • 配置Any State到跳跃状态的过渡(条件为IsJumping=true),跳跃状态到Idle/Run状态的过渡(条件为IsJumping=false且Speed参数正常,Speed功能正常)
  • 绑定OnLandEvent时只能从MonoScript中选择字符串名称的函数,不确定是否相关

解决方案

1. 确认OnLandEvent绑定有效性

检查CharacterController2D组件的OnLandEvent是否正确关联到PlayerMovement的OnLanding方法:

  • 如果使用字符串绑定,确保函数名拼写完全一致(区分大小写);
  • 优先采用拖拽绑定:在Inspector中找到CharacterController2D的OnLandEvent,点击"+"号,拖拽Player对象到对象框,然后从下拉菜单中选择PlayerMovement -> OnLanding();
  • 运行时查看Debug日志,若未出现"Player landed. IsJumping set to false.",说明事件未触发,需检查GroundCheck位置是否在玩家脚底、m_WhatIsGround是否包含地面图层。

2. 简化动画过渡条件

  • 跳跃状态到Idle/Run的过渡,暂时去掉Speed条件,只保留IsJumping=false,测试是否能正常过渡;
  • 确认Animator参数中的IsJumping为Bool类型,未被误设为其他类型;
  • 检查跳跃动画的Loop Time是否未勾选,避免动画循环导致无法触发过渡。

3. 代码细节修正

  • 修正PlayerMovement脚本开头的笔误eusing为using,避免编译错误;
  • 验证CharacterController2D中GroundCheck的检测逻辑:确保OverlapCircleAll能正确检测到地面,可在Scene窗口查看GroundCheck的位置和检测范围。

4. 实时调试验证

运行游戏时,在Animator窗口的Parameters面板观察IsJumping的值:

  • 若落地后IsJumping仍为true,说明OnLanding未被调用,回到第一步排查绑定问题;
  • 若IsJumping已变为false但过渡仍未触发,检查过渡的Has Exit Time是否被意外勾选,或过渡的条件逻辑是否冲突。

内容的提问来源于stack exchange,提问作者Tom Hunt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 07:17:07