You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity 2020.2.7f播放模式下触发动画异常及后续相关问题求助

问题描述

我需要实现一个触发式动画,流程为「上升>旋转>上升」。参考相关教程操作后,首次运行正常,但后续加载出现异常:

  • 在**Play Mode(播放模式)**下,动画仅执行「上升」部分,无法完成后续的旋转和上升动作;
  • 在**Inspector Mode(检视器模式)**中,动画可以正常完整播放。

我尝试过重新创建动画、重写相同代码、升级Unity版本至2020.2.6-2020.2.7,但均未解决问题。以下是动画触发控制器代码:

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

public class PG_animationV3 : MonoBehaviour
{
    [SerializeField] private Animator Pg = null;
    [SerializeField] private bool Pg_anim_trigger = false;
    [SerializeField] private bool Pg_anim_trigger_down = false;

    private void OnTriggerEnter(Collider other)
    {
        if (Pg_anim_trigger)
        {
            Pg.Play("PG_animatorcs",0 ,0 );
        }
        else if (Pg_anim_trigger_down)
        {
            Pg.Play("PG_animatoipnv2anim", 0 ,0 );
        }
    }
}

更新记录

3/10/21

将立方体挂载到带有动画的父物体下后,动画问题得到解决,但出现新错误Animator.GotoState:State could not be found,且立方体无法显示(相机动画运行正常)。

3/11/21

添加gameObject.setactive(false);并将父物体的Animator Controller设置为None后,程序不再崩溃,但现在需要设置计时器以及将玩家速度设为0。以下是玩家移动控制代码:

public class Player_controls : MonoBehaviour
{
    GameObject Player;
    public Rigidbody rb;
    public Vector3 po;
    public float forwardMovement = 1050f;
    public float sidewayMovement = 660f;
    public float Jumpmovement = 5.01f;
    public float Speed = 50;

    void OntriggerEnter(Collider other)
    {
        if(other.gameObject.CompareTag("Power-up"))
        {
            forwardMovement = 0;
        }
    }

    public void FixedUpdate()
    {
        rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
    }
}

现寻求针对上述一系列问题的完整解决方案。


完整解决方案

我来一步步帮你解决这些问题:

1. 修复动画仅执行部分动作的核心问题

你的初始动画断层问题,大概率是状态机过渡逻辑不严谨加上OnTriggerEnter重复触发导致动画被强制重置:

  • 先检查Animator状态机:确保「上升→旋转→上升」的每一步过渡都正确设置。比如第一个上升状态结束后,要通过Exit Time(勾选并设置为1)或者动画事件触发参数来过渡到旋转状态;旋转状态结束后同理过渡到第二个上升状态,不要留空或者设置错误的过渡条件。
  • 修改触发逻辑,防止重复触发打断动画:添加一个标记变量,确保同一时间只播放一次动画,代码调整如下:
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PG_animationV3 : MonoBehaviour
    {
        [SerializeField] private Animator Pg = null;
        [SerializeField] private bool Pg_anim_trigger = false;
        [SerializeField] private bool Pg_anim_trigger_down = false;
        private bool isAnimPlaying = false; // 标记动画是否正在播放
    
        private void OnTriggerEnter(Collider other)
        {
            if (isAnimPlaying) return; // 如果动画正在播放,直接返回
    
            if (Pg_anim_trigger)
            {
                isAnimPlaying = true;
                Pg.Play("PG_animatorcs", 0, 0);
                // 记得在动画的最后一帧添加动画事件,调用ResetAnimFlag()方法重置标记
            }
            else if (Pg_anim_trigger_down)
            {
                isAnimPlaying = true;
                Pg.Play("PG_animatoipnv2anim", 0, 0);
            }
        }
    
        // 动画事件调用的方法,需要在Animator的动画片段中添加事件
        public void ResetAnimFlag()
        {
            isAnimPlaying = false;
        }
    }
    
  • 检查Animator的Update Mode:设置为Normal,避免和FixedUpdate的时序冲突导致动画播放异常。

2. 解决Animator.GotoState:State could not be found错误

这个错误是状态名称不匹配或者Animator Controller缺失状态导致的:

  • 核对状态名称:仔细检查PG_animatorcsPG_animatoipnv2anim这两个名称,和你Animator Controller里的状态名称完全一致吗?比如PG_animatoipnv2anim看起来像是拼写错误(多了个i?),一定要确保大小写、拼写完全相同。
  • 立方体无法显示的排查:检查立方体的Mesh Renderer是否启用,父物体的缩放/位置是否把立方体移出了相机视野,或者立方体的Layer被相机的Culling Mask排除了。

3. 实现计时器与玩家速度控制

你的玩家控制代码有个明显的拼写错误,而且缺少计时器逻辑,我帮你调整完善:

public class Player_controls : MonoBehaviour
{
    public Rigidbody rb;
    public float forwardMovement = 1050f;
    public float sidewayMovement = 660f;
    public float Jumpmovement = 5.01f;
    public float Speed = 50;
    public float powerUpDuration = 3f; // 可以自定义Power-up的持续时间
    private float originalForwardSpeed; // 保存初始速度
    private Coroutine speedCoroutine; // 用于管理计时器协程

    void Start()
    {
        originalForwardSpeed = forwardMovement; // 初始化时保存初始速度
        rb = GetComponent<Rigidbody>(); // 直接获取自身的Rigidbody,避免空引用
    }

    // 注意这里是大写的O!之前的OntriggerEnter是拼写错误,Unity识别不了
    private void OnTriggerEnter(Collider other)
    {
        if(other.gameObject.CompareTag("Power-up"))
        {
            // 如果之前有计时器在运行,先停止它,避免重复触发
            if (speedCoroutine != null)
                StopCoroutine(speedCoroutine);
            
            forwardMovement = 0; // 立即归零速度
            speedCoroutine = StartCoroutine(ResetSpeedAfterDelay(powerUpDuration));
            Destroy(other.gameObject); // 销毁Power-up,防止重复触发
        }
    }

    // 协程实现延迟恢复速度
    private IEnumerator ResetSpeedAfterDelay(float delay)
    {
        yield return new WaitForSeconds(delay);
        forwardMovement = originalForwardSpeed; // 恢复初始速度
    }

    public void FixedUpdate()
    {
        rb.AddForce(0, 0, forwardMovement * Speed * Time.deltaTime);
    }
}
  • 额外注意:Power-up对象必须有Collider(勾选Is Trigger),如果是静态物体,需要添加Rigidbody并勾选Kinematic,否则OnTriggerEnter不会触发。

4. 父物体Animator的最优设置

你之前把父物体的Animator Controller设为None,这不是长久之计:

  • 如果父物体本身不需要动画,直接移除它的Animator组件,避免不必要的错误;
  • 如果父物体需要动画,确保它的Animator Controller包含你要调用的所有状态,并且状态名称完全匹配。

内容的提问来源于stack exchange,提问作者Fire-Source

火山引擎 最新活动