《重装上阵》程序篇:机械腿科普

仙贝苹果汁

2022-12-203582次浏览

1评论

6收藏

5点赞

分享

本文字数在2800左右

篇幅适中,图文为主

内容干货 金牌独家

阅读时间3-5mins

学院菌推荐指数:⭐⭐⭐⭐⭐

《重装上阵》是一款沙盒创造竞技类游戏。游戏的核心玩法分为两大部分:一是使用带有各种功能的模块,像拼乐高一样组装成一辆可以移动与对战的机甲,然后操作机甲在各种玩法规则下进行多人PVP对抗。二是用模块拼装出丰富多样的造型,并通过游戏内的拍照、短视频等功能,将造型分享给其他玩家观看。网易游学特邀《重装上阵》程序“仙贝苹果汁”分享如何实现一个机械腿。

在《重装上阵》中实现一个机械腿,需要考虑很多方面的内容,包括物理、输入操作、UI、动画、同步、特效/音效、战斗等等。本文将会介绍一下机械腿涉及的物理和动画相关技术。

下图是游戏中一款典型的机械腿:

它可以被拼接在战车上面,成为行进和跳跃的动力来源。下图是安装了机械腿的战车:

与轮子的不同之处在于,安装了机械腿的战车可以前后左右自由平移,还可以360°自由转向,类似于人物的行走。而每条机械腿都是独立的模型,也有一套相对独立的动力系统,他们是怎么一起配合的呢?

由于自由拼接的特性,我们的每个机械腿在作用于战车的时候,都是独立计算驱动力的,其动力模型和通常的车轮类似,但它的转向是自由的,相当于一个万向轮。对于轮子来说,从UI开始,玩家的摇杆输入,被转换成两个0-1之间的浮点数,一个代表前进后退,一个代表左右转向;而机械腿则不同,玩家的输入会首先被转换成成极坐标的形式,由一个半径r和一个角度θ组成,这两个值在传到物理引擎的时候,分别代表了动力和方向。相机的水平朝向也会传给物理引擎,作为转向力的来源。

物理引擎在接收到了这三个参数之后,会在每帧进行物理模拟,用这三个参数和战车/场景的物理属性计算出下一帧战车的姿态。具体的模拟过程,利用了传统的欧拉积分的方法,为了达到更好的模拟效果,我们还使用了变步长积分,把每帧的时间差Δt切分成更小的时间片。这里也复用了轮子的一些计算,包括使用抽象的转速/扭矩计算出推力的过程。

现在有了动力,就差动画表现了。为了更加拟真,我们使用了动作融合加反向运动学(IK)的技术。

为此需要先了解一下骨骼动画,骨骼动画是由一系列的关键帧插值而成的。每一个关键帧里面都有一个pose,一个pose中每根骨骼的变换在我们的代码中由一个结构体来表示:

struct Transform

{

Vector3 pos;

Rotation rot;

Vector3 scale;

}

用四元数表示旋转,比较省空间,也更容易插值。某一时刻的动作就可以表示为,即关键帧在时间维度上面的插值,如下图所示:

动作融合,顾名思义就是将待融合的动画的输出逐一加权融合。可以简单地用下面的代码来表示:

  void Blend(TransInfo* anims, int const, Transform& out)

  {

      out.Identify();

      float weightSum = 0.0f;

      for (int i = 0; i < count; ++i)  // 对于每一个待融合的动画

      {

          weight = anims[i].weight;

          weightSum += weight;

          if (weightSum > 0.0f)

          {

              // 用当前的总权值和当前动画的权值插值

              Intrp(out, anims[i].transform, weight / weightSum);  

          }

      }

  } 

如果用公式表示,就可以写成如下的形式:

一般的游戏中,动作融合常用于动作之间的平滑过渡,例如人物角色从走到跑的过程等,也有些游戏会拿来做动作的叠加,例如一边转身一边射击等。如果是动作过渡,这里的就可以是时间,而在我们的机械腿的实现中,这个实际指的是由战车的速度和机械腿在车上的局部坐标求得的机械腿的线速度。

具体怎么实现动作融合呢?重装上阵使用的NeoX引擎支持使用动画树,动画树看起来像下图:

每一个cource是美术制作的一个动画片段,每一个blendNode的子节点都会被融合,除了基本的blendNode之外,还有各种其他的内置的混合节点,以一定的规则处理子节点的结果,输出给上一层,就这样一层一层到达根节点之后,就得到了最终的pose。而对于机械腿,有下面的动画树:

前后左右加上idle,一共5个动画,根据机械腿的前进方向进行融合。我们使用了Directional2D的插值方式,前后左右的坐标分别为(0,1),(0,-1),(-1,0),(1,0),而idle的坐标为(0,0),输入参数来自机械腿的速度。例如下图中绿色圆圈的位置就代表机械腿向右前方行走时的插值位置。

通过动作融合,我们的机械腿可以动起来了,但是看起来还比较不自然,没有一种踩在地上的感觉,一直在滑步,有时还会插进地面下面。这些就可以用反向运动学(IK)来解决,让脚稳定地踩在地上。

IK是一种通过先确定子骨骼的位置,然后反求推导出其所在骨骼链上n级父骨骼旋转,从而确定整条骨骼链的方法。通俗举例来说,就是已知脚的位置,求整个腿的每根骨骼的旋转。在具体使用中,通常还会和原始的动画进行加权融合。而在我们的机械腿的实现中,使用了最简单的Two Bone IK,它适用于两根骨骼的情况,使用解析法求解,比较高效。下面大概讲一下原理。

上图是一个抽象的机械腿,(0, 0)为root骨骼的位置,(x, y)为Target,也就是脚掌的位置。d1​, d2​为可以旋转的两根骨骼。所以图中的已知量为d1​, d2​, x, y,需要求的值为θ1​和θ2。​

在机械腿IK的具体实现中,主要需要考虑以下几个问题:1、IK权值的变化,2、行走过程中Target的变化,3、Join Target的选择,4、需求和算法的折衷。

1.权值计算

权值的定义如下:对于每个骨骼的权值,,其中为动作融合之后当前帧的变换,为IK求解得到的变换,为最终的变换。

对于机械腿来说,理想的状态应该是当腿踩在地上的时候,weight为1,否则为0,而考虑到平滑,就需要在0和1之间加上过渡,为了更具有可操作性,我们让美术加了一根没有蒙皮的骨骼(output),程序读取该骨骼的x轴坐标作为基本的权值,再根据一些特殊情况做调整。当机械腿处于idle状态的时候,weight=1。

2.Target的确定

Target的选取也由output骨骼确定,当权值大于某个阈值的时候,我们认为脚已经着地了,就把此刻的着地点作为Target,以防止滑步,着地点由射线检测确定。当权值小于某个阈值的时候,我们认为脚抬起,此时Target将由前面射线检测得到的着地点过渡到动画本身确定的Target的位置。

3.Joint Target的选择

[a]
[b]

上面两幅图(前腿)是在极端扭曲的情况下,设置Joint Target前后的动作的对比,图[a]是默认Joint Target,图[b]是使用自定义的Joint Target。Joint Target的具体计算方法是以Target、root骨骼以及Target垂直地面的上方确定一个平面,再在这个平面上选择一个点,而这个点的高度实际上并不重要,因为骨骼的长度是确定的,所以重点是上述三点确定的面。

4.一方面我们要保证算法输出的正确,比如脚不插地、不滑步,但是策划需要机械腿驱动车辆的速度更快,而腿的长度不变。显然,这只能加快动画的播放速度了,于是我给了一个线性的函数让策划填表来控制。然而,当我们保证了算法正确的时候,机械腿的行走动画播放速度已经快到无法直视了,所以只能做出一些妥协,当IK求解得到的Target与动画本身确定的Target之间距离较大时,根据距离线性地把权值降低,直到0。这样就能让动画的播放速率降低一些的同时不会把腿拉得很长,保证实际体验才是最重要的。

在解决了上述4个问题之后,机械腿的表现就做好了,但是还有很多的细节和参数需要调整,这就要和策划同学一起配合,多去了解策划和玩家的反馈,一些参数还可以开放给策划来填表,省一些自己的工作量。

本文只是讲述了机械腿涉及的部分基础内容,和实际游戏中的最终结果相比,剩下的就是大量的细节和打磨。而这也是游戏特有的魅力之一:游戏不仅仅是对现实的完全复刻,而是能够提供超越现实的体验。我们的机械腿实现也是始终把玩家体验和手感放在最优先,对细节反复打磨,从而达到最好的效果。

👇相关阅读👇

《重装上阵》策划篇:任性地做一款不一样的手游

《重装上阵》策划篇:独立游戏的商业化再造

《重装上阵》策划篇:核心战斗规则设计分享

《重装上阵》策划篇:创造类玩法设计分享

《重装上阵》美术篇:模块皮肤设计

《重装上阵》UX篇:沙盒气质的交互设计

《重装上阵》QA篇:QA工作如何化“被动”为“主动”

评论 (1)

0/1000
网易游学APP
为热爱赋能
扫描二维码下载APP