Unity之Animation

一、Avatar

Unity使用Avatar系统来识别布局中的特定动画模型是否为人形,以及模型的哪部分对应于腿、手臂、头和躯干,Avatar可将动画从一个人形角色映射到另一个角色,允许重定位和反向动力学(IK)。

1.创建Avatar

在导入一个角色动画模型之后,可以在 Import Settings 面板中的 Rig 选项下指定角色动画模型的动画类型,包括 Legacy、Generic 以及 Humanoid 3 种模式,如下图所示。
在这里插入图片描述

(1)Legacy

旧版动画类型,Unity4.0版本以前推出的动画系统,一般动画由Mecanim系统导入,但无法使用人形动画的专有功能。

(2)Generic

一般动画类型,支持非人形(怪物)动画,也支持人形动画,但它无法使用Humanoid动画重定向功能。即模型只能使用自带骨骼所制作的动画,不能给其他模型使用

(3)Humanoid

使用Humanoid(人形动画),如上图所示,点击Animation Type选项右侧的下拉列表,选择Humanoid,然后点击Apply,Mecanim动画系统会自动将用户所提供的骨架结构与系统内部自带的简易骨架进行匹配,如果匹配成功,Avatar Definition 下的 Configure 复选框会被选中,同时在 Assets 文件夹中,一个 Avatar 子资源会被添加到模型资源中。

二、Animation视图

在这里插入图片描述

1.动画时间轴

动画视图面板右边是当前编辑的时间轴,每个动画属性的关键帧都显示在此时间轴中,时间轴视图有两个模式:关键帧清单模式(Dopesheet)和时间线模式,点击对应按钮进行对用模式切换。

(1)关键帧清单时间轴模式

可通过框选方式选择多个关键点。通过此功能可一次选择和操作多个关键点。
在这里插入图片描述

(2)曲线时间轴模式

可通过框选方式选择多个关键点。通过此功能可一次选择和操作多个关键点。
在这里插入图片描述
(3)回放和帧导航在这里插入图片描述

2.创建新动画剪辑

可如下图所示建立:
在这里插入图片描述
可以在录制的情况下拖动录制动画的对象,也可在对象的属性面板里修改属性来录制动画,也可在动画时间轴直接创建对象某个属性,再在时间轴右侧手动添加关键帧,输入对应的动画数值。

3.支持的可动画属性

Float、Color、Vector2、Vector3、Vector4、Quaternion、Boolean。

4.曲线编辑

将动画视图切换到Curves,点击关键帧可以对其进行拖动,修改其倾斜角度,也可点击右键进行更复杂的属性设置。在这里插入图片描述

5.添加动画事件

如下图所示添加动画事件:
在这里插入图片描述

三、Animator Controller

在 Animator 窗口中可创建、查看和修改 Animator Controller 资源。Animator 窗口有两个主要部分:主要网格化布局区域以及左侧 Layers 和 Parameters 面板。带有深灰色网格的主要部分是布局区域。使用此区域可在Animator Controller中创建、排列和连接状态。左侧面板可在 Parameters 视图和 Layers 视图之间切换。Parameters 视图 允许您创建、查看和编辑 Animator Controller 参数。这些参数是您定义的变量,充当状态机的输入。要添加参数,请单击加号图标,然后从弹出菜单中选择参数类型。要删除参数,请在列表中选择参数,然后按 Delete 键(在 macOS 上使用 fn-Delete 来删除所选参数)。
在这里插入图片描述

1.动画参数

可以通过动画曲线更新参数的值,然后通过脚本访问参数来控制一些特定的属性值。脚本同样可设置被Mecanim拾取的参数值,例如,脚本可以设置参数来控制混合树。可在Animator窗口的Parameters界面来设置默认参数值,参数值分为四个基本类型:
Integer - 整数
Float - 带小数部分的数字
Bool - true 或 false 值(由复选框表示)
Trigger - 当被过渡使用时,由控制器重置的布尔值参数(以圆形按钮表示)
在这里插入图片描述
可使用Animator类中的函数,从脚本为参数赋值:

using UnityEngine;
using System.Collections;

public class SimplePlayer : MonoBehaviour {
    
    
    
    Animator animator;
    
    // 使用此函数进行初始化
    void Start () {
    
    
        animator = GetComponent<Animator>();
    }
    
    // 每帧调用一次 Update
    void Update () {
    
    
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        bool fire = Input.GetButtonDown("Fire1");

        animator.SetFloat("Forward",v);
        animator.SetFloat("Strafe",h);
        animator.SetBool("Fire", fire);
    }

    void OnCollisionEnter(Collision col) {
    
    
        if (col.gameObject.CompareTag("Enemy"))
        {
    
    
            animator.SetTrigger("Die");
        }
    }
}

2.状态机

Create Sub-State Machine创建,角色通常具有包含若干阶段的复杂动作。合理的做法是识别单独阶段并将单独状态用于每个阶段,而不是用单个状态来处理整个动作。允许您将一组状态折叠为状态机图中的单个指定项,这些折叠的状态组称为子状态机,若要创建子状态机,可右键单击 Animator Controller 窗口中的空白空间,并从上下文菜单中选择 Create Sub-State Machine。子状态机在编辑器中用细长六边形表示以区别于正常状态。
在这里插入图片描述

3.动画层

单击窗口右侧的齿轮可显示该层的设置。
在这里插入图片描述
在每一层上,您可以指定遮罩(应用动画的动画模型的一部分)以及混合类型。Override 表示将忽略其他层的信息,而 Additive 表示将在先前层之上添加动画。
可以通过按小部件上方的 + 来添加新层。

Mask 属性用于指定此层上使用的遮罩。例如,如果您只想播放模型上半身的投掷动画,同时让角色也能够行走、奔跑或站立不动,则可以在层上使用一个遮罩,从而在定义上半身部分的位置播放投掷动画:
在这里插入图片描述
Layers 侧边栏中显示“M”符号,表示该层已应用遮罩。

动画层同步可以实现不同层中复用同一状态机,例如,如果想要模拟“受伤”行为,并生成“受伤”状态下的行走/奔跑/跳跃动画,而不是“健康”状态下的动画,您可以单击其中一个层上的 Sync 复选框,然后选择要同步的层。随后状态机的结构便会相同,但状态使用的实际动画剪辑不同。

这意味着同步的层根本没有自己的状态机定义,而是同步层状态机的一个实例。在同步层视图中对状态机的布局或结构所做的任何更改(例如,添加/删除状态或过渡)都是针对同步层的源进行的。同步层的唯一独特更改是每个状态内使用的选定动画。

通过 Timing 复选框,Animator 可调整同步层中每个动画所需的时间(由权重决定)。如果取消选中 Timing,则会调整同步层上的动画。该调整会将动画的长度拉伸到与原始层上的一致。如果选中该选项,则动画长度将在两个动画之间平衡(基于权重)。在两种情况下(选中和不选中),Animator 都将调整动画的长度。如果不选中,则原始层将是唯一母版。如果选中,则采用折中方案。
在这里插入图片描述

4.Mute 和Solo

Mute会禁用过度
Solo仅播放该过度
在这里插入图片描述
可设置多个 Solo 过渡以仅播放启用了 Solo 的过渡。如果一个过渡启用了 Solo,则 Unity 将对其他过渡启用 Mute。如果同时启用 Solo和 Mute,则 Mute将优先执行。

控制器图并非始终反映引擎的内部 Mute 状态。

5.目标匹配

通常在游戏中可能出现以下情况:角色必须以某种方式移动,使得手或脚在某个时间落在某个地方。例如,角色可能需要跳过踏脚石或跳跃并抓住顶梁。想象一下,您想安排一个角色跳到一个平台的情况,并对这种情况已经有名为 Jump Up 的动画剪辑。首先,您需要在动画剪辑中找到角色开始离地的位置,注意在本示例中,此位置是动画剪辑中标准化时间的 14.1% 或 0.141,您还需要在动画剪辑中找到角色即将落地的位置,在本示例中,此位置为 78.0% 或 0.78。

using UnityEngine;
using System;

[RequireComponent(typeof(Animator))] 
public class TargetCtrl : MonoBehaviour {
    
    

    protected Animator animator;    
    
    //场景中的平台对象
    public Transform jumpTarget = null; 
    void Start () {
    
    
        animator = GetComponent<Animator>();
    }
    
    void Update () {
    
    
        if(animator) {
    
    
            if(Input.GetButton("Fire1"))         
                animator.MatchTarget(jumpTarget.position, jumpTarget.rotation, 
                AvatarTarget.LeftFoot, new MatchTargetWeightMask(Vector3.one, 1f), 
                0.141f, 0.78f);
        }       
    }
}

6.反向动力学

大多数动画是通过将骨架中的关节角度旋转到预定值来生成的。子关节的位置根据父关节的旋转而改变,因此可从父关节包含的各个关节的角度和相对位置来确定关节链的终点。这种构建骨架的方法被称为正向动力学。

然而,从相反视角看待构建关节的任务通常很有用:在空间中选择一个位置后,向后找到一种有效的关节定位方法,使终点落在该位置。如果您希望角色触摸位于用户选定位置的对象或让角色的双脚牢牢扎入不平坦的表面,这种方法可能很有用。此方法称为反向动力学 (IK),可在 Mecanim 中用于_已正确配置的任何人形Avatar骨骼。
在这里插入图片描述
要为角色设置 IK,通常要在场景周围放置与角色互动的对象,然后通过脚本(尤其是,诸如 SetIKPositionWeight、 SetIKRotationWeight、 SetIKPosition、 SetIKRotation、 SetLookAtPosition、 bodyPosition、 bodyRotation之类的 Animator 函数)来设置 IK

在上图中,我们展示了一个抓住圆柱形物体的角色。我们如何将其实现?

我们从拥有有效的 Avatar角色开始。

下一步创建 Animator Controller,使其包含该角色的至少一个动画。然后,在 Animator 窗口的 Layers 面板中,单击层的齿轮设置图标,并选中弹出框中的 IK Pass 复选框。
在这里插入图片描述
设置默认层的 IK Pass 复选框。

确保已将 Animator Controller 分配给角色的 Animator 组件 (Animator Component):
在这里插入图片描述
接下来,为其附加一个实际处理 IK 的脚本,将此脚本命名为 IKControl。此脚本为角色的右手设置 IK 目标,并设置角色的观察位置以使其观看所持物体:

using UnityEngine;
using System;
using System.Collections;

[RequireComponent(typeof(Animator))] 

public class IKControl : MonoBehaviour {
    
    
    
    protected Animator animator;
    
    public bool ikActive = false;
    public Transform rightHandObj = null;
    public Transform lookObj = null;

    void Start () 
    {
    
    
        animator = GetComponent<Animator>();
    }
    
    // 用于计算 IK 的回调
    void OnAnimatorIK()
    {
    
    
        if(animator) {
    
    
            
            // 如果 IK 处于活动状态,请将位置和旋转直接设置为目标。
            if(ikActive) {
    
    

                // 设置观察目标位置(如果已分配)
                if(lookObj != null) {
    
    
                    animator.SetLookAtWeight(1);
                    animator.SetLookAtPosition(lookObj.position);
                }    

                // 设置右手目标位置和旋转(如果已分配)
                if(rightHandObj != null) {
    
    
                    animator.SetIKPositionWeight(AvatarIKGoal.RightHand,1);
                    animator.SetIKRotationWeight(AvatarIKGoal.RightHand,1);  
                    animator.SetIKPosition(AvatarIKGoal.RightHand,rightHandObj.position);
                    animator.SetIKRotation(AvatarIKGoal.RightHand,rightHandObj.rotation);
                }        
                
            }
            
            // 如果 IK 未处于活动状态,请将手和头部的位置和旋转设置回原始位置
            else {
    
              
                animator.SetIKPositionWeight(AvatarIKGoal.RightHand,0);
                animator.SetIKRotationWeight(AvatarIKGoal.RightHand,0); 
                animator.SetLookAtWeight(0);
            }
        }
    }    
}

因为我们不打算让角色的手伸到物体内部中心(圆柱的轴心点),所以放置一个空的子对象(在此情况下,命名为“Cylinder Grab Handle”(圆柱抓握把手)),确保手应该放在圆柱上,并将其相应旋转。然后,这只手瞄准此子对象。
在这里插入图片描述
一个空的子游戏对象充当 IK 目标,因此这只手将正确放在可见的圆柱对象上

然后,应将此“抓握把手”游戏对象分配为 IKControl 脚本的“Right Hand Obj”属性

在此示例中,我们把观察目标设置为圆柱本身,因此即使把手靠近底部,角色也会直接看向物体的中心。
在这里插入图片描述

四、混合树

1.创建混合树

鼠标光标放到网格空白区域右键Create State->From New Blend Tree:
在这里插入图片描述
在这里插入图片描述
使用动画剪辑和输入参数来设置混合树时,Inspector 窗口通过图形显示动画如何随着参数值变化而进行组合(拖动滑动条时,树根中的箭头会改变其着色以显示其控制的动画剪辑)。
在这里插入图片描述

2.混合类型

(1)1D混合:

混合节点的 Inspector中的第一个选项是 Blend Type。此下拉选单用于选择不同混合类型之一;这些类型可以根据一个或两个参数进行混合。1D 混合根据单个参数来混合子运动。
在这里插入图片描述

(2)2D混合:

2D Simple Directional:最好在运动表示不同方向(例如“向前走”、“向后退”、“向左走”和“向右走”或者“向上瞄准”、“向下瞄准”、“向左瞄准”和“向右瞄准”)时使用。根据需要可以包括位置 (0, 0) 处的单个运动,例如“空闲”或“瞄准”。在 Simple Directional 类型中,在同一方向上_不_应该有多个运动,例如“向前走”和“向前跑”。

2D Freeform Directional:运动表示不同方向时,也使用此混合类型,但是您可以在同一方向上有多个运动,例如“向前走”和“向前跑”。在 Freeform Directional 类型中,运动集应始终包括位置 (0, 0) 处的单个运动,例如“空闲”。

2D Freeform Cartesian:最好在运动不表示不同方向时使用。凭借 Freeform Cartesian,X 参数和 Y 参数可以表示不同概念,例如角速度和线速度。一个示例是诸如“向前走不转弯”、“向前跑不转弯”、“向前走右转”、“向前跑右转”之类的运动。
在这里插入图片描述

(3)直接混合:

使用直接混合树可将 Animator 参数映射到 BlendTree 子项的权重。如果您想要精确控制所混合的各种动画而不是使用一个或两个参数来间接混合它们(1D 和 2D 混合树便是这种情况),这将非常有用。
在这里插入图片描述

3.Animator Override Controller

Animator Override Controller 是一种资源,它可让您扩展现有 Animator Controller,从而替换使用的特定动画但保留其原始结构、参数和逻辑。

因此,您可以创建同一个基本状态机的多个变体,但是每个变体使用不同的动画集。例如,您的游戏可能有各种各样的 NPC 类角色生活在世界上,但每种类型(小妖精、恶魔、小精灵等)都有自己独特的行走、空闲、坐定等动画。

通过创建一个包含所有 NPC 类型角色逻辑的“基础”Animator Controller,即可为每种角色类型创建一个重写,并放入各自的动画文件。
在这里插入图片描述

五、性能和优化

1.动画系统

(1) 控制器

未设置控制器的 Animator 不会花时间执行处理。

(2)简单动画

播放没有混合的单个动画剪辑会使 Unity 的速度比旧版动画系统更慢。旧系统非常直接,对曲线采样并直接写入变换中。Unity 的当前动画系统具有用于混合的临时缓冲区,并会对采样曲线和其他数据进行额外复制。当前系统布局已针对动画混合和更复杂设置进行优化。

(3)缩放曲线

动画化缩放曲线比动画化移动和旋转曲线的成本更高。为了改善性能,请避免使用缩放动画。

注意:这不适用于常量曲线(具有相同动画剪辑长度值的曲线)。常量曲线经过优化,成本低于比普通曲线。常量曲线的值与默认场景值相同时,常量曲线不会每帧都写入场景。

(4)层

大多数时间,Unity 都在估算动画,并将动画层和动画状态机的开销保持在最低水平。向 Animator 添加另一层(无论同步与否)的成本取决于层播放的动画和混合树。层的权重为零时,Unity 会跳过层更新。

(5)人形动画类型与通用动画类型

以下提示可帮助您选择具体类型:

导入人形动画时,如果不需要 IK(反向动力学)目标或手指动画,请使用 Avatar 遮罩 (class-AvatarMask) 将它们移除。
使用通用类型时,使用根运动比不使用根运动的成本更高。如果动画没有使用根运动,请确保未指定根骨骼。

(6)场景级别优化

可进行许多优化,一些有用的提示如下:

使用哈希而不是字符串来查询 Animator。
实现一个小的 AI 层来控制 Animator。您可以让它为 OnStateChange、OnTransitionBegin 和其他事件提供简单回调。
使用状态标记可轻松地将 AI 状态机与 Unity 状态机匹配。
使用其他曲线来模拟事件。
使用其他曲线来标记动画;例如,与目标匹配一起使用。

2.运行时的优化

始终通过将 Animator 的 Culling Mode 设置为 Based on Renderers 来优化动画,并禁用蒙皮网格渲染器的 Update When Offscreen 属性。这样即可在角色不可见时让 Unity 不必更新动画。

参考文献:
Unity官方手册

猜你喜欢

转载自blog.csdn.net/qq_45548042/article/details/120973774