Unity第三人称角色动画循环异常 角色相对摄像机往复位移问题求助
问题描述
Unity实现第三人称角色移动功能时出现稳定复现异常:Running动画循环播放过程中,角色每个动画周期都会出现移动到摄像机前方后自动回退的现象。
附异常逻辑脚本:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class char_control : MonoBehaviour { public GameObject Player; public bool isRunning; public float horizontal_move; public float vertical_move; // Update is called once per frame void Update() { if (Input.GetButton("Horizontal") || Input.GetButton("Vertical")) { Player.GetComponent<Animation>().Play("Running"); horizontal_move = Input.GetAxis("Horizontal") * Time.deltaTime * 100; vertical_move = Input.GetAxis("Vertical") * Time.deltaTime * 1; isRunning = true; transform.Rotate(0, horizontal_move, 0); transform.Translate(0, 0, vertical_move); } else { Player.GetComponent<Animation>().Play("Idle"); isRunning = false; } } }
根本原因
异常由配置和逻辑的两层问题共同触发,和「每动画周期复现回退」的特征完全匹配:
- 核心原因:Running动画片段导入配置错误,未开启循环姿势烘焙。动画单周期播放完成后,根节点会自动复位到动画起始帧的位置,视觉上就是角色往前移动一段后突然弹回原位,循环往复。
- 次要原因:脚本逻辑存在冗余和冲突:一是每帧重复调用
Animation.Play()触发动画状态切换,存在不必要的性能开销和状态重置风险;二是代码手动控制位移的同时,没有关闭动画根运动,两套位移逻辑叠加容易出现移动速度异常、穿模等连带问题。
修复步骤
1. 修正动画导入配置
选中项目资源窗口中的Running动画片段,在Inspector的Animation设置面板做如下调整:
- 勾选
Loop Time、Loop Pose两个选项,开启动画循环支持 - 展开
Root Transform Rotation、Root Transform Position(Y)、Root Transform Position(XZ)三个折叠项,全部勾选Bake Into Pose,对应Offset规则选择Loop
配置完成后点击Apply保存,动画循环时根节点不会再出现周期结束回弹的问题。
2. 优化控制脚本
做三处调整:缓存组件引用避免每帧重复获取、增加动画状态判断避免每帧触发Play、明确位移坐标系、消除根运动和代码位移的冲突。修正后代码如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class char_control : MonoBehaviour { public GameObject Player; public bool isRunning; public float horizontal_move; public float vertical_move; private Animation _playerAnim; public float moveSpeed = 5f; public float rotateSpeed = 100f; void Start() { // 提前缓存动画组件引用,避免每帧GetComponent产生额外开销 _playerAnim = Player.GetComponent<Animation>(); // 代码控制位移时直接关闭根运动,避免两套位移逻辑冲突 if (TryGetComponent<Animator>(out var animator)) { animator.applyRootMotion = false; } } void Update() { bool hasMoveInput = Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0; if (hasMoveInput) { // 仅在状态切换时触发动画播放,避免每帧重复调用Play if (!isRunning) { _playerAnim.Play("Running"); isRunning = true; } horizontal_move = Input.GetAxis("Horizontal") * Time.deltaTime * rotateSpeed; vertical_move = Input.GetAxis("Vertical") * Time.deltaTime * moveSpeed; transform.Rotate(0, horizontal_move, 0); // 明确指定基于自身坐标系位移,避免父节点/摄像机变换导致的位移异常 transform.Translate(0, 0, vertical_move, Space.Self); } else { if (isRunning) { _playerAnim.Play("Idle"); isRunning = false; } } } }
补充说明:如果后续打算改用根运动控制位移,直接删掉代码里
Rotate、Translate的手动变换逻辑即可,不要两套位移逻辑同时生效。
内容的提问来源于stack exchange,提问作者Prakhar Mehndiratta
相关产品推荐
相关产品推荐

