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

Unity问题:Animator窗口动画正常,协程切换时角色场景内冻结

问题:Unity角色切换动画时冻结,Animator窗口显示正常但场景仅播放默认动画

需求:通过协程随机播放walk、idle2、sniff等布尔参数对应的动画,玩家靠近时触发攻击动画。实际运行时仅默认动画能正常播放,切换其他动画时角色冻结,但Animator窗口内动画运行正常。

用户提供的脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public class CreatureController : MonoBehaviour
{
    public Animator animator; 
    public float speed = 3.0f;
    //public float turnSpeed = 200.0f; 
    public Vector3 boundaryMin; 
    public Vector3 boundaryMax; 

    private bool isWalking = false;
    private Vector3 targetDirection;

    private readonly string[] animationParameters = { "walk", "idle2", "sniff", "roar", "crouch" };

    void Start()
    {
        if (animator == null)
        {
            animator = GetComponent<Animator>();
        }
        targetDirection = transform.forward;

        SetRandomStartingPosition();
        StartCoroutine(PlayRandomAnimation());
    }

    void Update()
    {
        isWalking = animator.GetCurrentAnimatorStateInfo(0).IsName("Creep|Walk1_Action") || 
                    animator.GetCurrentAnimatorStateInfo(0).IsName("Creep|Crouch_Action");

        if (isWalking)
        {
            MoveCreature();
        }
    }
    
    void SetRandomStartingPosition()
    {
        float randomX = Random.Range(boundaryMin.x, boundaryMax.x);
        float randomZ = Random.Range(boundaryMin.z, boundaryMax.z);
        transform.position = new Vector3(randomX, transform.position.y, randomZ);
    }

    void MoveCreature()
    {
        transform.Translate(Vector3.forward * speed * Time.deltaTime);

        Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
        //transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * turnSpeed);

        Vector3 position = transform.position;
        if (position.x < boundaryMin.x || position.x > boundaryMax.x || position.z < boundaryMin.z || position.z > boundaryMax.z)
        {
            Vector3 directionToCenter = (new Vector3((boundaryMin.x + boundaryMax.x) / 2, position.y, (boundaryMin.z + boundaryMax.z) / 2) - position).normalized;
            targetDirection = directionToCenter;
        }
    }

    IEnumerator PlayRandomAnimation()
    {
        while (true)
        {
            int randomIndex = Random.Range(0, animationParameters.Length);
            string chosenAnimation = animationParameters[randomIndex];

            foreach (string parameter in animationParameters)
            {
                animator.SetBool(parameter, false);
            }

            animator.SetBool(chosenAnimation, true);
            Debug.Log($"Playing animation: {chosenAnimation}");

            float waitTime = Random.Range(2.0f, 5.0f);
            yield return new WaitForSeconds(waitTime);

            if (chosenAnimation == "Creep|Walk1_Action" || chosenAnimation == "Creep|Crouch_Action")
            {
                targetDirection = new Vector3(Random.Range(-1.0f, 1.0f), 0, Random.Range(-1.0f, 1.0f)).normalized;
            }
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("Triggered by: " + other.name);  
        if (other.CompareTag("Player"))
        {
            animator.SetBool("attack", true);
        }
    }
}

问题分析与修复方案

1. 布尔参数与动画状态名混淆

协程中错误地将布尔参数名(如walk)和动画状态名(如Creep|Walk1_Action)做比较,导致切换到walk/crouch动画时无法更新移动方向,修正代码:

// 原错误代码
// if (chosenAnimation == "Creep|Walk1_Action" || chosenAnimation == "Creep|Crouch_Action")

// 修正后
if (chosenAnimation == "walk" || chosenAnimation == "crouch")

2. Animator状态机过渡配置检查

  • 确保所有布尔参数对应的动画状态,都有从默认状态(如idle)的过渡条件:对应参数为true,其余参数为false。
  • 关闭过渡的Has Exit Time选项,避免等待当前动画播放完毕才切换。
  • 确认每个动画状态都配置了回到其他状态的过渡逻辑,保证参数切换时能触发状态变化。

3. 根运动与手动移动冲突处理

角色冻结但Animator窗口动画正常,大概率是根运动设置问题:

  • 如果动画自带根运动(如walk动画包含位移):勾选Animator组件的Apply Root Motion,同时注释掉MoveCreature中的transform.Translate代码,避免手动移动覆盖根运动。
  • 如果动画无自带根运动:确认isWalking的状态名判断正确,非walk/crouch动画时角色不动是预期行为;若动画本身无动作显示,检查动画是否正确赋值给状态机,模型的动画组件是否正常。

4. 攻击状态的逻辑补充

当前协程会在攻击时继续切换动画,覆盖攻击参数,需添加攻击状态标记暂停协程:

  • 新增isAttacking变量,在触发攻击时设置为true,并重置所有其他动画参数。
  • 修改协程循环条件为while (!isAttacking),攻击时停止随机动画切换。

修复后的完整代码

using System.Collections;
using UnityEngine;
using Random = UnityEngine.Random;

public class CreatureController : MonoBehaviour
{
    public Animator animator; 
    public float speed = 3.0f;
    public Vector3 boundaryMin; 
    public Vector3 boundaryMax; 

    private bool isWalking = false;
    private Vector3 targetDirection;
    private bool isAttacking = false;

    private readonly string[] animationParameters = { "walk", "idle2", "sniff", "roar", "crouch" };

    void Start()
    {
        animator ??= GetComponent<Animator>();
        targetDirection = transform.forward;

        SetRandomStartingPosition();
        StartCoroutine(PlayRandomAnimation());
    }

    void Update()
    {
        if (isAttacking) return;

        isWalking = animator.GetCurrentAnimatorStateInfo(0).IsName("Creep|Walk1_Action") || 
                    animator.GetCurrentAnimatorStateInfo(0).IsName("Creep|Crouch_Action");

        if (isWalking)
        {
            MoveCreature();
        }
    }
    
    void SetRandomStartingPosition()
    {
        float randomX = Random.Range(boundaryMin.x, boundaryMax.x);
        float randomZ = Random.Range(boundaryMin.z, boundaryMax.z);
        transform.position = new Vector3(randomX, transform.position.y, randomZ);
    }

    void MoveCreature()
    {
        // 若使用根运动,注释此行
        transform.Translate(Vector3.forward * speed * Time.deltaTime);

        Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
        //transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * turnSpeed);

        Vector3 position = transform.position;
        if (position.x < boundaryMin.x || position.x > boundaryMax.x || position.z < boundaryMin.z || position.z > boundaryMax.z)
        {
            Vector3 directionToCenter = (new Vector3((boundaryMin.x + boundaryMax.x) / 2, position.y, (boundaryMin.z + boundaryMax.z) / 2) - position).normalized;
            targetDirection = directionToCenter;
        }
    }

    IEnumerator PlayRandomAnimation()
    {
        while (!isAttacking)
        {
            int randomIndex = Random.Range(0, animationParameters.Length);
            string chosenAnimation = animationParameters[randomIndex];

            foreach (string parameter in animationParameters)
            {
                animator.SetBool(parameter, false);
            }

            animator.SetBool(chosenAnimation, true);
            Debug.Log($"Playing animation: {chosenAnimation}");

            float waitTime = Random.Range(2.0f, 5.0f);
            yield return new WaitForSeconds(waitTime);

            if (chosenAnimation == "walk" || chosenAnimation == "crouch")
            {
                targetDirection = new Vector3(Random.Range(-1.0f, 1.0f), 0, Random.Range(-1.0f, 1.0f)).normalized;
            }
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            isAttacking = true;
            foreach (string parameter in animationParameters)
            {
                animator.SetBool(parameter, false);
            }
            animator.SetBool("attack", true);
            // 可选:监听攻击动画结束后恢复随机动画
            // StartCoroutine(ResumeRandomAnimationAfterAttack());
        }
    }

    // 可选:攻击动画结束后恢复随机逻辑
    // IEnumerator ResumeRandomAnimationAfterAttack()
    // {
    //     yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length);
    //     isAttacking = false;
    //     animator.SetBool("attack", false);
    //     StartCoroutine(PlayRandomAnimation());
    // }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:54:52