Unity 2021中Animator混合树抖动问题求助
玩家动画异常抖动问题排查
环境与设置
- Unity版本:2021.3.6f1
- 动画配置:四向idle/walk独立动画,通过两个混合树控制,使用
moveX、moveY、is_idle三个Animator参数 - 输入系统:Unity新输入系统
- 相机:Cinemachine(已确认无问题)
问题现象
运行时动画出现异常抖动,表现为Animator不断重置动画播放状态。
排查过的方案
- 最初使用Any State到各动画状态的过渡,问题依旧
- 改用混合树后,调整多种参数仍未解决
关键代码(Player Controller)
using UnityEngine.InputSystem; using UnityEngine; using System.Collections.Generic; public class PlayerController : MonoBehaviour { public float moveSpeed = 1f; public ContactFilter2D movementFilter; public float collissionOffset = 0.05f; Vector2 movementInput; Vector2 prevMovementInput; Rigidbody2D rb; Animator animator; List<RaycastHit2D> castCollissions = new List<RaycastHit2D>(); void Start() { rb = GetComponent<Rigidbody2D>(); animator = GetComponent<Animator>(); } private void FixedUpdate() { if (movementInput != Vector2.zero) { bool success = TryMove(movementInput); if (movementInput != prevMovementInput) { animator.SetFloat("moveX", movementInput.x); animator.SetFloat("moveY", movementInput.y); prevMovementInput = movementInput; } if (!success) { success = TryMove(new Vector2(movementInput.x, 0)); if (!success) { success = TryMove(new Vector2(0, movementInput.y)); } } animator.SetBool("is_idle", false); } else { //animator.SetBool("is_idle", true); } } private bool TryMove(Vector2 direction) { int count = rb.Cast(direction, movementFilter, castCollissions, moveSpeed * Time.fixedDeltaTime + collissionOffset); if (count == 0) { rb.MovePosition(rb.position + direction * moveSpeed * Time.fixedDeltaTime); return true; } else { return false; } } void UpdateAnimation_DONT_USE() { // 原无混合树时的动画控制逻辑 if (movementInput.x == 0.00d) { if (movementInput.y == 1.00d) { //up animator.SetInteger("direction", 1); } } else if (movementInput.x == 1.00d) { if (movementInput.y == 0.00d) { //right animator.SetInteger("direction", 2); } } else if (movementInput.x == 0.00d) { if (movementInput.y == -1.00d) { //down animator.SetInteger("direction", 3); } } else if (movementInput.x == -1.00d) { if (movementInput.y == 0.00d) { //left animator.SetInteger("direction", 4); } } } void OnMove(InputValue movementValue) { movementInput = movementValue.Get<Vector2>(); } }
可能的问题点与解决建议
- 输入值精度误差
新输入系统的摇杆输入可能存在微小浮点波动,导致movementInput != prevMovementInput频繁触发,不断重置混合树参数。添加阈值判断避免误触发:
if (Mathf.Abs(movementInput.x - prevMovementInput.x) > 0.01f || Mathf.Abs(movementInput.y - prevMovementInput.y) > 0.01f) { animator.SetFloat("moveX", movementInput.x); animator.SetFloat("moveY", movementInput.y); prevMovementInput = movementInput; }
FixedUpdate与动画帧时序不匹配
Animator的参数更新依赖Update帧,而当前参数设置在FixedUpdate中,可能因FixedUpdate调用频率(默认50次/秒)与游戏帧率不一致导致同步问题。建议将Animator参数更新逻辑移到Update()方法中。混合树过渡设置问题
检查混合树的动画过渡时长(Transition Duration),若设置为0会导致瞬间切换引发抖动;同时确认混合树的参数范围与输入值(-1到1)完全匹配,避免参数超出范围导致的异常混合。is_idle参数逻辑缺失
当前代码中静止状态下的is_idle设置被注释,导致无法切换到idle混合树,可能引发状态冲突。取消注释animator.SetBool("is_idle", true),确保静止时正确触发idle动画。移动失败时的参数同步问题
当TryMove失败(比如碰撞障碍物),玩家实际未移动但movementInput仍为非零,导致混合树参数与实际移动状态不符。可以在移动失败后,根据玩家实际的移动方向(而非原始输入)更新Animator参数,或者在完全无法移动时临时将is_idle设为true。
内容的提问来源于stack exchange,提问作者MrCPlusPlus
相关产品推荐
相关产品推荐

