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

Unity Animator卡在空默认状态:Show/HideClipboard动画触发无响应

剪贴板Show/Hide动画触发异常排查方案

问题描述

在Unity中实现了Pickup、Drop、ShowClipboard和HideClipboard四个动画,仅后两者存在异常:拾取剪贴板并显示clipboardSlot UI后,按Tab键切换动画时,控制台显示触发成功,但Animator始终卡在空默认状态,动画无法播放。已尝试改用布尔参数替代触发器、单独使用Animator、将HideClipboard直接连接至AnyState等方法,均未解决问题。

Animator窗口

关联代码

using UnityEngine;
using UnityEngine.UI;

public class PickableObject : MonoBehaviour
{
    private Text tabHintText; // Reference to the Text component of TabHint
    private Image clipboardSlotImage; // Reference to the Image component of ClipboardSlot
    private static bool isAnyObjectPickedUp = false;
    private bool isClipboardPickedUp = false;
    private bool isClipboardVisible = false;
    private bool isPlayerInRange = false;
    private Transform playerHand;
    private bool isPickedUp = false;
    private bool isFirstAnimationActive = true;
    private Vector3 originalScale;

    public Vector3 positionOffset;
    public Vector3 rotationOffset;

    public GameObject interactionPromptE;
    public GameObject interactionPromptQ;
    public GameObject arms;
    public GameObject clipboard;

    private Animator clipboardAnimator;
    private MouseLook mouseLook;

    void Start()
    {
        // Initialize variables
        InitializeUIComponents();
        FindPlayerHand();
        InitializeClipboardAnimator();
        HideInteractionPrompts();
        HideArmsAtStart();
    }

    private void InitializeUIComponents()
    {
        GameObject canvas = GameObject.Find("Canvas");

        // Find and assign the clipboardSlotImage and tabHintText components
        clipboardSlotImage = canvas.transform.Find("ClipboardSlot")?.GetComponent<Image>();
        tabHintText = canvas.transform.Find("TabHint")?.GetComponent<Text>();

        if (clipboardSlotImage == null)
        {
            Debug.LogError("ClipboardSlot Image component not found!");
        }
        if (tabHintText == null)
        {
            Debug.LogError("TabHint Text component not found!");
        }

        // Initially hide the UI elements
        clipboardSlotImage?.gameObject.SetActive(false);
        tabHintText?.gameObject.SetActive(false);
    }

    private void FindPlayerHand()
    {
        playerHand = GameObject.FindWithTag("Player").transform.Find("HandPosition");
        if (playerHand == null)
        {
            Debug.LogError("HandPosition not found! Check hierarchy and path.");
        }
        originalScale = transform.localScale;
    }

    private void InitializeClipboardAnimator()
    {
        clipboardAnimator = clipboard.GetComponent<Animator>();
        if (clipboardAnimator != null)
        {
            clipboardAnimator.ResetTrigger("Show");
            clipboardAnimator.ResetTrigger("Hide");
        }
        else
        {
            Debug.LogError("No Animator component found on the clipboard!");
        }
    }

    private void HideInteractionPrompts()
    {
        interactionPromptE?.SetActive(false);
        interactionPromptQ?.SetActive(false);
    }

    private void HideArmsAtStart()
    {
        if (arms != null)
        {
            Animator animator = arms.GetComponent<Animator>();
            if (animator != null)
            {
                animator.ResetTrigger("PickUp");
                animator.ResetTrigger("Drop");
            }
            arms.SetActive(false);
        }
        else
        {
            Debug.LogError("Arms object not found at the start!");
        }

        mouseLook = Camera.main.GetComponent<MouseLook>();
        if (mouseLook == null)
        {
            Debug.LogError("MouseLook script not found on the Main Camera!");
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player") && !isPickedUp && !isAnyObjectPickedUp)
        {
            isPlayerInRange = true;
            interactionPromptE?.SetActive(true);
        }
    }

    void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            isPlayerInRange = false;
            interactionPromptE?.SetActive(false);
        }
    }

    void Update()
    {
        if (isPlayerInRange && !isPickedUp && Input.GetKeyDown(KeyCode.E))
        {
            PickUpObject();
        }
        else if (isPickedUp && !isClipboardPickedUp && Input.GetKeyDown(KeyCode.Q))
        {
            DropObject();
        }

        if (clipboardSlotImage != null && clipboardSlotImage.gameObject.activeSelf && Input.GetKeyDown(KeyCode.Tab))
        {
            ToggleClipboardAnimations();
        }
    }

    void PickUpObject()
    {
        isPickedUp = true;

        if (gameObject.CompareTag("Clipboard"))
        {
            isClipboardPickedUp = true;
            isClipboardVisible = true;
            clipboardSlotImage?.gameObject.SetActive(true);
            tabHintText?.gameObject.SetActive(true);

            StartCoroutine(DisableClipboardWithDelay());
        }
        else
        {
            isAnyObjectPickedUp = true;
            interactionPromptQ?.SetActive(true);
        }

        EnableArms();

        if (arms != null)
        {
            Animator animator = arms.GetComponent<Animator>();
            if (animator != null)
            {
                animator.SetTrigger("PickUp");

                if (isClipboardPickedUp)
                {
                    StartCoroutine(HideArmsAfterAnimation(animator));
                }
            }

            if (mouseLook != null)
            {
                mouseLook.areArmsActive = true;
            }
        }

        AttachObjectToHand();
    }

    void AttachObjectToHand()
    {
        transform.SetParent(playerHand);
        transform.localPosition = Vector3.zero;
        transform.localRotation = Quaternion.identity;
        transform.localPosition = positionOffset;
        transform.localRotation = Quaternion.Euler(rotationOffset);

        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
        {
            rb.isKinematic = true;
            rb.useGravity = false;
        }

        interactionPromptE?.SetActive(false);
    }

    void DropObject()
    {
        isPickedUp = false;
        isAnyObjectPickedUp = false;
        Animator animator = arms.GetComponent<Animator>();
        if (animator != null)
        {
            animator.SetTrigger("Drop");
            StartCoroutine(DisableArmsAfterAnimation(animator));
        }
        DetachObjectFromHand();

        interactionPromptQ?.SetActive(false);
    }

    void DetachObjectFromHand()
    {
        transform.SetParent(null);
        transform.localScale = originalScale;

        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
        {
            rb.isKinematic = false;
            rb.useGravity = true;
        }
    }

    IEnumerator DisableClipboardWithDelay()
    {
        yield return null; // You can use `yield return new WaitForEndOfFrame();` if necessary
        clipboard.SetActive(false);
        DisableArms();
        if (mouseLook != null)
        {
            mouseLook.areArmsActive = false;
        }
    }

    IEnumerator HideArmsAfterAnimation(Animator animator)
    {
        yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length);

        if (!isClipboardPickedUp || !isClipboardVisible)
        {
            DisableArms();
            if (mouseLook != null)
            {
                mouseLook.areArmsActive = false;
            }
        }
    }

    IEnumerator DisableArmsAfterAnimation(Animator animator)
    {
        yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length);
        DisableArms();
        if (mouseLook != null)
        {
            mouseLook.areArmsActive = false;
        }
    }

    void ToggleClipboardAnimations()
    {
        EnableArms();
        if (mouseLook != null)
        {
            mouseLook.areArmsActive = false;
        }

        if (isFirstAnimationActive)
        {
            ShowClipboard();
            Debug.Log("Show Clipboard Triggered");
        }
        else
        {
            HideClipboard();
            Debug.Log("Hide Clipboard Triggered");
        }

        isFirstAnimationActive = !isFirstAnimationActive;
    }

    void ShowClipboard()
    {
        if (!clipboardAnimator.GetCurrentAnimatorStateInfo(0).IsName("ShowClipboard"))
        {
            clipboardAnimator?.SetTrigger("Show");
            Debug.Log("ShowClipboard state triggered");
        }
    }

    void HideClipboard()
    {
        if (!clipboardAnimator.GetCurrentAnimatorStateInfo(0).IsName("HideClipboard"))
        {
            clipboardAnimator?.SetTrigger("Hide");
            Debug.Log("HideClipboard state triggered");
        }
    }

    void EnableArms()
    {
        if (arms != null)
        {
            arms.SetActive(true);
        }
    }

    void DisableArms()
    {
        if (arms != null)
        {
            arms.SetActive(false);
        }
    }
}

排查与修复方案

一、核心代码问题:剪贴板对象被禁用导致Animator失效

拾取剪贴板时,DisableClipboardWithDelay协程直接执行了clipboard.SetActive(false),导致挂载在该对象上的Animator组件完全停止工作,无法响应后续的Trigger触发。这是动画无法播放的根本原因。

修复方式:
不要禁用整个剪贴板对象,仅隐藏其可视组件(如Renderer),保留Animator的激活状态:

IEnumerator DisableClipboardWithDelay()
{
    yield return new WaitForEndOfFrame();
    // 只隐藏可视部分,保留Animator运行
    Renderer clipboardRenderer = clipboard.GetComponent<Renderer>();
    if(clipboardRenderer != null)
        clipboardRenderer.enabled = false;
    DisableArms();
    if (mouseLook != null)
    {
        mouseLook.areArmsActive = false;
    }
}

同时在Show/Hide方法中同步控制Renderer显示:

void ShowClipboard()
{
    Renderer clipboardRenderer = clipboard.GetComponent<Renderer>();
    if(clipboardRenderer != null)
        clipboardRenderer.enabled = true;
    clipboardAnimator?.ResetTrigger("Hide");
    clipboardAnimator?.SetTrigger("Show");
    Debug.Log("ShowClipboard state triggered");
}

void HideClipboard()
{
    Renderer clipboardRenderer = clipboard.GetComponent<Renderer>();
    if(clipboardRenderer != null)
        clipboardRenderer.enabled = false;
    clipboardAnimator?.ResetTrigger("Show");
    clipboardAnimator?.SetTrigger("Hide");
    Debug.Log("HideClipboard state triggered");
}

二、代码逻辑优化

  1. 移除冗余的状态判断:原代码中!clipboardAnimator.GetCurrentAnimatorStateInfo(0).IsName("XXX")的判断,在对象禁用时会返回无效状态,直接移除该判断并重置对立Trigger,避免触发冲突。
  2. 同步状态变量:在ToggleClipboardAnimations中更新isClipboardVisible,保持状态同步:
void ToggleClipboardAnimations()
{
    EnableArms();
    if (mouseLook != null)
    {
        mouseLook.areArmsActive = false;
    }

    if (isFirstAnimationActive)
    {
        ShowClipboard();
        isClipboardVisible = true;
        Debug.Log("Show Clipboard Triggered");
    }
    else
    {
        HideClipboard();
        isClipboardVisible = false;
        Debug.Log("Hide Clipboard Triggered");
    }

    isFirstAnimationActive = !isFirstAnimationActive;
}

三、Animator配置验证

  1. 检查过渡条件:确保默认状态到ShowClipboard的过渡、ShowClipboard到HideClipboard的过渡,分别绑定了Show和Hide Trigger参数。
  2. 关闭Exit Time:将所有相关过渡的Has Exit Time设为false,确保Trigger触发后立刻切换状态,无需等待动画播放完毕。
  3. 确认参数类型:Animator中Show和Hide必须是Trigger类型,而非布尔或其他类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:29:49