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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:21:15