【Unity动画系统】详解Root Motion动画在Unity中的应用(二)

Root Motion遇到Blend Tree

如果Root Motion动画片段的速度是1.8,那么阈值就要设置为1.8,那么在代码中的参数就可以直接反映出Root Motion的最终移动速度。

Compute Thresholds:根据Root Motion中某些数值自动计算这里的阈值。

Velocity X/Y/Z:代表在XYZ方向上的速度,注意Z方向的话,也就是前后的方向上的位移速度。

Root Motion下的旋转速度,第一个是弧度每秒,第二个是角度每秒;一般用在角色转身的时候。

有时候多个动画的播放速度不一致,那么可以选择Adjust Time Scale的第一个Homogeneous Speed,Unity会自动为我们计算出这里的播放速度。

但是游戏角色并不是上面Threshold里同样的速度移动,Threshold中的速度是针对行走动画原本的骨骼的,而Unity中是通过Avatar系统复用了这个动画,所以移动速度会有改变。

不同角色应用相同的Root Motion,移动速度为什么不一样?

虽然transform相同,但是人物缩放值不一样(Avatar不一样)

cs 复制代码
void Start(){
	aniamtor.humanScale;	//获取角色骨骼的大小
	animator.speed /= animator.humanScale;	//速度/人物大小就可以求得相同的速度
}

但是又不想影响动画速度,那么就使用。

cs 复制代码
void Start()
animator.SetFloat("ScaleFoctor" , 1 / animator.ScaleFoctor);	

这样只有BlendTree播放的速度会受到影响(BlendTree的播放速度,相对应的其他动画正常速度播放,简而知加速后减速,动画加速但是混合树减速),虽然前进步数加快导致速度加快,但是混合树使整个动画播放变慢,则导致动画播放正常。

如何在使用Root Motion时自定义移动速度

使用Root Motion会使移动速度变慢,如果我们想自定义移动速度,那么最简单粗暴就是改变动画速度。

如果想改变速度就用"想改变的速度/阈值",然后改成播放速度。

但其实移动的速度不是匀速的。

上面的阈值其实是移动的平均值。

为什么引入Root Motion,怕导致动画与移动不同步。

总结:

Root Motion最主要是解决动画,而不是位移,那么我们应该把移动的控制权从Root Motion中拿回来(RigidBody主要用来模拟各种物理计算)。接下来引入RigidBody:

cs 复制代码
Rigidbody rig;

void Start(){
    rig = GetComponent<Rigidbody>();
}

private void OnAnimatorMove(){	//启用后Unity不再用直接用Root Motion来驱动游戏对象,调用时间在FixedUpdate和动画系统的各回调方法之后,物理引擎计算之前
    Move();
}

void Move(){
   currentSpeed = Mathf.Lerp(targetSpeed, currentSpeed, 0.5f);
   animator.SetFloat("Speed", currentSpeed);
   rig.velocity = animator.velocity;	
   //rig.velocity = currentSpeed;	//如果希望精准运动的话,就将currentSpeed赋值给rig,不过这里动画和移动可能会有细微不同步
}

需要将Update Mode改为Animate Physics。

Root Motion与RigidBody的冲突:重力为什么不起作用了?

为什么使用RigidBody,重力不起作用了?

要将Root Transform Position(Y)的Bake Into Pose打开

使用RigidBody后角色下落速度慢是为什么?

在物理引擎Internal physics update给物理引擎一个9.8的下落速度,然后FixedUpdate是一秒刷新50次,就是0.02s一次。

Rigidbody大概在Internal physics update里获取一个大约0.196的向下速度,然后rigidbody也会根据当前的速度对游戏对象进行位移,然后物理引擎刷新回到OnAnimatorMove,但是此时将速度刷新归0,又重新获取速度0.196被强行打断了下落速度。

解决:x与z的速度来自于动画的xz,但y轴的来自rigidbody。

cs 复制代码
Vector3 vector3 = new Vector3(animator.velocity.x , rig.velocity.y , animator.velocity.z);
rig.velocity = vector3;

考虑:

如果对重力要求不高,那么可以自己写脚本来控制重力。

如果对移动要求不高,那么可以不使用Root Motion。

重点:Root Transform Position(Y)的Bake Into Pose最好勾上,然后动画的Update Mode改为Animate Physics,而且使用RigidBody后不要将代码执行放在FixedUpdate或者OnAnimatorMove方法里(刷新方式为Animate Physics)。

在Unity中利用Root Motion、Input System、Cinemachine制作一个简单的角色控制器

创建一个人物控制器,然后创建一个混合树改名为Locomotion。

创建一个参数Vertical Speed,让动作转换来自此参数。

添加三个动作,不设置自动阈值。

以Z轴方向为主进行阈值设定,用想要的速度/现阈值求出播放速度。

添加一个Player Input,设置输入系统:

Behaviour改为用Unity的事件

添加方法

添加刚体和碰撞体

从Cinemachine添加一个Virtual Camera

因为做的是一个俯视的视角,所以不需要旋转,死死的跟住角色即可。

添加一个脚本TopDown来做控制器:

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;

public class TopDown : MonoBehaviour
{
    Animator animator;  //动画

    Vector2 playerInputVec; //保存用户的输入
    bool isRunning; //判断是否在跑

    Vector3 playerMovement; //玩家的运动
    public float rotateSpeed = 1000;

    Transform playerTransform;

    float currentSpeed; //当前的速度
    float targetSpeed;  //目标速度
    float walkSpeed = 1.5f;    //走路速度
    float RunSpeed = 3.5f;  //奔跑的速度


    private void Start()
    {
        animator= GetComponent<Animator>();
        playerTransform = transform;    //缓存只需要找一遍,而调用transform则每次都要找一遍
    }

    private void Update()
    {
        RotatePlayer();
        MovePlayer();
    }

    public void GetPlayerMoveInput(InputAction.CallbackContext ctx)    //接收玩家输入的方法
    {
        playerInputVec = ctx.ReadValue<Vector2>();  //接收玩家放向键入
        Debug.Log(playerInputVec);
    }

    public void GetPlayerRunInput(InputAction.CallbackContext ctx)  //奔跑方面的操作
    {
        isRunning = ctx.ReadValue<float>() > 0 ? true : false;  //从输入系统中读取float的值 
        Debug.Log(isRunning);
    }

    void RotatePlayer() //旋转角色的方法
    {
        if (playerInputVec.Equals(Vector2.zero))    //判断玩家的输入是不是0,是0则return
        {
            return;
        }
        playerMovement.x = playerInputVec.x;
        playerMovement.z = playerInputVec.y;

        Quaternion targetRotation = Quaternion.LookRotation(playerMovement , Vector3.up);   //playermovement是z轴的朝向的,playermovement与up叉乘的结果是x轴朝向的向量,x轴与z轴叉乘的结果是y轴方向。  
        transform.rotation = Quaternion.RotateTowards(playerTransform.rotation , targetRotation , rotateSpeed * Time.deltaTime);    //向什么方向转,每帧旋转多少
    }

    void MovePlayer()
    {
        targetSpeed = isRunning ? RunSpeed : walkSpeed;
        targetSpeed *= playerInputVec.magnitude;    //判断是否有输入,即使在奔跑输入为0那么就不移动
        currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, 0.5f); //0.5的速度逐渐增加到目标速度
        animator.SetFloat("Vertical Speed", currentSpeed);
    }
}
相关推荐
仁希'2 分钟前
《Unity3D高级编程之进阶主程》第二章 架构(三) - 架构的误区,如何做前端架构,以及如何架构Unity3D项目
笔记·unity·架构
老朱佩琪!6 小时前
Unity分享一个简单的3D角色漫游脚本
3d·unity·游戏引擎
雪 狼8 小时前
unity对于文件夹的操作
windows·unity·游戏引擎
JessieHaha16 小时前
Unity2019.4.5 首页背景图和背景色WebGL
unity·webgl
Angle灬魔君20 小时前
Unity【入门】场景切换和游戏退出及准备
游戏·unity·游戏引擎
沐沐森的故事1 天前
Unity 使用AVProMovieCapture实现Game视图屏幕录制
unity·游戏引擎·avpro·moviecapture·录制·游戏录制·game录制
Bunny Chen1 天前
Unity 简单载具路线 Waypoint 导航
unity·游戏引擎
这是我581 天前
C语言牢大坠机
c语言·其他·开源·动画·visual studio·牢大坠机
ttod_qzstudio1 天前
Unity中使用VectorGraphics插件时,VectorUtils.RenderSpriteToTexture2D方法返回结果错误的解决方法
unity·vector graphics·spriterenderer
我写代码菜如坤2 天前
unity获取键盘按键
unity·计算机外设·游戏引擎