作者:Jerish,首发公众号“游戏开发那些事”
“角色移动”是大部分游戏的玩法基础。不过在游戏中,他看起来如此的简单自然以至于很容易被玩家所忽略掉。实际上,角色的移动涉及到动画、渲染、物理、同步等多项开发技术以及大量的美术工作。即使单从设计上讲,“角色移动”也经常会给开发者们带来一些让人陷入两难的问题。总而言之,想将角色移动做到完美确实是相当困难的。
虚幻5演示的Demo中有一段爬墙的动画,想做出这种效果其实是非常不容易的
这篇文章会围绕3D角色的移动,来谈谈为什么如此基础的功能却需要极高的技术力与细节成本?
一、移动原理与常见问题
之前一篇科普文章中曾讲述过“游戏角色移动的基本原理”。
游戏世界与真实世界相同,也是一个三维的空间(2D游戏是二维空间),我们游戏中的所有角色,都会在这个三维的世界里面出生、移动、交互直至死亡。除了角色以外,游戏中的其他对象也是如此产生的,比如说建筑、武器、道具等等。他们身上都保存着自己的坐标位置,当我们在移动我们的角色的时,其实就是在不断的修改当前角色的坐标值。
所以想让一个对象在游戏世界中动起来其实很简单——改变他的位置即可。
但假如我们控制的是一个人形的角色,只是简单的修改他的位置是不够的,我们还需要让角色走得更自然和真实。换句话说,就是让角色的移动看起来更贴近真实世界。这里面其实涉及到人体结构、物理受力等很多复杂的问题,我们很难通过简单的数学模型或物理引擎来达到期望的效果。因此,目前游戏界通常采用人为制作的动画的方式来取代真实的物理模拟。
动画的实现有顶点动画和骨骼动画两种方式,现在普遍采用骨骼动画来处理角色的移动。骨骼动画,会将3D模型抽象为一些关键点的集合,这些关键点互相连接成“骨骼”并组成一个完整的骨架结构,我们通过改变骨骼的朝向和位置来既可以为模型生成动画。
由此可见,一个角色的动作是否自然,很大程度上取决于动画师的设计。
现在,有了位置以及动画,角色就可以真正的动起来了。游戏中每一个角色的移动需要有两个基本的系统共同维护,一个是角色的动画系统,另一个是隐藏在代码背后的位置计算系统(可以称为移动组件系统)。角色动画系统负责按照时间戳播放其动画,一般由美术通过三维建模软件制作(2D的比较简单,就是按帧替换图片,3D一般是骨骼动画)。将动画导入到游戏引擎中,就可以看到该角色会不停播放移动的动画,但是他不会向前产生任何位移。
那么角色是不是就可以完美的动起来了呢?不,噩梦才刚刚开始。首当其冲出现的就是一个非常常见的基本问题——“滑步”,滑步产生的根本原因是动画上的表现与其实际位置的移动距离不匹配(角色一步走10米,看起来像是在滑行)。
解决滑步通常从两个方面考虑:
第一个方面是针对角色常规的移动。一般来说,玩家在走路、跑步的过程中速度是有规律的,要么是匀速、要么是匀加速。这时候可以把移动组件系统应用起来,也就是每当玩家播放移动动画的时候通过玩家的状态来合理的计算其应该走的位移长度并应用。这种方式就可以实现通常意义上的角色移动了。也就是说,这些比较有规律的移动计算可以交给代码层面去处理,代码需要仔细考虑角色所处的状态通过合理的数学模型去模拟计算位移。
第二个方面是针对比较复杂的特殊移动。角色如果不是匀速移动也不是匀加速移动,我们的移动组件系统根本没法算,就算你代码写的再好也很难达到美术想要的效果。所以我们通常会采用Rootmotion的解决方案,即每一帧的位置交给美术处理,他们在做动画的时候把位移信息也写到动画里面,我们移动的时候读取美术的数据就好了。
当然,考虑到美术工作量等问题,我们不能都用rootmotion解决。角色还有一些特殊处理但是又比较通用的状态,比如转弯、转身、突然停住等,显然转弯的时候玩家的身体会倾斜、转身时候我们的脚步应该逐渐踩过去,突然停住要有一个刹住脚步并恢复的效果(而不是直接滑过去),这些一般我们使用动画状态机配合混合空间(Blendspace:可以根据两个输入的动画来进行混合)可以解决大部分问题,不过还是需要美术制作很多动画资源。
而状态机,和计算机的FSM(有限状态机)概念是相似的。在游戏里面,其实就是我们预先定义了一些节点作为角色行为状态,比如跑,站立,下蹲等。当我们按下下蹲按钮的时候,判断一下是否满足某种状态切换的条件,满足的话角色将就会从默认状态转移到下蹲状态。
仔细观察,你会发现很多以前游戏都没有处理这些细节,就是因为太过繁琐。
最近几年,一个称为MotionMatch的技术流行开来,用来解决各种动画的衔接问题。他的基本原理就是首先制作一个丰富的动画资源库,然后在角色移动的时候不断的根据当前角色的动作与状态信息根据算法从资源库里面挑选最适合下一帧播放的动画。育碧的“荣耀战魂For Honor”就采用了这种技术,同时配合深度学习去挑选动作,达到了非常不错的效果。不过这项技术对于小的团队明显不太适用。也许多年后会出现一套完善的共享开源动作库,大家都可以体验到该技术带来的便捷了。
https://www.gdcvault.com/play/1023280/Motion-Matching-and-The-Road
好了,如果我们成功解决了上面的这些问题,是不是就可以让角色愉快的移动了呢?
答案当然是NO!下面还有一堆进阶的问题等着我们解决,而且不同的游戏面对情况截然不同!
二、移动进阶问题
1.碰撞
一个玩家动起来了,那如果遇到了障碍怎么办呢,是推着障碍走还是被阻挡住,这其中的标准如何制定?如果撞到了另一个玩家,可以把人顶走么?玩家上楼梯是一级一级上还是平滑着上?玩家在多少度的斜坡上可以走呢?类似的问题数不胜数,很多都是游戏设计上的问题。
这些问题大部分可以在物理引擎的支持下,通过射线检测等方式解决,但是也有一些不好处理的情况。
对于上面的问题,这里简单给出常见的方案。比较大的物件都会设置为静态的(也就是不会移动的),所以玩家移动时会被挡住,而小的东西玩家可以直接踩过去。至于能否顶走其他玩家,要看游戏的类型而决定。上楼梯我们为了保持摄像机的平滑,会把台阶的物理设置成平滑的。
2.特殊模式
前面说了那么多都只是角色行走,而在游戏中设计者的思路是天马行空的,策划可能想让角色飞起来、游泳、吊单杆、爬梯子、以及各种逼着牛顿从棺材板出来的反物理行为(如多段跳、轻功)等,那么要如何处理这些移动模式呢?
其实上面的每一种特殊的移动方式,都需要特殊的处理方式,几乎没有统一的解决方案。比如爬梯子,为了让玩家的手能够与梯子的把手重合,我们需要严格设置梯子的每隔的间距,并做到与动画相适应。同时,也需要做出一种特殊的移动模式,只当角色处于爬梯子状态时只能匀速的修改坐标的Z值。
简而言之,特殊模式很复杂,每一个功能拎出来一个都够程序和美术做一阵的。
《尼尔机械纪元》2B爬梯子的过程设计的非常舒适
3.特殊对象
我们控制的角色不一定是人形的角色,还可能是汽车、动物。
对于汽车对象,有一点好处是我们基本不用考虑动画的适配问题,因为汽车不需要像人一样挥动四肢,只要轮子动起来就可以了(当然像地平线、极品飞车这种3A级别的赛车游戏,车身的动画也是相当复杂的)。但是我们需要在程序里面比较完美地模拟出其移动的轨迹,这就必须要涉及到物理引擎的使用和优化,难度也是相当之高。
对于动物,我们需要解决一些骨骼的处理问题,由于人形角色在游戏中使用的比较广泛,相关的系统支持的都还算不错。但是由于动物的骨架结构与人不同,所以我们需要制作新的美术资源,这也就意味着人形的骨骼资源不能适配到动物上。如果你做了各种结构迥异的生物,那美术肯定就要被累死了。(所以,仔细观察的话你会发现,一个游戏里面很多小怪角色的身体结构都是相似的)
4.多个移动状态的衔接
前面提到了角色可能走、跑、唱、跳、飞、游等,当你从一个状态切换到另一个状态时应该如何衔接?走着走着突然被车撞飞(有点惨),是应该头着地还是屁股着地?是被撞的一瞬间就切换成飞出去的动画么?假如你往前翻越一个栅栏的时候被打了一枪,应该往前还是往后倒?翻墙的时候能翻越多高多厚的墙体?
有些时候我们可以通过开启角色的Ragdoll(布娃娃系统),完全交给物理引擎去处理一些移动模拟。但是很多情况下,我们想要玩家能够控制最后的效果,所以又不能完全交给物理引擎。目前的部分引擎的动画系统支持基于物理的动画,我们可以按照固定权重进行物理和动画的混合,一定程度上减少了美术的工作量,也可以做到一些更真实的效果。
5.移动同步
前面说了那么多,还只是针对单机游戏。如果你是网游,那么你的角色动起来的时候应该如何告诉其他客户端怎么动?如果延迟过大造成两个客户端的位置或者动画不一样怎么办?在很多游戏中,移动同步是一个相当大的难题,因为玩家的位置每一帧都可能在变化。
常见的同步方案有“帧同步”(Lockstep)和“状态同步”,也有很多优化的方案。
6.其他细节
比如关卡这个地方能不能走,不能走怎么防止玩家过去?(可以采用的空气墙处理,然而玩家很讨厌空气墙)
能不能两个人一起走过一个狭小的空间?因为如果一个玩家站在那里,其他玩家过不去可能体验会很差,所以可能考虑调整碰撞体大小来让两个人可以并排通过。当然,这也涉及到关卡的设计问题。
走了一半穿模怎么办?调整动画或者碰撞体。
类似的问题还有很多很多,我这里就不再一一列举了。
三、总结
总的来说,角色移动不是简单的与某几个系统有关,可以说他涉及到了整个Gameplay的大部分设计逻辑,甚至在一些游戏中,我们认为他是游戏的玩法核心也毫不为过。
然而,很多时候,他显得那么自然以至于很多玩家都将其忽略了。
来源:游戏开发那些事