分享

万字教程 对 Unity 中的 动画系统基础 全面解析+实战演练,你确定要错过吗?

 敲代码的小Y 2021-12-01

📢前言

  • 本篇文章将对Unity中的动画系统做一个基础的介绍
  • 包括AnimationAnimator的基础属性知识和实战演练来学习Unity中的动画系统
  • 动画在游戏中的地位尤为重要,模型动画一般是成套的,可以有专门的建模师对游戏模型进行设计,然后加上一系列的动画进行模型动作设计~

       就拿王者荣耀举例来说,在英雄单独介绍的界面,每次点开一个英雄都会有一个英雄出场动画,大家都知道传说皮肤荣耀典藏的出场动画都特别炫酷,那这就是一个典型的动画了。

       还有在游戏中英雄的待机动画移动动画普攻释放技能等等,都是通过调用播放不同的动画来实现的

       设计师们通过对每个人物骨骼的特点设计出不一样风格的动画效果,能让人眼前一亮,让每个英雄的动作都有自己的标志性,这就是一个极好的作品啦!

        一般来说每个模型都有一系列独有的动画系列,具有相同骨骼类型的模型还能共用一套动画!在游戏中,有些英雄的攻击模式比较相似,设计师们就可以偷懒让他们共用一个系列的动画啦~

那我们这次就来一起学习一下Unity中的动画系统吧!


🎄Unity动画基础知识

  • 在介绍Unity动画之前,先来科普一下基础知识
  • 大家都知道在Unity中有很多的游戏对象,所谓游戏对象就是场景中存在的一些个物体
  • 模型呢在Unity中算是一个特殊的游戏物体
  • 专门做模型的软件也有挺多,较出名的类似3DMAXMaya等等

  • 模型的格式有很多种,比如 *.max、*.fbx、*.obj等等格式

  • Unity中常用的模型格式就是后缀为 .fbx 的文件了

  • Unity中的动画系统有两个,一个是Animation,另一个是Animator

  • Legacy动画系统使用的是Animation组件,直接对动画片段进行操作

  • Mecanim动画系统使用的是Animator组件,是对各动画片段之间的切换进行控制。

  • 相对于老版Animation,新版的Animator加入了动画状态机和骨骼Avatar。

常用类介绍:

  • Animator:动画控制器,控制Mecanim动画系统的接口,用来管理多个动画;
  • Animation:用于播放动画,老版中单独的一个Animation也可以完成动画的播放和切换,不过状态切换之类的需要程序猿代码控制。在新版中,状态管理部分交给了Animator;
  • AnimationClip:动画剪辑片段,储存基于关键帧的动画,是用于Animation来播放动画;
  • AnimationState:动画状态,用来改变单一动画的播放速度、权重、时间、层级、播放Mode,以及混合模式;
  • AnimationEvent:动画事件,用于某种条件下触发自定义函数;
  • StateMachineBehaviour:动画状态机管理器拓展类,脚本继承了该类之后,绑定到Animator上某State上面。当状态发生变化,可以重载响应函数。类似 触发器的响应函数;

下图就是我导入的一个后缀为 .fbx 的模型,可以看到该模型子级目录中还有挺多东西,那就是他的结构组成啦~还有网格渲染和一个骨骼

  • 然后顺带介绍一下模型的Rig窗口属性,其他几个窗口的属性说实话,我现在还没自己改过。
  • 因为没有用过,个人感觉对程序开发项目来说没有那么重要,所以在这里就不误人子弟了...

有的模型还带有动画,类似下面这种点击右边的播放按钮可以查看动画效果~


🎁Animation动画组件

  • 上面提到了AnimationLegacy动画系统使用的

  • 由于Animation算是老版的动画系统,所以这里我们就简单介绍一下,然后做一个简单实例,更多的介绍留给新人——Animator

  • unity的老版本中我们只有Animation组件,在4.6版本以后则增添了Animator组件

  • Animation:用于播放动画,老版中单独的一个Animation也可以完成动画的播放和切换,不过状态切换之类的需要程序猿代码控制。在新版中,状态管理部分交给了Animator

❤️Animation组件属性:


🧡Animation实战演练:简单模仿坦克开炮动画制作

  • 我们这里用一个简单的实例来说一下Animation的简单用法
  • 还是这个坦克模型,我要给他做一个简单的开炮动画
在这里插入图片描述
  • 这里我们选中这个坦克模型,按照以下图示三步操作
  • 选中游戏对象,打开Animation编辑窗口
在这里插入图片描述
  • 然后进入这个画面,点Create创建动画,然后选择一个存放的地址,在Assets目录下单独新建一个管理动画文件夹就行~

  • 因为我这里想要的效果是让坦克的炮筒(GameObject)前后移动,做成一个简单的开炮动画
  • 所以我这里选择坦克的炮筒控制它的坐标位置前后变化来作为一个开炮动画
  • 那这一步的目的就是控制GameObjectPosition位置,按一下图示做就行
在这里插入图片描述
  • 上述步骤完成后然后就是以下图片所示模样:
在这里插入图片描述
  • 双击图示红色框部分可以新建一个关键帧,中间三个就是我新建的关键帧,这个关键帧可以左右拖动,代表关键帧执行的时间节点

  • 有小伙伴可能想问这个关键帧有什么用呢,请接着往下看,就知道它的作用了!

在这里插入图片描述
  • 然后我们把这五个关键帧的Position.z分别设置成:0,1,0,-1,0

  • 将图中那条白色竖线左右拖到关键帧上设置Position.z的属性即可,输完数值别忘了点回车,确认数值已经输上!

在这里插入图片描述
  • 然后有的小伙伴又要问了,这几个数值有啥用呀,非要这样设置嘛?
  • 废话不多说,先来看一下这样设置出来的效果图吧
在这里插入图片描述
  • 虽然效果有点陋~但是勉强能达到我想要的一个开炮使得炮筒移动的效果。
  • 我们上面的动画设置的五个关键帧,分别是Position.z的值为:0,1,0,-1,0
  1. 第一个关键帧Position.z的值设置为0,默认值我们没改动
  2. 第二个关键帧将Position.z的值设置为1,让炮筒向前移动一米
  3. 第三个关键帧将Position.z的值设置为0,让炮筒复位
  4. 第四个关键帧将Position.z的值设置为-1,让炮筒向后移动一米
  5. 第五个关键帧将Position.z的值设置为0,让炮筒再复位
  • 这样就达成了上图中的效果,炮筒前后移动一米,然后初末位置相同,形成一个简单的无缝循环动作。就达成我们的目的啦~

  • 关键帧就是在一个我们指定的时间,将设置的动画属性修改成我们设置的值

  • 上面五个关键帧,相当于五个时间节点,在每个时间节点都设置好了他们应该执行的属性

  • 然后将这个文件拖到Animation的组件上在脚本中调用执行动画的代码就可以啦


💛脚本代码控制动画API

  • 上面的步骤设置完成之后,就可以在想使用动画的地方调用啦~
  • 下面是简单的动画播放停止的调用方法
  • Play("TankAnimation1" );,播放动画,传入参数为动画名字

  • Stop("TankAnimation1") ,停止动画,传入参数为动画名字

  • CrossFade("TankAnimation1", 0.5f); ,有过度的切换动画,传入参数(动画名字,过度时间)


🔔Animator动画组件

  • Animator是4.6版本以后出现的,Animator是动画控制器,控制Mecanim动画系统的接口,用来管理多个动画;
  • Mecanim动画系统就是Animator
在这里插入图片描述

💚动画工作流程

  • Unity动画系统是基于动画剪辑的概念,其中包含了特定对象如何随着时间改变其位置、旋转或其他属性的信息。
  • 每个片段都可以看作是一个单一的线性录音。
  • 来自外部来源的动画剪辑是由艺术家或动画师与第三方工具,如Max或Maya,或来自动作捕捉工作室或其他来源。
  • 然后,动画剪辑被组织成一个被称为Animator Controller的类似于流程图的结构化系统。
  • Animator控制器充当一个“状态机”,跟踪当前播放的剪辑,以及动画何时应该改变或混合在一起。
  • 通俗来说就是就是通过Animator组件去控制Animator Controller中的动画切换,来达到我们想要的模型执行各种动作。

💙属性介绍

Animator组件属性:

在这里插入图片描述
  • Controller :动画控制器(动画状态机)

  • Avatar:⻣骼

  • Apply Root Motion:应⽤根动作

  • Update Mode:动画的更新模式。Normal:同步更新,动画速度与运行速度相匹配,运行速度慢,动画慢。Animate Physics:动画是有物理的相互作用时,用此模式。Unscaled Time:不成比例的时间,动画忽略当前的运行速度。

  • Culling Mode:动画的裁剪模式。Always Animate:总是启用动画,不进行裁剪。Cull Update Transforms:更新裁切。Cull Completely:完全裁切。

其中骨骼根动作这里不做研究,因为一般是建模师负责,我们用的时候不多Controller 就要仔细研究一下了,因为他就是导致Animator强大的一个原因!

Controller 的属性:

在这里插入图片描述
  • Controller 新建方法:Creat -> Animator Controller
在这里插入图片描述

Animator Controller视图:

在这里插入图片描述
  • 可以在这个面板右键创建一个Empty,新建的第一个成为默认动画状态!

  • 也可以直接将动画从Project面板拖动到这个Animator Controller视图中~

在这里插入图片描述

Animation States动画状态:

点击这个State可以查看(Animation States)动画状态:

  • Motion:当前状态下的动画片段
  • Speed:动画的默认速度
  • Mirror:镜像
  • Foot IK:是否使用Foot IK
  • 是否使用Foot IK:是否对没有动画的属性写回默认值
  • Transitions:由当前状态出发的过渡条件列表

黄色显示的状态为默认状态,指状态机首次激活时所进入的状态。可以在其他状态上右击选择Set as Layer Default State命令改变默任状态。

Animation Transitions(动画过渡)

在某一动画状态右击后选择Make Transition,然后选择过渡到的下一动画状态,即可建立过渡联系。

  • 就是点击动画之间的小箭头这个

  • Has Exit Time :有退出时间,上一个动画播放完毕才能执行过渡动画。
  • Setting:设置
  • Exit Time:退出时间
  • Fixed Duration:固定持续时间
  • Transfition Duratio:过渡持续时间
  • Transition Offset:过渡偏移
  • Interruption Source:中断源
  • Ordered Interuption:有序中断

Animation Parameters(动画参数)

参数值的四种基本类型:

  • Float:浮点数
  • Int:整数
  • Bool:返回布尔值,通过复选框来选择True或者False
  • Trigger:触发一个布尔值,复位控制器时消耗一个转变,由一个圆按钮表示。

Animation Layers(动画层)

  • 实现同一时刻进行多种动画状态播放。
在这里插入图片描述

Blend Tree(融合树)

对两个或更多个相似的运动进行混合

制作:在视图右击空白处后选择Create State -> From New Blend Tree,双击进入。实例:Blend Type(混合类型)

  • 1D混合:通过唯一的一个参数来控制子动画的混合。
  • 2D Simple Directional(2D简单定向模式)适用于所有动画都具有一定的运动方向、其中任何两段动画的运动方向都不相同的情形。
  • 2D Freeform Directional(2D自由定向模式)适用于所有动画都具有一定的运动方向,但同一方向上可以存在多段动画。
  • 2D Free Cartesian(2D自由笛卡尔模式)适用于动画不具有确定运动确定运动方向的情形。Direct:直接。让用户直接控制每个节点的权重。

该内容部分知识参考文章 https://blog.csdn.net/NCZ9_/article/details/84639900


💜Animator类介绍

先来介绍下Animator的常用类,后面实战演练会用得到

💞动画操作步骤

以下是一个实施动画的具体操作步骤

  • 1、配置好⻣骼

  • 2、裁剪好动画⽚段

  • 3、创建动画控制器 AnimatorController

    • 添加动画⽚段
    • 设置默认的动画状态是哪个
    • 设置动画与动画之间的过渡关系
    • 添加动画参数
    • 设置动画过渡条件
  • 4、通过代码调整动画参数。从⽽达成过渡条件,实现动画切换


💕Animator实战演练:实现人物行走,攻击动画切换

  • 下面我们开始进行Animator实例介绍:实现人物行走,攻击动画切换
  • 这个实例就不需要我们自己设计动画了(不是科班出身,设计的动画还丑,怕你们吐槽~)
  • 我们只需要将现成的动画使用脚本控制来实现目的!

开始操作

  • 首先我们新建一个场景,导入一个模型站立的小猫咪,如下图:
在这里插入图片描述
  • 因为我导入的这个小猫咪的模型自带几种动画,所以我这里就不需要自己制作模型动画啦~
  • 一般也都是这样,我们只需要将这个动画在Animator Controller进行设计就好啦!
在这里插入图片描述
  • 在小猫咪模型身上添加一个Animator组件:Add Component -> Animator

新建一个Animator Controller,上面已经讲过了,这里再发一次~ 并将这个Controller拖到Animator组件中~

  • 双击打开Controller后,直接将动画从Project面板将动画片段拖动到这个Animator面板中即可

  • 然后将他们之间的动画过渡关系设置好

  • 如下图中,Aert为待机动画,Move为移动动画,ATK3为攻击动画

  • 左边是Animation Parameters(动画参数),上面提到过~CanMove是一个Bool值,用来控制执行移动动画Attack是一个Trigger值,用来控制执行攻击动画MovePlayerSpeed是一个Float值,用来控制移动动画的播放速度

在这里插入图片描述
  • 这里有需要注意的一个点,点击Move状态,然后将MovePlayerSpeed添加上,因为我们待会要通过这个改变移动动画的播放速度

待机动画向移动动画过渡设置:

  • 点击下图中左边待机动画向移动动画过渡的箭头,在右边有个小加号,将我们的动画参数CanMove加上,选择True-反过来则是CanMove加上,但是数值变为False

待机动画向攻击动画过渡:

  • 点击左边动画 Aert -> ATK3 的箭头,然后点击右边的小加号,将Attack添加上

移动动画向攻击动画过渡:

  • 点击左边动画 Move-> ATK3 的箭头,然后点击右边的小加号,将Attack添加上

  • 但是到这里有的小伙伴可能就有些疑惑,为什么动画参数一个用Bool,一个用Trigger,还有一个用Float呢?

  • 这个呢,是根据具体情况而定的

  • 因为移动动画,使我们按下某个键后就一直执行的,所以用Bool值来进行移动的判定

  • 但是攻击动画呢,是你按下去一次就执行一次,显然Trigger更适合攻击动画

  • 使用float值呢是为了改变动画的播放速度,显然是一个数值,使用float是最好的选择啦!

这样的话在Unity视图方面就设置完成了,接下来就是写代码控制动画的播放了 直接上代码挂在我们的小猫咪身上就行啦!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CatAnimation : MonoBehaviour
{
    [Header("移动速度")]
    public float MoveSpeed = 1f;
    [Header("旋转速度")]
    public float TurnSpeed = 5f;
    
    private Animator ani;
    private  float hor, ver;
    private void Awake()
    {
        ani = GetComponent<Animator>();
    }
    private void Update()
    {
        hor = Input.GetAxis("Horizontal");
        ver = Input.GetAxis("Vertical");
        if (hor !=0 || ver !=0)
        {
            ani.SetBool("CanMove",true);
        }
        else
        {
            ani.SetBool("CanMove",false);
        }
        //判断当前角色是否正在播放移动动画
        if (ani.GetCurrentAnimatorStateInfo(0).IsName("Move"))
        {
            //声明一个临时播放速度
            float sp = 0;

            if (ver >= 0)
            {
                if (Input.GetKey(KeyCode.LeftShift))
                {
                    sp = 2;
                }
                else
                {
                    sp = 1;
                }
            }
            else
            {
                if (Input.GetKey(KeyCode.LeftShift))
                {
                    sp = -2;
                }
                else
                {
                    sp = -1;
                }
            }
            //前后移动
            transform.position += ver* transform.forward * Time.deltaTime * MoveSpeed;
            ani.SetFloat("MovePlaySpeed",sp);
            transform.eulerAngles += transform.up * TurnSpeed * hor;
        }
        if (Input .GetKeyDown(KeyCode.J))
        {
            ani.SetTrigger("Attack");
        }
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            MoveSpeed *= 2;
        }
        if(Input.GetKeyUp(KeyCode.LeftShift))
        {
            MoveSpeed /= 2;
        }
    }
}

代码解析:

  • 代码中在Awake中拿到Animator组件,在Update中定义了一个简单的移动和旋转的方法

  • 然后通过判断 是否按下水平和垂直轴 来进行 是否播放移动动画的方法

  • 如果按下任意一个WASD或者上下左右键,调用ani.SetBool("CanMove",true);进行播放移动动画

  • 不然就执行ani.SetBool("CanMove",false);执行待机动画

  • 然后对当前是否进行播放移动动画进行了一个判断:if (ani.GetCurrentAnimatorStateInfo(0).IsName("Move"))

  • 如果在播放移动动画,则按下Shift键时,对移动动画播放进行加速,并且加速小猫咪的移动速度,这样就可以让小猫咪的移动速度和动画播放速度更协调

  • 不然的话小猫咪普通移动时的动画播放加速移动时的动画播放就是一样的,显然不符合常理,之前所添加的MovePlayerSpeed属性就是为了如此!

  • 再就是按下J键的时候调用 ani.SetTrigger("Attack");进行攻击动画的播放~

上图,看效果!

  • 到此为止一个简单的Animator使用就完成啦!
  • 我做的这个只是最简单的Animator使用方法,在真正的一个游戏里面,对动画这块的内容操作也是极其复杂
  • 本篇文章只是作为一个对Unity动画系统的基础入门介绍+使用
  • 想更深入动画系统还需要更多的时间进行研究!

👥总结

  • 本文介绍了Unity中的两种动画系统:AnimationAnimator

  • 目前前者用的较少,后者使用的较为普遍

  • 文章不仅对Unity动画系统做了一个简单的介绍,还分别介绍了两种动画系统的基础属性和实战演练

  • 感兴趣的小伙伴可以自己动手演练一番,毕竟实战出真知!

  • 个人感觉介绍的可能没有那么深,但还是挺详细的,适合新手入门!

  • 如果文章哪里写的不好,请各位多多指教~ 文章干不干,你来说了算!肝了两天,码字不易!

  • 觉得文章不错可以给个三连支持一下博主哦~谢谢

在这里插入图片描述

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多