《Unity游戏动画设计》-动画基础知识

要想让游戏看上去栩栩如生,动画表现往往扮演着十分重要的角色,我们打开一座陈旧的遗迹大门,打翻一架沉重的钢琴,或是按下“R”释放一团闪耀着的大火球。。这些都需要动画来赋予他生命,刚体动画,骨骼动画,粒子动画等等,否则,一切元素都会归于静止而显得枯燥和缺乏生命力。


什么是动画

在基本的层面上,动画就是时间和变化这两个独立特征之间关系的表示,这么说未免显的太隐晦,不如说,动画定义了一段时间内的变化。如红绿灯由红灯变为黄灯,这就是一段动画,表示一段时间(红灯开始到黄灯亮起)内的变化(颜色变黄)。

因为我本身不是动画设计师,所以没能从艺术角度分析什么是动画,但毫无疑问优秀的动画可以有效得表达情绪,氛围,场景以及理念等特征。大大加强游戏的代入感。(例如经典的奥日与黑暗森林,用了大量动画效果打造出一个幻想的世界)

奥日与黑暗森林

1.帧

帧是我们在游戏中经常能听到的概念,在动画表示中,时间需要被划分为变化过程中独立且离散的单位,这一单位就是帧。,这一概念的出现给属性变化提供了机会————我们可以在某一帧内打开大门或是移动角色。但是每秒的帧数(即FPS)是因计算机设备而异的,通常较差的帧率意味着较低的性能。

1.1关键帧

虽然帧为我们的动画提供了改变机会,但并不是每一帧都需要改变,例如无人开启的大门将一直保持静止状态,直到有人用钥匙唤醒它,即大门有两个状态,开启状态和闭合状态,这两个状态变化时刻就可以表示动画中的关键节点,即关键帧。通常我们只要定义关键帧的动画,Unity会自动帮我们生成中间帧,平滑的补全中间动画,这一数学过程称之为差值运算,也是我们在Unity中常用的一种运算方法。


动画类型

从技术角度上动画可以根据实现方式的不同划分为不同的动画类型,我们先简要介绍他们,随后的章节我们会补全更多的知识。

1.刚体动画

刚体动画用于创建预制动画序列,通常不需要我们过多的人工干涉,直接利用Unity的动画组件进行配置即可。刚体动画多用来移动和改变对象的整体属性,如奔驰的车辆,沿合页闭合的大门,翻倒的大树等,均是作为独立的整体进行操作,在动画结束后,其内部结构和组成未发生任何变化,这一类动画可直接在Unity的动画编辑窗口加以定义,其中曲线中的结点为关键帧(变化帧)。

刚体动画窗口

2.骨骼动画

如果仅靠刚体动画,我们是无法表现人类的追逐跑动,独眼巨人霸气的一记重击,对此就要用到骨骼动画,这类动画通常不改变整体的位置,旋转和缩放,而会在帧间对内部组成和结构进行移动或变形。动画设计师往往创建特定骨骼对象的网状物,并以底层网状骨骼予以近似,进而对周边和内部几何体实现方便独立的操作。十分有效的模拟人物行走时手臂的摆动或是头部的转动。通常情况下,骨骼动画会在3D建模软件中完成,但是Unity也提供相应的组件进行设置,我们后期给予讨论。

一个简单易懂的2D骨骼动画教程 by Brackeys(youtube)

2D骨骼动画实例

3.精灵动画

这是对于2D动画,图形用户界面以及各类3D特效(例如水纹,刀光)通常要用到的动画,这类动画不会像刚体那样运动,其内部结构也不会发生改变。而是利用图像或帧序列以特定的帧速率播放,进而呈现一致的动画外观,例如2D动画中角色的行走,跑动,跳跃,通常是已经绘制完毕并放置与精灵表单中,在需要时直接轮换播放。

精灵表单

4.物理动画

在很多时候,我们希望动画呈现真实的效果并动态的与场景反馈,但很多时候是我们无法预知的,我们希望落石因为重力而下坠,希望气球因为浮力而上升,但我们无法控制所有的落石,气球。这是最常用的方法是采用Unity提供的物理系统,让其自动模拟真实世界的行为方式。

5.变形动画

有些情况下,我们对动画有更为复杂的要求,我们希望主角晚上看到月亮变成魁梧的狼人,希望青蛙变成帅气的王子,类似这些情况,我们需要将网格状态混合,或通过平滑方式从一帧合并至另一帧的不同状态中去,这一过程称之为变形动画,或形状混合,实际上,该方案依赖于动画关键帧之间网格的对应定点,以及中间帧不同形态见得混合结果,该方案计算量较大,对性能会产生一些影响,但可以产生精美切逼真的外观表现。

6.视频动画

Unity可播放视频文件,将视频转化为.OGV格式,即可将其作为资源文件。根据此类文件,Unity可将视频视为网格对象上的动画纹理进行回放。

7.粒子动画

动画模拟过程中经常会出现某些不具备特定形状的非实体对象,例如火焰,花火,雾效,云朵,这时候就是粒子动画的主场了。粒子系统作为Unity的重要组件有着很强大的作用,通常可配置出令人惊艳的动画效果。

粒子动画

8.可编程动画

一定时间内动画属性的变化来自于程序设计,即开发者针对特定功能编写的代码,这样的动画,我们称之为可编程动画。也是我们最常见的动画类型。当然多数时候,动画效果都是通过设计师制作完成,代码仅仅在运行期间对动画行为进行触发和引导。


编写代码实现动画

下面通过实例来解释Unity动画中一些重要的概念,一致性动画,运动向量,协同程序和动画曲线。这里我假设已经掌握了Unity的基础知识,懂得Unity中窗口和组件的名称所指,熟悉创建物体,创建脚本并编写简单代码。

一致性动画

我们创建这样一个2D场景(所有技术均可以扩展到3D项目),一艘宇宙飞船正在关卡中匀速前进。这看起来很简单,但是很酷不是吗。
创建我们的飞船(其实是一个小方块贴图,可随意更换),位置归零,调色,创建脚本Ship.cs挂载到飞船上,然后就如下所示:

ship

现在我们要飞船移动前进,修改update()函数,改变物体的position,让其每一帧前进0.05个单位,因为帧率很快,所以这个速度绝对不算慢:

    void Update () {
        transform.position += new Vector3(0.05f, 0, 0);
    }

move

但是这里有一个问题,我们知道update函数每一帧会调用一次,但是帧数是根据硬件和软件环境有所不同的,所以对于FPS为70的机器,1s他会移动70 * 0.05=3.5个单位,而对于FPS为50的机器来说,他会移动50 * 0.05=2.5个单位,用户的体验就得不到同样的保证,而且在对于多人游戏来说更是一种不平衡,必须所有玩家都处于同步状态!

解决方法就是一致性动画————速度,时间和deltaTime

根据上述条件我们知道,因为帧率的不同我们不能用它来表示物体的移动,我们按照现实世界的规律可以采用速度——距离——时间公式,让距离=速度*时间,对于1m/s的物体,5s就移动5米!这种思考就不会受到帧速率的影响。另外不同的计算机在时间上都是保持一致的,1s在所有设备上不多不少,这时就要用到deltaTime。

deltaTime表示为Unity本地变量,作为time类中的数据在各帧中被更新,表示了据上一帧所经历的时间,这是一个很重要的消息,当乘以速度后我们就可以对速度就行缩放,进而将帧率不同的设备上取得一致的速度。例如我的FPS大约为100,那么我这一次执行update距上次大约为过去0.01s,所以deltaTime就为0.01,我的速度会乘0.01,另一台机器FPS假设为50,那么它的deltaTime为0.02,速度会乘0.02,我帧率虽然是他的两倍,但因为缩放,最终效果是一样的移动距离。这就是deltaTime的神奇之处。(虽然看起来没啥变化= =)

fps

    //添加了速度变量,方便操作,因为帧率大约缩放了0.01,所以我的速度值乘以100
    public float speed = 5.0f;
    //...
    //用deltaTime对速度进行缩放
    void Update () {
        transform.position += new Vector3(speed * Time.deltaTime, 0, 0);
    }

deltaTime Move

(p.s 如果移动的很慢很慢,一定是你的Inspector窗口的速度没变,Unity会自动保存之前的值)

某一方向的运动行为

如果你看过星球大战,那么你一定知道真正飞船拖着炫彩的火焰尾巴在宇宙中盘旋,翻滚躲避激光,相比之下我们的飞船移动太过生硬了,肯定会被一发导弹就炸毁!现在我们要将他在任意方向上移动。

这里需要用到向量这个概念,相信你不会陌生,因为向量既有方向,又有大小,这不正好符合我们的速度吗。例如(1,0,0)代表x轴正方向移动,(0,-1,0)代表y轴负方向移动,我们甚至可以使用(1,1,0)表示在x轴和y轴的45°夹角方向进行移动。代码如下:

    //重新定义一个方向变量
    public float speed = 0.05f;
    //...
    //三者相乘    
    void Update () {
        transform.position += direction.normalized * speed * Time.deltaTime;
    }

我们在inspector窗口改变其方向,运行结果如下:

inspector

dir move

动画曲线

但是这还不是我们想要的,我们不想要直线运动,而是要让飞船在宇宙空间画一个完美的曲线!或者速度从快到慢进行一个过度。这时候就到了动画曲线出场了。

    //添加动画曲线
    public AnimationCurve AnimCurve;
    //乘以动画曲线
    void Update () {
        transform.position += direction.normalized * AnimCurve.Evaluate(Time.time) * speed * Time.deltaTime;
    }

这是我们Inspector窗口出现了可配置的曲线属性,点击打开曲线窗口就可对曲线进行编辑。曲线中横轴表示时间,纵轴便是曲线的值,可以点击左下角的预制曲线进行快速配置,也可以在曲线上点击添加控制点,我们以创建常用的曲线为例:开始时速度递增,结束时速度递减,运动1s,其中开始点,和结束点均位于0点便是速度为0,静止状态,中间1表示达到最大速度。
我们在曲线中心插入控制点拖动到1,两端控制点拖动到0,并移动控制点两端的线段使其平滑,通常右键设置Auto,Unity会自动帮我们平滑处理,更多的操作参考官方文档。最终效果如下:

curve

curve move

协同程序

下面我们应用协同程序实现一个常用的动画效果,朝目标平滑旋转。这在游戏里也是常用的技巧之一,例如RPG游戏中我们让前进的角色向右走,角色会先把身体转向右边再执行行走动画,如果在我们输入指令后角色突然面向右边了,就太诡异了。
以两个方形为例,我们让橘红色方块(假设是一辆坦克战舰)始终瞄准飞船。新建Tank.cs脚本如下:

    //瞄准的目标
    public Transform target;
    //旋转速度
    public float RotateSpeed = 100f;
    // Use this for initialization
    //游戏开始时执行协程
    void Start () {
        StartCoroutine(TrackRotation(target));
    }
    //旋转协程
    IEnumerator TrackRotation(Transform target)
    {
        while (true)
        {
            if (target != null)
            {
                //找到旋转方向
                Vector3 dir = target.position - transform.position;
                //转化为Quaternion
                Quaternion newRotation = Quaternion.LookRotation(Vector3.forward,dir);
                //应用旋转
                transform.rotation = Quaternion.RotateTowards(transform.rotation, newRotation, RotateSpeed * Time.deltaTime);
            }

            yield return null;
        }
    }

这段代码涉及的东西有些多,我们一一来说,IEnumerator即协同程序类似与多线程,多任务机制,可以与其他处理一同进行,对于动画而言,这是十分有用的特性,他至少包含一条yield语句,代表留空多长时间,上段程序yield return null表示我们跳过一帧然后再回到while循环。协程程序需要StartCoroutine()来开启,用法与函数没有太大不同。
至于Quaternion四元数我会专门用一章时间来介绍,因为旋转是Unity中十分重要的操作之一,而且理解起来也不容易,等我写好后会放在博客上。现在只需要知道,我们找到旋转方向后把方向转化为四元数(一种的旋转表示方法),然后让Y轴始终指向dir方向即可。
效果如下:

rotate

其实这些只是Unity动画系统中很少的一部分,涉及的知识也是很基础的知识,要想深入了解动画系统,需要投入很大的精力,但是别急,这次只是Unity动画基础知识讲解和一些实验,接下来我们一起学习其他的知识。


完整的工程项目在我的Github主页

欢迎大家与我交流学习

猜你喜欢

转载自www.cnblogs.com/acgstone/p/10098817.html
今日推荐