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

Unity角色动画控制异常求助:闪避动画触发错误(附脚本)

问题分析与修正方案

你的问题核心是状态逻辑冲突和错误的速度设置:

  • 跳跃、闪避逻辑块互相重置对方的状态变量,导致动画触发混乱
  • 闪避时设置ySpeed=2直接赋予了向上速度,这就是伴随跳跃行为的原因
  • 闪避分支的错误逻辑导致动画持续触发,无法正常切换

以下是修正后的完整脚本,关键修改点已标注:

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    [SerializeField]
    private float rotationSpeed;

    [SerializeField]
    private float jumpHeight;

    [SerializeField]
    private float gravityMultiplier;

    [SerializeField]
    private float jumpHorizontalSpeed;

    [SerializeField]
    private float jumpButtonGracePeriod;

    [SerializeField]
    private float dodgeButtonGracePeriod;

    [SerializeField]
    private Transform cameraTransform;

    private Animator animator;
    private CharacterController characterController;
    private float ySpeed;
    private float originalStepOffset;
    private float? lastGroundedTime;
    private float? jumpButtonPressedTime;
    private float? dodgeButtonPressedTime;
    private bool isJumping;
    private bool isGrounded;
    private bool isDodging;

    void Start()
    {
        animator = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
        originalStepOffset = characterController.stepOffset;
    }

    void Update()
    {
        // 基础移动输入
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");
        Vector3 movementDirection = new Vector3(horizontalInput, 0, verticalInput);
        float inputMagnitude = Mathf.Clamp01(movementDirection.magnitude);

        // 慢走逻辑
        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
        {
            inputMagnitude /= 2;
        }

        // 动画混合树参数
        animator.SetFloat("Input Magnitude", inputMagnitude, 0.05f, Time.deltaTime);

        // 基于相机方向调整移动方向
        movementDirection = Quaternion.AngleAxis(cameraTransform.rotation.eulerAngles.y, Vector3.up) * movementDirection;
        movementDirection.Normalize();

        // 重力计算
        float gravity = Physics.gravity.y * gravityMultiplier;
        ySpeed += gravity * Time.deltaTime;

        // 更新地面状态时间
        if (characterController.isGrounded)
        {
            lastGroundedTime = Time.time;
        }

        // 捕获输入事件
        if (Input.GetButtonDown("Jump"))
        {
            jumpButtonPressedTime = Time.time;
        }
        if (Input.GetButtonDown("Dodge"))
        {
            dodgeButtonPressedTime = Time.time;
        }

        // 统一处理地面状态逻辑
        if (Time.time - lastGroundedTime <= jumpButtonGracePeriod)
        {
            characterController.stepOffset = originalStepOffset;
            ySpeed = -0.5f;
            isGrounded = true;
            animator.SetBool("isGrounded", true);

            // 优先处理闪避(可根据需求调整优先级)
            bool shouldDodge = Time.time - dodgeButtonPressedTime <= dodgeButtonGracePeriod;
            bool shouldJump = Time.time - jumpButtonPressedTime <= jumpButtonGracePeriod && !shouldDodge;

            if (shouldDodge)
            {
                // 闪避:地面动作,移除向上速度
                animator.SetBool("isDodging", true);
                animator.SetBool("isJumping", false);
                animator.SetBool("isFalling", false);
                isDodging = true;
                isJumping = false;
                dodgeButtonPressedTime = null;
                lastGroundedTime = null; // 短暂脱离地面判定,防止连续触发
            }
            else if (shouldJump)
            {
                // 跳跃逻辑
                ySpeed = Mathf.Sqrt(jumpHeight * -3 * gravity);
                animator.SetBool("isJumping", true);
                animator.SetBool("isDodging", false);
                animator.SetBool("isFalling", false);
                isJumping = true;
                isDodging = false;
                jumpButtonPressedTime = null;
                lastGroundedTime = null;
            }
            else
            {
                // 普通地面状态
                animator.SetBool("isJumping", false);
                animator.SetBool("isDodging", false);
                animator.SetBool("isFalling", false);
                isJumping = false;
                isDodging = false;
            }
        }
        else
        {
            // 空中状态
            characterController.stepOffset = 0;
            isGrounded = false;
            animator.SetBool("isGrounded", false);
            animator.SetBool("isDodging", false); // 空中强制关闭闪避动画
            isDodging = false;

            if ((isJumping && ySpeed < 0) || ySpeed < -2)
            {
                animator.SetBool("isFalling", true);
                animator.SetBool("isJumping", false);
                isJumping = false;
            }
        }

        // 角色转向逻辑
        if (movementDirection != Vector3.zero)
        {
            animator.SetBool("isMoving", true);
            Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
            transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
        }
        else
        {
            animator.SetBool("isMoving", false);
        }

        // 空中移动处理
        if (!isGrounded)
        {
            Vector3 velocity = movementDirection * inputMagnitude * jumpHorizontalSpeed;
            velocity.y = ySpeed;
            characterController.Move(velocity * Time.deltaTime);
        }
    }

    private Vector3 AdjustVelocityToSlope(Vector3 velocity)
    {
        var ray = new Ray(transform.position, Vector3.down);
        if (Physics.Raycast(ray, out RaycastHit hitInfo, 0.2f))
        {
            var slopeRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
            var adjustedVelocity = slopeRotation * velocity;
            if (adjustedVelocity.y < 0)
            {
                return adjustedVelocity;
            }
        }
        return velocity;       
    }

    private void OnAnimatorMove()
    {
        if (isGrounded)
        {
            Vector3 velocity = animator.deltaPosition;
            velocity = AdjustVelocityToSlope(velocity);
            velocity.y += ySpeed * Time.deltaTime;
            characterController.Move(velocity);
        }
    }

    private void OnApplicationFocus(bool focus)
    {
        Cursor.lockState = focus ? CursorLockMode.Locked : CursorLockMode.None;
    }
}

关键修改说明

  1. 统一状态处理:将跳跃、闪避逻辑合并到地面状态判断中,避免互相覆盖状态变量
  2. 修正闪避速度:移除闪避时的ySpeed=2设置,闪避为地面动作,无需向上速度
  3. 输入优先级控制:设置闪避优先于跳跃的逻辑(可按需调整),防止同时触发两个动作
  4. 空中状态清理:空中强制关闭闪避动画,避免错误触发旧动画
  5. 状态同步优化:规范状态变量与动画参数的同步逻辑,确保状态一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 15:05:05