GDC2016【荣耀战魂 For Honor】的次世代动画之路

发表于2016-06-21
评论0 7k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏美术行业精英群

167422913

 

SimonClavet

UbisoftMontreal

翻译 TraceYang  校对 钱康来

   本次的分享人是在UbisoftMontreal工作了10年的的动画程序员Simon Clavet。下图所列的游戏都是Clavet参与制作的。

 

  而现在Clavet所在做的是叫做【荣誉战魂(For Honor)】的游戏。


 介绍视频


  这款游戏在去年的E3上展出过,的乐趣是和伙伴们在战场里战斗,可以扮演骑士,武士或维京,有大量的角色可供选择。每个区域都像是一场非常真实的中世纪战争。有竞赛和多人的模式,以及一些可以很简单杀死的AI敌人。通常的当玩家发现另外一个玩家角色时,就可以锁定他,选择自己的立场,攻击或者阻挡攻击。战斗是慢节奏的,而不是那种毁手柄的游戏。你需要解读对手的攻击,攻击方式,轻击和重击的效果反馈不一样,可以用巨物或小刀攻击。

  下面的视频是4V4的对战。目标就是获取点数,地图里面有一个特殊点,可以干掉对手来控制。可以看到这些游戏里的动画,挑战的是有着舞步一般真实的精确性,关联着详细的信息,相信玩家会喜欢这个游戏。

   那么,从情感的立场上,Clavet他们的目标是真实精确的游戏体验,就像试玩里那样,玩家可以清晰的解读攻击,以通过学习来在游戏变得更加出色,通过掌握把握时机的能力,这样就玩的越来越好。但同时玩家需要非常可信任的动画,游戏世界比真实生活要更棒,但没有龙与魔法这些,是非常真实的世界,尽管现实中,维京勇士从来没有与日本武士战斗过。


 

  那么本次分享,首先会介绍一些动画系统的历史, 以显示我们的角色那些是可以做,那些是不能做的,接下来是Motion Matching这种新动画技术的介绍,然后会在编辑器中展示Clavet他们是如何工作的,最后会展示一些程序化的自动润色,让敌人的动画更加适应环境以及获得所需的精确的游戏体验。


 

  首先,Clavet展示了更多的最终效果,以此来聚焦角色在地图上的导航移动,然后会讨论每一个内容。下面的视频里,是女性的守卫(Warden),是第一个女性版本的类似骑士的战士。可以看到不同的动画过渡,有很多不同的细节启动和停止动作,可以说是有无限的停止动作,而且可以连贯播放和切换到休闲的状态。


 

  下面的视频是日本武士角色,行动相当敏捷,是细节很多的类型,可以看到都是全身运动,这里使用了全身动画(FullbodyAnimation),而不是那种上下分层的动画(LayerAnimation)。在一些其他的例子情况可能需要使用分层动画,但大多数的时候是没有分层,而都是全身动画的。 

  接下里是男性守卫,可以走动,慢跑,冲刺,当角色启动和停止时可以看到各种类型的反映,就像1米以内启动1秒内再停下,都是在动作捕捉的真实的动画变化,Clavet他们也希望可以在游戏里所有在动作捕捉摄影棚中所看到的动作。在视频里可以看到,到玩家锁定敌人后,人物会变得紧张,就像是意识改变全身的动作变化来切换姿态。可以从侧面攻击和格挡,角色可以在自己目标周围跳跃

  然后回到开始,来介绍上面的结果是如何开始进行的


 

  那在一开始Clavet他们创建了叫PlayAnim()的函数。用来让角色播放你需要的动作,在需要的时候可以在你的代码里调用这个函数。


 

  那么也就是像下图这样,为了播放启动动画(start),所以要PlayAnim这个动画,在结束启动动画的播放后,将会播放走动的循环动画(walk loop),这个是一个移动动画的组,不断的重复从开始到结束的动画。当不需要再走动时,需要播放停止的动画。


 

  虽然可以按代码这样来做,但Clavet明显并没有这么去做,因为希望能可视化的来表现,所以使用了可视化的状态机,让状态来进行循环播放,并做动画的转移。在箭头里进行两个动作的混合,或者进行动作的迁移,或是在中间的迁移状态。(这里使用了UE4的动画状态机做示范)


   

  开发者可以决定里面的结构,每种移动都可以在游戏里制作。因为状态机可以是分层,可以更加复杂,需要应对这些状态的迁移,而且Gameplay可以通过传送参数列表来控制状态机,状态转移的条件上利用这些参数,然后就通过参数,可以决定从一个状态迁移到另一个状态。或者用户可以通过一个Gameplay的信号来进行状态的迁移。(下面的图使用的是U3D的动画状态机做示范)。


 

 

  另一方面,我们来看下会产生的问题,每一帧都需要评估(evalute)这个函数,比如速度比较快,或者是按某个数值运行,或者想以较低但大于0的速度运行来播放行走的动作,或者速度为0来播放休闲的动作,所以需要每帧都需要进行评估,决策系统会在显示的决策树中进行决定。


 

  你可以将这些条件选项可视化。如果需要把动画混合在一起,就是要用动画混合树或者决策树。这个系统的想法,是对从右侧进来的动画,决定他们是在上面还是把中间的节点混合在一起。在到左边放到一个动画里。


 

  同样,也会有一组参数来决定如何把动画混合在一起,例如把慢跑和冲刺混合时,要去获取需要的速度,这样就可以选择用真实规则来决定使用哪个分支。


 

  这种方法还是不好,会变得相当的复杂,所以用不同的动画给Upbody和LowBody,像攀爬动画时就可以独立的控制。最后,这些具体的动画都对应成状态,状态机中的每个状态做成混合树的状态机。也可以在混合树的一个节点里包含一个状态机,一层层嵌套下去   。因为所有都是分层的,会变得像很大的蜘蛛网一样,并控制你的角色。


 

  那么,第一个问题就是,例如要进行移动到一个位置,那么要如何在基本性质上对这个移动行为命名,就像是这个移动的动画文件的名字,以及如何在整个结构体里来配置它。


 

  Clavet的示例动作是一个向前并启动(StartStrafe)再转身(Around),这里命名为StartStrafe90TurnOnSpot45Stop,这种不得不起名字的事情是并是实际想要的。如果要把这个放到混合树或者状态机里,那么只能祝你好运了。还是要说,首先在直觉上我们并不能处理这类疯狂导航的很麻烦的事情。


 

   再返回到动画混合树,下面的图里很多的节点,这里会有大量的动画,那么就要选择一种智能的方法来混合动画

 

  这里选择的方法称作ParametricBlend,通过一组多维数据来设置,例如,速度(speed),Strafe角度(strafeangle)和斜率(slope),可以用多个像这样的纬度,每个红色的点都是一个和其他动画混合的组,当Gameplay有特定的speed,strafeangle,slope时,会去查找一个动画的混合值来把这些融合在一起,来获得和需求接近的结果。 


 

  下图中,每一列都是一个纬度参数,动画师可以通过参数来设置值,每一行都是一个循环,假设每个都可以很好做混合,每个列的值并不是浮点数,而是离散的数值,就像是tag一样,来决定需要哪些动画的组。

  下面代码里,可以通过Gameplay中类似查询的方式,生成了一个AnimQuery 利用参数来查找,并增加需要的特性。在代码的示例中,找到了一些想要的动画,并按3米每秒的速度,斜度45度,以及90度的strafe angle,然后调用称作computBlendFactor的函数,会计算出每个动画是用多少值来混合在一起。来尽可能生成你想要的效果。就像是radialbasis函数,或者你想象成2D平面上的一个三角形,动画系统通过设置参数来对应三角形中的一个点,以三角形中的重心坐标根据这个点的位置来混合动画。


 

   接下来看下第2个问题。


 

  首先先看下视频动画,角色开始移动,然后有一点转向,这里的问题是,如何通过一种统一的方式来处理动画循环和过渡。从直觉上看,动画循环没有什么特别的,会和结束的动画混合,或和开始的动画混合,感觉就像是在过渡一样。它是从混合一个起始动作开始,然后不断的循环播放它。视频里另外可以看到的是,一些点的启动动画变成循环,当要完成启动并变成循环时,真的很难正确的解决。当必须要精确的去决定,何时启动动画结束,何时循环开始时,并不能很好的去决策制作。也就是说,决定的并不是你实际像要做的结果。 

  其实这个问题在15年以前就有人提出了Motion Graph。他的想法是通过一个很长动画的非结构化列表(Unstructuredlistof animations)。预处理方面,你可以在动画数据库里预处理,对每个位置都找到它可以跳转到的其他位置。这样就可以设置在姿势匹配的位置。如果姿势可以很好对应的话,速度也几乎相同的话,那么就有了动画的过渡点。

   

   

 

  以下图为例,你的动画在任何时候都可以开始动画过渡,然后通过一个可能性的列表来决定移动到哪里。


 

   那么可以在这些动画中漫游,并按图表中的路径提供动画。

 

   接下来第3个问题是,如何选择下一个动画。

 

   这里有一些方法可以选择的,其中一个是方法是利用机器学习的概念中增强学习(reinforcementlearning)的方法。通过预处理,可以观察一个动画过渡到特殊目标的表现是否足够好,为每一个需要转化的离散方向的目标进行预处理操作,譬如说你要往左转,那么将动画切成一小块一小块,然后判断每一小块到转向的变化是否足够好。

   Michael Buttner在3年前的讲座里讨论过他在【杀手5:赦免(Hitman: Absolution)】中使用的相关技术。而他现在也在多伦多并受雇于育碧。他在工作室中开发他自己版本的动作匹配(MotionMatching),实际上在育碧里4到5个不同的项目组,也正在研究这类技术。

   

  但这种方法的问题在于,为了能舒适的控制,需要非常高密度的图表,因为通常要有各种动画过渡的等待位置。所以并不能真正的立刻的切换到需要完成的动画。因为之前是离线的采样点,所以在真正切换前往往会稍微等一下,才会真正的变动画。这也是为什么这个管理只在NPC上使用,因为不能足够的反映主要角色。


 

  所以,Clavet他们使用了Moition Field,Moition Field不是什么很酷的论文,是10多年前的了。文章的内容看起来很厉害,让你充满动手试试的欲望,文章提出了一种将运动数据泛化到高维矢量场的方法,被称之为motionfield。在实时计算的时候,根据用户输入在motionfield中随意合成即可。听起来这可以是一个解决方案。就像是一个计算每个可能目标的估值函数,来告知如何在动画组成的高维魔幻世界里游走。


 

   

  这样就可以获得更多的反映,在下面视频的左边是图表方式使用静态图的解决方案(Graph),可以看到会出现我们讨论过的反应上的丢失,只有一部分时间角色会实际的跟随箭头。视频右边是运动场的版本(MotionField),有更多的运动响应,可以把动作融合在一起,并且快速的过渡,可以打断当前动作并和其他的动作混合,真正的是在运动场中流动。


 

 

  那么这种方法的问题在哪里呢?运动场(MotionField)并没有像其他技术一样被广泛使用。当你读这篇论文的时候,会感觉这个听起来有些复杂,要达到真正需要效果所需的复杂度等级并不是你所像要的。这个技术感觉起来有些过度了,虽然能运作,但要让他可以运作实现起来太难了,这也可能是它没有使用到游戏里的原因。也可能是它性能问题或内存问题,但Clavet觉得主要还是因为他复杂度上的问题。所以要实现这些可能并不需要像它那样那么复杂的函数,可能是更加贪婪的方法,用我们所需要更加简单的方式来处理这些动画片段。 不需要像原文中使用那么复杂的距离函数,简单粗暴的解决即可。


 

   所以,Clavet他们只是使用这篇论文的主要思路,实现方式非常简单但又很有效果。那就是需要的时候,可以随时跳转到任意其他的帧上。这其实就是最关键的核心点,只需要找到一些数据片段,就可以正确的播放动画了。

 

那么是最后的一个问题。

 

  最后的一个问题是复杂的停止动画(stopanimation),最后一步细节或者最后发生的一些细节事务。那么,当要开始播放停止动画时,如何在停止动画里选择正确的开始时间?角色是循环播放动画来做相同的跑动,处于一个特定姿势,当这时要开始播放停止动画,需要在停止动画里找到一个看起来相相同的位置来开始播放。

   首先要做的是,需要播放哪个停止动画,可能在游戏中你的版本是左脚向前,或者是右脚向前,那么可以在停止动画的最开始播放。当姿势匹配(posematch)时可以精确的开始播放动画。用这种方法当姿势匹配时动作开始和结束都会很精确。速度匹配(velocitymatch)上,因为有时你刚刚开始启动,正在播放启动动画时,刚进行到一半时,并不是全速在移动,这时要开始播放停止的动画,这时就不能播放完整的停止动画了,因为你的速度会边快,然后再减速,这样感觉起来不好。所以最好是可以推测速度,让停止动画在所对应的位置开始播放。

   还有就是需要精确的停止位置的匹配,需要在游戏世界的一个精确位置上停下来。那么就要播放和开始走步,以保证可以到达正确位置。或者就是要把上面这些做混合,这就是为什么接下来你会看到,我们将评估动画中的不同权重,并根据代价(Cost)来找到最佳的动画。


 

 

   接下来,是今天要正式介绍的MotionMatching,前面讨论了不同的版本MotionMatching,现在要清晰的介绍它实际工作和和它了不起的地方。   

 

  实现方法是非常简单蛮力的方式来做动画的选择。我们从这个方法出发,就可以看到这是非常简单的。



  方法非常简单,就是在每帧查找所有的动补数据,并跳转到最佳的位置上。


 

  实现上,每个候选的跳跃点都有一个成本值(cost),如果候选的点和当前的姿势完美的匹配,而且后面的动作片段就是我们想要的,那么它的代价就是0。这里计算cost的函数有两部分,像当前的姿势和速度,之后的动画片段内容也是要进行匹配的。


 

  那么,在算法的输入上,是一组动作捕捉数据,测试动作是"verytired",真的要是full body的去匹配,很难分割成小的片段。所以Clavet他们不需要手动的去为启动,停止以及转身动画进行分段。这是尝试的第一步是。3年以前,Clavet只是直接把这些动补数据塞入到了游戏里,每段5~10分钟的动画。然后通过算法,每个相似帧的来选择小的动画片段来播放。

   下面的视频是最后的结果,可以看到角色真的是在尝试跟随着红色的箭头。

   下面的视频是关闭混合,可以看下切换时的动画,切换动作很大(跳帧)。但当我们切换时混合一些时间的话,表现就好多了。视频里后半部就是打开混合的,模型开始跟着变化的方向不断变动,有个渐变的过程。

   下面视频是行走的示例,视频左边的一套行走的动作,右边的地面上,红色的路径是希望走的轨迹,通过Gameplay在每帧来传递这个信息,而蓝色的路径是当前播放的动画,可以看到每次蓝色路径切换了,就意味着动画切换了,


 

 

  当我们要进行动作捕捉时,在游戏里要让它可以像前面那样工作的话,这里有很多的角色,不是只有一个日本武士,一个维京勇士和一个圣骑士,还有大量的其他角色。所以要增加更多的动作捕捉,要捕捉特殊风格的角色,例如女性版本的角色,随着我们做的越来越经验丰富,最后需要大概15或20分钟,一组镜头大概有6到7个左右。


 

  下面的视频展示了剑兵的放松(relax)动作,以及转头的放松动作。用来做放松动作的导航。这里从一组原地转身(turnon spot)开始,按45度来捕捉。然后是中断开始动作(Interrupted Start),这个是从放下木棍开始的。


 

 

  然后对于一组位置要进行一些重新的定位(Reposition),这里是没有转身因素,以确保可以获得游戏世界里的位置。 接下来是有位移的动作,这样就有了每个方向的启动和停止的动作:从45度(Plant45)开始(其实就是跑动过程中同时转45度),接下来是135,90,180。对于攻击动作来说,需要前进后退和左移右移,以及斜的前进后退(类似对角线)。

   然后是一些代码的展示,这个实际上相当的简单,Clavet也相信听众可以回去试试。


 

  那么要保存当前动画索引(CurrentAnimIndex)来指定播放什么动画,以及在动画中所处的时间(CurrentAnimTime)。然后每帧会调用称作AmoUpdate的更新函数,这个是在代码称作Amo的系统,因为Amo全称是automaticmotion。在函数里每帧传递一个Goal,以及dt(DeltaTime)来指示要前进多少。后面会介绍Goal的实际结构。Goal里是Gameplay的所有信息交代给动画去做的,是个很小的信息Pack,小的数据结构来告诉函数下一秒需要什么。

  所以接下里,在当前的动画时间(CurrentAnimTime)里加上dt,然后来评估当前的姿势(Pose),这个pose实际上类似指定的动画索引在指定动画里时间的插值(Lerp)。在这个姿势时,也有一些数据的片段,可以让计算cost的函数更快。例如要Pose Matching,需要一些游戏世界里骨骼的位置,那么在这个姿势下,会预计算这些信息,后面将会提到Pose里更多的细节。不过要记住的是里面有我们计算Cost函数需要的所有信息。所以这里调用函数通过数据来评估,因为预计算的姿势,一秒预计算十次,相当于有一个10Hz的动作细节密度。然后在相邻动作直接插值,你也可以像普通的动画一样直接使用。

  那么接下就是循环所有的Pose,并记录下最佳cost的数据和最佳的Pose数据,你要做的就是循环全部的数组,并评估每个Post作为候选pose,然后是候选Pose,你可以使用ComputeCost函数来评估候选Pose,提供当前Pose和候选Pose以及Goal来计算。如果ThisCost小于Bestcost,那么把它记录下来。大致上这个是怎么工作的介绍,稍后会提到它是如何组织的。逻辑实现上就是尝试去寻Cost更小的动画。


 

  通常上,最优候选动画和当前动画并没有差非常多时,比如一个人快要赢了,就挑出当前的动画,继续播放下去,如果要停留在动画里, 就尝试去做的就是胜利者在相同位置,也就是有相同的动画索引(CurrentAnimIndex),当前动画时间(CurrentAnimTime)与胜利动画的时间也足够的接近。如果不在同一个位置的话,那么可能就是在动作捕捉中的某个地方,那么就要使用PlayAnimStartingAtTime这个函数,就是在用某个时间播放动画,同时用0.25的混合时间,也可以是0.5或0.4什么的。


 

  接下来讨论的函数是Cost计算(ComputeCost),它使用当前姿势(currentPos),候选姿势(candidatePos)以及目标(Goal)来计算,这里分为两部分,第一部分是计算当前的cost(ComputeCurrentCost),这个计算不需要Goal做参数,只用currentPos和candidatePos。接下里会更多的讨论一些里面实际的内容。


 

  只需要比较少量骨骼的位置一致就行,因为像跑步这种动作,不断循环的,如果去比较每一个骨骼没太大意义,只要他还在循环其实就够了,而且对于起始、终结动画来说很难比对的,只要比少量的骨骼足够,而且也方便。譬如跑步的时候会抬脚两次,这是问题非常难处理。


 

     那么是要匹配哪些姿势和速度(Pose /Velocity),首先需要匹配的是局部速度(LocalVelocity),然后是脚的位置(Feetposition ),比如角色的全局空间,或角色的局部的位置和速度(Feetvelocities)。还有就是武器的位置也是可以匹配的,稍后会讲到连击(Combo)的问题。比如有两个版本的攻击动作,其中一个攻击是从某个空间的某个姿势开始,而另一个攻击动作是从另外的姿势开始,那么只有通过武器的位置,才能找到是取得哪个变化,来设置姿势匹配武器。

  这个Trick还可以按需求加入很多需要去匹配的事项,可以匹配你需要的所有事项,比如有时会需要头部朝向。另外这种方法可以通过预计算来节省消耗,比如在内存中更加紧凑,比如相同的位置,来更高效的循环,这样可以非常快速的计算,因为所有的数据都已经有了。这样做的原因是像使用的动画的格式通常是局部的角度,这样就不必处理全部的结果,不需要为计算骨骼动画的结果来得到脚的位置和速度而在循环中去消耗计算。


 

 

  接下来的视频里,可以看到关闭姿势匹配后的效果,脚步一些动作产生的问题,因为没有考虑脚的位置,所以播放起来会找不到在动画循环里的位置,当要切换动画时,并没有脚可以移动到哪里的概念,所以也不能维持整个循环。


 

 

  下一个部分是如何去跟随我们想要的轨迹。可以试着增加一个抽象的值,比如希望的速度,角度,或者其他可用的比如速度,方向。因此可能是需要抽象一些事物,但这个并不是Clavet他们实际所做的。


 

  实际需要做的是去和和在地面上的移动轨迹匹配,Gameplay会提供需要你去匹配的轨迹。这个Trick就是只检查如果播放动画,动画片段会把你的角色带到哪里。根据预计算的信息计算一小段即可,不需要比较完整的动画数据。然后这个方法会把动作捕捉的每个小片段的信息,把让动画可以与你的实体在游戏世界中对齐。要做的只是移动一个旋转角度,然后再次旋转来匹配你的角色实体。


 

     要的是延迟时间(TimeDelay),通过一个点的列表,可以设置未来的一个特定时间里希望的路径走向。在Clavet他们的案例中,使用的是3个或4个的较小的数量,不是10个也不是1个点。譬如4个点分别代表2s后,4s后,6s后这样-。

   然后在Goal的结构里可以加入更多的信息,例如游戏中姿势(DesiredStance)的概念很重要,意味着可以在goal里获得更多信息。


 

  下面的视频里,一个是获得未来位置(futureposition)的Cost,这里的不同就是红色的是我们未来要去的位置,可能是1秒后的。而蓝色路径是表示当前动画的未来走向。你可以调整让它更平滑一些,这里cost函数是当前动画的点和GamePlay希望它所在位置的距离。另外还可以看下未来的方向(futureorientation),可以和角度对比,让动画到达需要的地方。也可以比较速度(velocity),这样你就可以开始匹配一个完整的地面Pattern,从静止到开始跑起来这样的一条曲线都拟合上(同时拟合position+velocity)。


 

 

  然后要加入的是计算Cost的函数(ComputeFutureCost),也就是第2部分,可以增加一定运动片段来匹配出想要的导航。这里计算Cost并没有使用当前的姿势(CurrentPose)做为参数,而是用的动画中将要所处的候选姿势(candidate pose),这个候选姿势是评估得到的向前动画。如果可以获取向前的位置和速度,这样就可以避免重复计算;当然你也可以根据pose信息重新计算未来一段时间内的位置和速度。

  有意思的部分是响应率(responsivity),通过这个,可以用滑块在真实性(realism)和反应性(responsiveness)之间选择,可以一直调整直到调出动画师喜欢的真实性与设计师喜欢的舒适度(Comfort)为止。


 

  而且还可以匹配更多的东西。


 

  是个站姿,但所有的角色都有不同的站姿,只能手动的去标记这些。所以能做的就是真正的去检测姿势,而且感觉比较简单,Clavet并不鼓励用自动检测来做的。但这个是非常值得做的。

  而这里的Trick是,当评估一个候选位置时,在Goal里指定需要到右侧的站姿(Right Stance)里。因为你现在是Left Stance,而且未来希望变成Right Stance,那么具有这种状态切换的动画就有一个较低的cost,并匹配你的现在的战立动作,自动的找到过渡动画。


 

 

 

  那么下面的视频就是结果,有很好的转身(Plants),可以切换站姿,trick是如果忘记特殊的过渡,那就只是做混合,如果回到动作捕捉工作室,就能捕捉更多,再加入这些新的过渡动画,就可以不用混合来解决问题了。当它们存在了就要继续查询,从而避免维度激增,如果覆盖的范围内有漏洞,就要有很多的维度,这样才不会在覆盖空间有角落,需要可以处理这些漏洞。


 

  接下来非常简洁的来介绍下优化部分,这部分技术发展相当积极,角色在远处就不需要每帧都更新。游戏里的情况是,我们只需要持续的播放角色动画,动画最后留点富余量,不要一下就真的完全结束,这样你可以在这个动画播放完了之后继续播放一小段,因为你更新的没那么频繁。你可以继续的播放你的动画。同样,如果要管理这些在cost函数里使用的事物的位置,比如传递的局部空间位置。cost函数用的那种欧几里得距离的话,就可以像在3D空间里找最近点一样,通过KD-Tree来加速,找到最接近的就是实际要找的最佳的候选动画。这里还有一些其他可以做的优化,这里虽然没有讲解太多的细节,但在多伦多工作室的版本里实现了很多内容,他们对每项都使用了不同的小的策略来优化,这个在后面会提到更多的细节,另外以后可能也会有另外一个主题专门讲这部分。


 

  接下来会讨论更多关于通过Gameplay来生成轨迹的细节,前面也提到了Gameplay会传递信息告诉我们如何制作轨迹。但之前也需要提到动画的位移(Displacement), 如果你要通过动画生成位移,那么所有的游戏动画引擎都需要去进行确定,再播放它。然后在游戏世界里实际的移动角色。

  或者你希望通过代码模拟来获得位移,然后再把动画设置到他上面。这些都有很好的理由。


 

  然后Clavet他们希望可以用代码来决定真实模拟点,这个是很抽象的,就像是通过控制器和位置来进行战斗设计。这个对Clavet他们来说是很糟糕的,因为做习惯动画的人不习惯这么干,我们需要使用它,但区分非常重要,这个方法和蒙皮(Skin)有点像。

  那么,我们有骨骼的实体(Entity)有一个模拟点,它需要尽可能的接近在一起,但并不需要非常完美的一起。


 

  所以这里使用了一些方法,模型被简化成了像导航系统里那样的。看起来像是加速度,可以获得较大的速度改变上的硬直。这里的方法就是严格的Springdamper,这样可以平滑速度。那么角色的damper就就可以平滑速度变化,它的硬直就像是位置3D衍生的,这里叫做jerk(硬拽),在状态的数学术语里就是加速度的变化。相当于我们以多快来增大速度,那么因为可以clamp最大加速度,需要用2到3个数字来调整模拟。实际上相当简单的,玩家很容易理解,会很满意这种方法。这里展示的使用的比较多,但真的相当好理解。启动的时间和停止的时间几乎一样保持一致,虽然感觉起来有一些懒,但很容易理解,玩家也可以预测自己在哪里停下,这样实际上更好,目标尽可能的可预测,玩家可以了解角色是如何运作的,这个模型并不真实,但是能够轻松的让玩家学会,这样他们就会预测其行动轨迹。

  像FPS游戏要求响应及时,所以硬直这种东西要尽可能少,但这个游戏更加偏重可预测性,玩ForHonor的时候看别人和看自己的角色,不会出现急停这种不可预测情况。


 

 

  然后就是根据模拟来选择动画。而且还可以再做一些位置(Displacement),


 

  这就是当游戏角色尝试跟随地面是的一个点时所发生的。这就是需要的轨迹,而我们要试图跟随它。这个Trick就是仅仅Clamp角色实体距离模拟一定的范围。Clavet认为这种方法中距离设置为15cm,当角色在休闲状态(Idel),或刚好在附近休闲。你的实体处于你的质心,因为这是混合发生的地方,只需要计算角色质心的近似值,确认你的角色在那里。

  所以如果当角色处于Idel时要这么做的话,那么至少要处于模拟的一定距离之外,而当角色在附近跑动时,从一端跑到另一端,角色会有一个小的脚部空间,这样会有特殊的位移风格,但模拟点还是在直线上的。这点是非常重要的,这非常接近模拟点,这样会让玩家觉得游戏是真实的。如果有人击中你的角色,你被攻击时,仍然可以模拟点受到攻击。如果角色距离太远的话,那么被击中的感觉就很不好。


 

  还有一件可以做的是未来轨迹的预测。角色可以通过运行模拟代码预测未来的在碰撞物周围的轨迹,可以预测在墙边以及峭壁旁停止,在墙边沿着墙角正确的转身。在下面视频的案例里,通过运行模拟,角色靠墙等待了一会,然后找到如何在稍后转身的轨迹,在到达墙角前,都是准备好切换到将来要转过墙角的动画,得到了非常漂亮和光滑的结果。


 

  下面的视频里角色在到达墙壁前步行停顿了少许时间,往回走了一小段,然后就像之前所说的那样非常平滑的转身,然后在到达峭壁前再行走一段。


 

Workflow 工作流 

  接下来讨论的仍然是MotionMatch,但更多的关于在编辑器里处理工作流方面的细节。


 

  提供给动画师和设计师的管线,首先是把动作捕捉数据进行调整后导入,做标记,可以更加清晰的确认,更加简单的读取,更好的定义关系。然后到编辑器,用一些事件来标记它,这里会更多的讨论一些的。这里的想法是,在运行时由Gameplay来提出要求(Request),也就是之前提到的Goal,有时也叫做Request,和Goal Query是一个东西。Request的可以是需要的轨迹,或者是之前提到站姿(Stance),以及关联的事件的查询。事件查询(Event Request) 也是刚才提到了一些细节,它的思路是,如果不查询事件,说明是要使用导航,如果需要事件,那就像是未来的计划,描述希望要发生的事件类型。然后就相当于两者混合,譬如希望若干秒后在某个特定位置,并同时考虑导航。然后匹配系统输出的动画会不断的查找数据片段中最佳的动画来播放。然后我们修改结果,让它更加精确的与Gameplay和环境相匹配,然后还有一些适应IK的操作?,也会对实体的旋转进行校正。


 

  但仍然还是需要一些状态机的,Clavet之前没提到是因为他认为只有一个状态机是很好的主意,如果需要在动画状态机和逻辑状态机之间选择,那么逻辑状态机更好一些。动画状态机更加扁平,没有结构;逻辑状态机结构性更强。这里使用了简单的定时不分层的状态机,将动画Clip切出逻辑Clip,比如攻击,格挡的时机,由战斗设计师通过游戏规则调整。然后游戏生成Request,Montion Matching会尝试查找可以匹配它的动画。


   

  下面的视频是连击(combo)的示例,先是轻击(light attack),然后是重击(heavy attack)。可以看到通过几个动画Clips,进入到Miss然后切换到下一个,因为这时候按了一下按钮,就走到另一个分支里去了。在先前的clip,分支变为true,因为它在代码里的回调条件成为true。在下面视频的案例里,敌人格挡了玩家角色,这个时候角色就要进行动作过渡,就像视频中所看的分流到了攻击的恢复动画上,每个攻击动画都有对应的恢复动画,也可以直接恢复到攻击上。所以你的角色可以格挡,可以击中敌人,或在击中墙壁时中断。这个例子里角色变成了格挡,恢复动画的时间长度可以调整,实际游戏中经过的时间是灰盒的有几百毫秒。可以试着修改时间窗口的位置,就像Clavet增加Clip land那样,玩家切换站姿和躲避的时间变长,会用更长的事件来恢复。


  

  那么,通过这些动画,动画师的工作就是放置一些事件,这里的想法是不能把事件放在移动的开始或者结束位置,必须要放置在移动最重要的时刻,按惯例来确定。那么在这个案例里,武器关联的时刻就是事件的位置。在同一个动画里有多个事件,动画里并不仅仅只有一个事件的,可以有多个。在mocapfloor(啥意思?),可以让这个角色战斗,可以连续的记录并有多个事件在里面。


 

  然后Clip里指定事件某个特定的时刻发生,就像是下面视频里Clip上浅黄的标记,这是个永不会休止的Request,将确定事件在这个时间发生。


 

   事件有自己的类型,通过类型从Clip来获取Request,同样事件上还加入了一些关联(constraint),就像tag,来表明目标的类型是什么,以及其他的一些约束,譬如very tired状态下就只能有一些特定动作。如果要在动画的每个事件加入同样的关联,那么可以把事件放置同一个动画层级。在下图的右边的列表,可以是同一类的关联,可以通过参数来对动画分组,方便动画师来寻找。可以像下图这样,有很大的动画组,然后把一个大组在分割成多个子组(SubGroup),并共享相同的参数,里面可以有更多的事件,所有的事物都摆放在里面。


 

  移动位移的速度也有调整,战斗设计师希望有更精确的控制,例如攻击的范围非常的重要,所以希望可以很简单的调整它。


 

  那么像下面视频一样,在普通的导航模式可以改变移动的类型,如果改变速度,可以看到动画的切换,是从一个很大的动画列表中选取的。在走动时,如果速度设置为0,就会变成闲置的状态,所有动画都在一个Clip里播放,在角色启动和停止时不会切换Clip。那么在攻击时,需要设置攻击范围远近,通过调整参数,可以范围稍近或稍远,通过范围的改变可以切换成选取的动画。


 

  下面是其他的案例,当Gameplay信任动画时,可以获取“移动曲线(movementcurve)”,使移动复杂化。下面的视频里角色移动并把敌人摔出,是相当复杂的移动,你需要同步两个角色稍微在一起。在这个案例里,会通过动画做一些位移的版本,因为Gameplay允许的。因为动画很复杂,所以GamePlay就直接让动画来控制位移,自己不管了。

ProceduralTouchups

  然后还有一些程序化的修补工作。


 

   其中一个是角色实体的方向校正,这里是通过动画来决定转身的旋转角度,以及何时开始转身,当开始往回跑并转身时,让动画来决定,会有风格的问题。但我们可以修正它来和未来的目标匹配。例如未来所需要的位置或朝向(futuredesired position / orientation)。然后使用旋转校正来修复一些问题。


 

  其中的一个问题就是,当角色像下面视频那样跑动时,通常在动画和模拟之间会有一些混乱,有一些角度上的问题,因为没有足够的时间把所有动作都捕捉下来,所以插值出来的旋转会有些问题。


 

视频27

 

  这里的使用的方法实际上相当简单,看一下模拟的将来要去的位置(红线)和动画要把角色带到的位置(蓝线),那么可以随着时间来旋转以匹配将来的位置。


 

  通过这个方法,可以看下视频里地面上蓝色的pattern一直都有小幅的旋转来修正错误。


 

  当角色Strafe Round和折返跑时,角色在未来几秒的朝向会比未来几秒的位置更重要,这种情况下,要旋转角色实体来匹配未来的朝向。例如是很大的一段帧动画。如果角色动画把角色带到这里,而目标在另外一个地方,那么要一直对角色旋转和对齐来校正朝向方向的校正。


 

  也可以程序化处理所有骨骼的旋转,可以只对上半身(upperbody)做校正。


 

 

  如果希望角色跟随一个特殊速度,就像视频里,是下半身顿一下,来校正速度。让速度保持在校正的方向上,就好比角色动画让角色走到了一个地方,但你的目标在另一侧,下半身会稍微旋转、顿一下。


 

  然后讨论的就是时间缩放(Timescale),如果这里Gameplay需要让动画速度加倍,这里实现Timescale的方法是很简单。


 

     你可以看到错误的预测,然后可以根据错误率(errorratio)Time scale动画,


 

  在下面的视频里,这个案例得到了相当快的结果。Clavet认为这可能这并不是非常好的方法, 不能把时间调整的那么多,他认为10%或20%以内是可以的,但是再多就不行了,滑动可以获得更好的结果。需要通过滑步(直接平移一下位置)来解决混合动画和predict position一致的问题。


 

  这点在我们的案例是非常重要的,因为有大量的滑动,因为要经常的混合,要经常的切换动画,在混合结束前,不得不等待来进行再次的动作切换,或是其他的动画。我们要经常的切换动画,这样就会有大量的滑动,那我们就需要通过Gameplay来保持,Goal里不同的速度要求,只能尽可能匹配。这里捕捉动作的时候速度是一个,就不太好变化多个。(因为捕捉的动画速度一定,但是游戏里速度不一定会不匹配->产生滑步->如何避免滑步)。


 

     Clavet对他们的方法是并没有介绍太多,这个可以做一个报告,但并不需要这么处理,只是在不能移动太多的时候,在主动画里锁定了脚趾,这个主动画可以是输入动画或者是有最多权重的,然后锁住脚,并放置一个插槽(socket)在这里,然后使用IK把角色移动到推测的位置。这个实现里有大量的细节要确认,不能打破姿势等等,有太多要讨论的,还有就是不能打断腿。虽然最后还是有一些滑动,但比之前还是少多了。


 

 

  然后,角色可以在斜坡和楼梯上使用武器。实现的方法是,可以投射一些线,找到地面,Clavet他们的方法是,并不用关心斜坡,不管角色的是否被锁住,不会去做轨迹的预测,具体玩家做什么奇怪的动作都不管,所有的移动会获得变形(Warp),就像是在一个虚拟平面上,非常的平滑,做的还是和平地一样的动画,但在显示上做了Warp。 


 

  这里尝试把这个处理显示在画面上方,可以看到得到了Tragetground,然后把它变平滑,然后小心的调整地面,角色就可以在全部的轨迹上行走。可以看到所做动画并不是很完美,还会有很多拙劣的现象发生,但动作确实平滑,而且也可以维持角色的轮廓。需要非常平滑的放下角色的臀部,这样和下面那个脚就能保持一条腿的距离。


 

 

  还有,相比绝对不会将模型插入到地面里去,平滑的动画更重要,因为那些疯狂的移动,维持正确的动画非常的重要。动画师也不喜欢你打破一个姿势动画。而最重要的事情是,绝对不要过分的伸展膝关节,这个并不需要。最好不要这么做,并不需要和地面完美的衔接,尽量不要让腿变得比它在动画里的长。


 

 

   当在查找动作匹配时,我们会倒找对应斜坡版本的斜坡动画(SlopeAnimation),当玩家在游戏中走下楼梯时,下楼梯那种一顿一顿的感觉,这种感觉很重要,只去校正动画中丢失的部分,也就是开始下楼梯以及走到平地的那一段,这种情况下IK会产生作用,但当你的角色进入向上或向下的循环,就不需要在关心这些了。


 

  然后,下面的视频是没有脊柱关节弯曲的。我们需要精确的交互,需要可以知道操作的角色击中了其他的角色的上面,这里实现了剑的IK,但后面发现需要通用的解决方案,来处理这些疯狂的移动。


  

  这里的方法是只去确认红色矩形内的发生的事情,要做的也只是当角色在不同高度时,就是让两个人的脊柱都按照倾斜的地面倾斜,并挪进一些,如果两个人一高一矮,也可以类似处理。


 

 

 

  下面的视频就是获得的在楼梯战斗的比较好的结果,你会发现如果只做剑部分的IK,是很难实现的。


 

   最后是一些未来的工作, Clavet希望可以匹配更多的东西,比如可以攀爬,跑酷,或者像猴子那样,这需要能匹配surface,原来是两个角色之间的动画交互,现在是角色和表面之间的动画交互,可以根据动画中Surface的位置信息评估所有的候选动画。真实的匹配可以让他们看起来在游戏世界里更加正确。是非常好的攀爬和跑酷系统。譬如原来是将角色的位置和朝向,还有脚的IK,现在可以针对Surface做手部的IK。

  同样可以让匹配伙伴的交互更加智能,譬如在足球游戏里,还要将球的位置考虑到动画播放里去。


 

  Motion Matching其实思路很简单,最核心的点是如何进行动作的描述和控制。具体来说,我们有了数据,有了想要的目标,现在如何在数据里找到最符合我们需要的动画 


 

  这种方法有三个好处。1 质量高,因为尽可能保留了动作捕捉数据里的输入动作,有细节保留的好。2. 能够让玩家感觉角色的控制性很好,很快做出了反馈。3. 顺便有个好处是,艺术家的工作量少了很多


 

  我们的目标是:只要标记好了获取到的动作捕捉的数据,游戏就自己动起来啦!


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台