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

Unity 2D游戏实现手部动画碰撞后冻结功能技术求助

解决Unity 2D手部动画碰撞冻结问题

针对你的需求,核心问题在于未正确实现动画冻结的状态控制,以及协程参数和触发逻辑的错误。以下是修改后的完整脚本,附带关键逻辑说明:

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

public class FacingScript : MonoBehaviour
{
    private SpriteRenderer _render;
    private Camera _mainCam;
    private Vector3 _mousePos;
    private Animator _aniHand;

    private bool _isAnimationFrozen = false;
    private Coroutine _freezeCoroutine;

    void Start()
    {
        _render = GetComponent<SpriteRenderer>();
        _mainCam = Camera.main;
        _aniHand = GetComponent<Animator>();
        gameObject.SetActive(false);
    }

    void Update()
    {
        if (!gameObject.activeSelf) return;

        // 处理鼠标朝向
        _mousePos = Input.mousePosition;
        _mousePos = _mainCam.ScreenToWorldPoint(new Vector3(_mousePos.x, _mousePos.y, _mainCam.transform.position.z - transform.position.z));
        
        Vector3 rotation = _mousePos - transform.position;
        _render.flipY = rotation.x > 0;

        // 处理E键逻辑:播放/恢复动画
        if (Input.GetKeyDown(KeyCode.E))
        {
            if (!gameObject.activeSelf)
            {
                gameObject.SetActive(true);
                _aniHand.Play("CharacterHand");
            }
            else if (_isAnimationFrozen)
            {
                // 强制恢复动画
                ResumeAnimation();
            }
            else
            {
                // 重新播放动画
                _aniHand.Play("CharacterHand");
            }
        }
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        // 碰撞到箱子或地面时触发冻结,避免重复触发
        if ((collision.gameObject.CompareTag("box") || collision.gameObject.CompareTag("ground")) && !_isAnimationFrozen)
        {
            _freezeCoroutine = StartCoroutine(FreezeAnimationCoroutine(5f));
        }
    }

    /// <summary>
    /// 冻结动画的协程
    /// </summary>
    private IEnumerator FreezeAnimationCoroutine(float waitTime)
    {
        _isAnimationFrozen = true;
        // 记录当前动画进度,恢复时从当前帧继续
        float currentNormalizedTime = _aniHand.GetCurrentAnimatorStateInfo(0).normalizedTime;
        _aniHand.speed = 0;

        // 等待5秒,期间如果按E则提前退出
        float elapsedTime = 0;
        while (elapsedTime < waitTime && _isAnimationFrozen)
        {
            elapsedTime += Time.deltaTime;
            yield return null;
        }

        // 如果不是被E键提前恢复,自动恢复动画
        if (_isAnimationFrozen)
        {
            ResumeAnimation();
        }
    }

    /// <summary>
    /// 恢复动画播放
    /// </summary>
    private void ResumeAnimation()
    {
        _isAnimationFrozen = false;
        _aniHand.speed = 1;
        // 从冻结时的进度继续播放
        _aniHand.Play("CharacterHand", 0, _aniHand.GetCurrentAnimatorStateInfo(0).normalizedTime);
        
        // 停止协程(如果存在)
        if (_freezeCoroutine != null)
        {
            StopCoroutine(_freezeCoroutine);
            _freezeCoroutine = null;
        }
    }
}

关键修改说明

  1. 状态控制优化

    • 新增_isAnimationFrozen标记动画是否冻结,避免碰撞时重复启动协程
    • 用_freezeCoroutine引用协程实例,方便中途停止
  2. 动画冻结与恢复逻辑

    • 冻结时设置_aniHand.speed = 0,这是最直接的动画暂停方式,同时记录当前动画进度
    • 恢复时重置speed = 1,并通过Play方法的第三个参数从冻结帧继续播放,避免跳帧
  3. E键逻辑完善

    • 首次按E激活手部物体并播放动画
    • 动画冻结时按E直接恢复播放
    • 非冻结状态按E重新播放动画
  4. 碰撞触发修正

    • 同时支持"box"和"ground"标签的碰撞检测
    • 只有在未冻结状态下才触发冻结协程
  5. 协程逻辑优化

    • 协程内加入循环判断,允许通过E键提前终止冻结状态
    • 自动恢复时先检查冻结状态,避免冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:50:26