Unity 角色移动Transform.translate与RigidBody两种方法

Unity角色移动脚本两种方法(个人学习总结)

做完官方项目之后,我准备拿其他模型作为练手,却发生了一些问题。并不是每个模型的骨骼动画都做的和官方的一样标准,可能会有一些不足。所以在用官方给的角色移动代码的时候会发现Animator.DeltaPosition.magnitude总是为0,即矢量的长度为0。
这样就会出现一个问题,我动画机播放了动画,角色却没有位移!这个问题也许出自于骨骼的制作问题(我试了很久,网上的一些作者可能做骨骼会有些问题),并不是标准的骨骼动画,所以角色也不能进行标准代码下的“根运动”

什么是根运动(也叫做根骨骼动画)
根骨骼动画:当动画中角色发生位移后,动作坐标原点跟随角色移动。例如一个向前跳跃的动画,如果在场景中重复该动画,能够看到角色一路往前跳跃,位置一直在前进。适用于有位移的放技能动作等。

非根骨骼动画:当动画中角色发生位移后,动作坐标原点始终保持在原地不变。例如一个向前跳跃的动画,如果在场景中重复该动画,能够看到角色往前跳跃后,完成一次该动画后角色的位置又会重置回去。适用于不能改变角色位置的动画,如闲置动作Idle。
在这里插入图片描述

如上图,当勾上那几个Bake Into Pose后,就成了非根骨骼动画,角色向前跳跃后,动作的坐标原点没有移动。

  1. Transform方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class JuggerMovement2 : MonoBehaviour
{
    public float turnSpeed = 20f;
    public float runSpeed = 3f;
    Animator m_Animatior;
    Transform m_transform;
    // Start is called before the first frame update
    void Start()
    {
        m_Animatior = GetComponent<Animator>();
        m_transform = GetComponent<Transform>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        float fire = Input.GetAxis("Fire1");
        //m_Movement.Set(horizontal, 0f, vertical);
        //m_Movement.Normalize();


        bool hasHorizontalInput = !Mathf.Approximately(horizontal, 0f);
        bool hasVerticalInput = !Mathf.Approximately(vertical, 0f);
        bool isWalking = hasHorizontalInput || hasVerticalInput;
        bool hasFire = !Mathf.Approximately(fire, 0f);
        m_Animatior.SetBool("IsWalking", isWalking);

        Debug.LogWarning("(" + horizontal + " , " + vertical + " , " + hasFire + ")");
        if (hasFire)
        {
            m_Animatior.Play("Attack(1)");
        }

        m_transform.Translate(new Vector3(0, 0, 1) * Time.deltaTime *vertical *runSpeed);//每一帧的增量
        m_transform.Rotate(new Vector3(0, 1, 0) * horizontal * 5.0f);//以Y-axis作为旋转轴
    }
}

为什么人物移动的Vertical对应的是Z轴呢?这得通过我们Unity Editor的Scenes去看
在这里插入图片描述
你看看我准备的模型,人物自身的坐标系,角色前进的方向是自身坐标系的Z轴方向,所以我们使用Transform.Translate的时候new Vector(0,0,1)。

m_transform.Rotate是以Y轴为旋转轴,所以里面是new Vector(0,1,0)*xxx

Transform的运动方法有个问题就是:这个脚本最适用于赛车类,用于操纵车的跑动。为什么呢?因为这个脚本产生的效果是W键D键是控制角色的前进后退,A键D键是控制人物的转向。所以当人物后退的时候并不会转过身子后退,而是头朝向前方,直直往后位移。这样的运动方式就比较适合赛车等驾驶工具,不太适合人物的移动。

  1. Rigidbody方法(人物必须得挂有Rigidbody组件)
using System.Collections;
using System.Collections.Generic;
using UnityEditor.UIElements;
using UnityEngine;

public class JuggerMovement : MonoBehaviour
{
    // Start is called before the first frame update
    public float turnSpeed = 20f;

    Animator m_Animatior;
    Rigidbody m_Rigidbody;
    Vector3 m_Movement;
    Quaternion m_Rotation = Quaternion.identity;
    void Start()
    {
        m_Animatior = GetComponent<Animator>();
        m_Rigidbody = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        float fire = Input.GetAxis("Fire1");
        m_Movement.Set(horizontal, 0f, vertical);
        m_Movement.Normalize();


        bool hasHorizontalInput = !Mathf.Approximately(horizontal, 0f);
        bool hasVerticalInput = !Mathf.Approximately(vertical, 0f);
        bool isWalking = hasHorizontalInput || hasVerticalInput;
        bool hasFire = !Mathf.Approximately(fire, 0f);
        m_Animatior.SetBool("IsWalking", isWalking);

        Debug.LogWarning("(" + horizontal + " , " + vertical + " , " + hasFire + ")");
        if (hasFire)
        {
            m_Animatior.Play("Attack(1)");
        }
        Vector3 desiredForward = Vector3.RotateTowards(transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
        m_Rotation = Quaternion.LookRotation(desiredForward);


    }

    private void OnAnimatorMove()
    {
        //m_Rigidbody.MovePosition(m_Rigidbody.position + m_Movement * m_Animatior.deltaPosition.magnitude);
        //m_Animatior.deltaPosition.magnitude总是为0的话就得自己设置向量的长度,我下面设置的是0.1f
        m_Rigidbody.MovePosition(m_Rigidbody.position + m_Movement * 0.1f);
        Debug.Log("m_Animatior.deltaPosition.magnitude: " + m_Animatior.deltaPosition);
        m_Rigidbody.MoveRotation(m_Rotation);
    }
}

先讲讲关于m_Movement.Set()和m_Movement.Normalize()的必要性问题:

3D 空间中的矢量具有三个值 — 此 Set 方法为每一个分配一个值。该方法有三个参数,矢量的每个坐标对应一个参数。现在,移动矢量的值在 x 轴上为水平输入,在 y 轴上为 0,在 z 轴上为垂直输入。第二个参数的 0 后面还有一个 f,用于指示计算机将该数字视为浮点数。
现在您需要解决一个小问题。移动矢量由两个数字组成,这两个数字的最大值可以为 1。如果它们两者的值都为 1,则矢量的长度(称为其大小)将大于 1。这便是勾股定理描述的三角形的边之间的关系。

在这里插入图片描述
这意味着您的角色沿对角线移动的速度将比沿单个轴的移动速度更快。为了确保不会发生这种情况,您需要确保移动矢量始终具有相同的大小。为此,可对其进行标准化。对矢量进行标准化意味着保持矢量的方向相同,但是将其大小更改为 1。所以用一下一行代码进行标准化

        m_Movement.Normalize();

计算角色的前向矢量

请记住:您需要让角色面对的方向与其移动的方向相同。所有的 Transform 组件都有一个前向矢量,因此一个很好的中间步骤是计算您希望角色具有的前向矢量。

        Vector3 desiredForward = Vector3.RotateTowards(transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);

下面是关于RotateTowards的定义
在这里插入图片描述

创建和存储旋转
接下来,您需要使用矢量来获取并存储旋转,以便可以在任何需要的地方使用。您将像存储移动矢量一样存储旋转,因此在该位置声明其变量很合理。

        m_Rotation = Quaternion.LookRotation(desiredForward);

四元数 (Quaternion) 是存储旋转的一种方式,可用于解决将旋转存储为 3D 矢量时遇到的一些问题。

移动
接下来让我们考虑一下移动动画化角色的细节。该角色有一段有趣的 Walk 动画,最好为此使用根运动。但是,该动画中没有任何旋转,如果您尝试在 Update 方法中旋转刚体 (Rigidbody),则动画可能会覆盖该刚体(这可能导致角色在应该旋转的时候不旋转)。
您实际需要的是动画的一部分根运动,但不是全部的根运动;具体来说,您需要应用移动而不是旋转。那么如何更改从 Animator 中应用根运动的方式呢?幸运的是,MonoBehaviour 有一种特殊的方法可用于更改从 Animator 中应用根运动的方式。所以用到OnAnimatorMove()

        //m_Rigidbody.MovePosition(m_Rigidbody.position + m_Movement * m_Animatior.deltaPosition.magnitude);
        //m_Animatior.deltaPosition.magnitude总是为0的话就得自己设置向量的长度,我下面设置的是0.1f
        m_Rigidbody.MovePosition(m_Rigidbody.position + m_Movement * 0.1f);

Animator 的 deltaPosition 是由于可以应用于此帧的根运动而导致的位置变化。您将其大小(即长度)乘以我们希望角色移动的实际方向上的移动向量。

旋转
接下来,应用旋转。在 OnAnimatorMove 方法中的 MovePosition 调用下面添加以下行:

        m_Rigidbody.MoveRotation(m_Rotation);

这与 MovePosition 调用非常相似,但它适用于旋转。这次您无需对旋转进行更改,而只是直接设置旋转。

刚体运动总结
关键在于m_Animatior.deltaPosition.magnitude。有些模型的骨骼没有对齐或者一些小问题的存在会使这个矢量的长度无法产生一个值,故而永远为0。所以我们可以手动设置成0.1f等等(移动速度越快则值越高)。但是这是固定值,实际上标准的骨骼动画的每一帧的magnitude的值可能是不一样的。所以设定了固定值的仿真效果肯定不会出色。可能会显得动画模型比较木讷。
刚体运动代码和上面第一种Transform代码的运动方式不一样:Rigidbody这个代码比较适合人物角色的移动,原因是W,A,S,D键并不同上面Transform一般(transform方法是W,S键控制前进后退。A,D键控制运动方向),这里的上下左右键位并没有分哪个键单独控制前进后退,哪个键控制左右。而是W键就是往前走,A键往左走,S键向后走,D键像右走,而且向哪个方向走人物的头就会面向哪一方。所以这个RigidBody移动代码适合做人物。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值