zhouqijie

第四部分 动画片段

游戏动画与动画电影的动画不同,由于游戏是互动体验,所以游戏的动画几乎不可能制作成一串很长连续的帧。取而代之,游戏角色的移动都必须拆分成大量小粒度的动作,称为动画片段(animation clip),或者直接简称动画(animation)

每个动画片段都能令角色表现一个有明确界定的动作。有些片段还会设计成循环形式例如步行或跑步动作,其他片段则只会播放一次。有些片段会影响全身,有些片段则只会影响身体某些部分。

CRE:Unity使用遮罩使得全身动画只影响一部分身体。

例外的情况是游戏的非交互部分。称为游戏内置电影(IGC)非交互序列(NIS)全动视频(FMV)。注意IGC和NIS通常是指用游戏引擎来渲染的,而FMV则是先预先渲染至MP4或WMV等视频文件类型然后在游戏内播放的。

还有一种半互动的序列–快速反应事件(quick time event, QTE)。在QTE里,玩家必须在非互动序列的正确时间按键,才能见到成功的动画并继续,否则就会播放失败动画。

1.局部时间线

每个动画片段都可以有一条局部时间线(local timeline),在t=0时开始t=T时结束(T为持续时间)。t的每个值称为时间索引(time index)

姿势插值和持续时间:

在电影和游戏中,动画师都不会以每秒30次或者60次设定角色姿势。而是会在片段中指定的时间点上设定一些重要的姿势,称为关键帧(key frame),然后计算机会采用线性或者基于曲线的插值计算中间的姿势。

动画引擎能够对姿势插值,我们实际能在片段间任何时间采样。换句话说,动画片段的时间线是连续的,即时间t是实数而非整数。(动画电影一般不用考虑帧之间插值,因为帧率锁定在24、30或60帧。但是实时游戏几乎永远不会在整数帧采样,因为游戏动画的时间是连续且可改变比例的。)

时间单位:

如果定义了帧率那么也可以用作为时间度量单位,典型的帧持续时间有1/30秒或者1/60秒。(无论选择哪种时间单位都不要把时间t定义为整数)

Jason:帧在游戏引擎有多重意思。动画片段的”帧”一般指持续时间而不是时间点,时间点可用”采样”替代。

循环片段:

把动画片段设计为不断重复播放时,称为循环动画。循环动画的最后一个采样是冗余的,许多引擎会略去最后一个采样。

归一化时间:

有时使用归一化时间u是非常方便的,u=0代表开始而u=1代表结束。归一化时间又称为动画的相位(phase)

要同步两个或以上持续时间不同的动画片段,归一化时间就很有用。例如要想把一个2s60帧的跑步周期平滑淡入/淡出至一个3s90帧的步行周期,简单的解决办法就是把步行的归一化起始时间和跑步的归一化起始时间匹配,然后以相同的归一化速率推进,使两个片段保持同步。

2.全局时间线

正如每个动画都有一个局部时间线,游戏里每个角色都有一个全局时间线(global timeline)。其时钟在游戏开始或者角色诞生时启动。

可以把播放动画简单地看成把片段的局部时间映射到角色全局时间。

  1. 全局起始时间$τ_{start}$。
  2. 播放速率R。
  3. 持续时间T。
  4. 循环次数N。

$t = R(τ - τ_{start})$
$τ = τ_{start} + (1/R)t$

处理循环片段和非循环片段可以用clamp或模除操作。

3.比较局部时钟和全局时钟

局部时钟的方法在于简单,并且是设计动画系统时最显然的选择。
全局时钟特别适用于同步动画。

CRE:局部时钟同步动画可能因为各种原因出现延迟错位。

4.简单的动画数据格式

一般来说动画数据是通过离散地骨骼姿势采样得到的,一个采样由骨骼中每个关节的完整姿势所组成。

这些关节姿势通常存储为SQT格式。我们有时候称一个动画由每关节多至10个通道(channel)组成,是指S、Q、T的10个分量。

动画片段的数据结构:

struct AniamtionSample
{
    JointPose * aJointPose;//关节姿势数组
}
struct AnimationClip
{
    Skeleton * skeleton;
    float framePerSecond;
    uint frameCount;
    AnimationSample * samples;//采样数组
    bool isLoop;
}

每个动画是为特定骨骼设计的,通常不会用于其他骨骼。因此AnimationClip数据结构含有骨骼引用或者独一无二的骨骼标识符。
aJointPose的数组长度已经假定和骨骼的关节数目相同,samples的数目视循环与否一般等于frameCount或者(frameCount+1)

在真实的引擎中,动画数据不会存储在这种简单的格式中,而是会用多种方式压缩以节省内存。

动画重定目标:

一个动画通常只兼容于特定骨骼。如果多个骨骼很近似(除了有些骨骼含有不会影响主要层次结构的子关节),那么为一个骨骼设计的动画应该能用于其余骨骼。要求是需要引擎播放动画时忽略未能匹配的骨骼。

更先进的技术,可把为一个骨骼设计的动画,重定目标(retarget)至不同的骨骼。这是一个活跃的研究领域,可以查阅相关资料。

CRE:Unity的Mecanim动画系统提供人形动画重定向功能。需要配置Avatar把具体骨骼映射到通用的简化人形骨骼。

5.连续的通道函数

动画片段的采样其实就是用来定义随时间改变的连续函数。

理论上这些通道函数在整个时间线上是圆滑并且连续的。然而许多游戏引擎只会在采样间线性插值,那么实际用到的是原来连续函数的分段线性逼近(也就是折线)。

CRE:但是现在很多游戏引擎支持平滑连续曲线的编辑例如Unity引擎。

6.元通道

很多引擎允许在动画中加入额外的元通道(metachannel)数据。这些通道可以把游戏专用的的信息编码,同时能和动画同步,而无需把这些信息以骨骼姿势存储。

较常见的一种特殊通道是在多个时间点上存储事件触发器(event trigger),当动画局部时间索引经过触发区时,触发器事件便会发送至游戏引擎。事件触发器常用于播放音效或者粒子效果。

另一种常见的做法是提供一种特殊关节(Maya称为定位器)。定位器可以和骨骼关节一起设置动画。定位器的典型用法是绑定摄像机的位置和变换,就能在游戏中播放动画时移动摄像机。

  1. 纹理坐标滚动。
  2. 纹理帧动画。
  3. 材质参数。
  4. 光源参数。
  5. 其他随时间改变且需要和动画同步的参数。