当我们提起 3D 动作游戏的时候,能想到著名游戏有很多,比如《怪物猎人崛起》《艾尔登法环》等。3D 动作游戏和 ARPG 可以说正在渐渐成为主流的游戏模式。当说到动作游戏的时候,就会想到“跳”这个功能,毕竟“跳”对于动作游戏,至少是 2D 的动作游戏来说,是一个非常重要的元素,而越来越多的 3D 动作游戏和 ARPG 也加入了跳的功能。
那么,作为游戏的开发者,当我们在说 3D 动作游戏的“跳”的时候,究竟在说的是什么呢?很多人听到这个字的时候脑海中产生的画面是一块平地上,角色高高跃起,然后落地 —— 这确实是“跳”这个功能最直白的表现,但是对于游戏开发来说,这只是“跳”这个功能的冰山一角。因为有了跳,就代表有了“浮空”和“地面”两个状态,由此会带来许许多多的新东西,形成许多新的玩法,也会为游戏的功能实现带来不少麻烦。
在 3D 动作游戏中做跳的功能可以为游戏带来什么当我们决定在 3D 动作游戏中加入跳跃功能的时候,就会产生两点基础性质 —— 地形高低差和空中作战,而基于这两点性质,在关卡设计等细节上都可以有很多更进一步的性质可以提炼。
地形高低差跳跃功能能带来的最直白的效果之一是地形高低差。这个地形高低差与早年的怪物猎人系列(4 代开始)和古墓丽影系列游戏中的地形高低差概念不同 —— 早年的怪物猎人中的地形高低差是指某些特定地方存在一些特殊地形,玩家操作角色在这些地方移动,从下往上会自动做一个攀爬动作,而从上往下则会自动做一个跳跃动作。
在最新一代的《怪物猎人崛起》中,因为翔虫的加入,使得游戏相当于有了自由的跳跃,外加上攀岩等功能,使得怪猎崛起有了真正意义上的地形高低差,但是这个地形高低差确实也只有切换地图的意义,而没有实际的游戏性的意义,比如 —— 使得玩家有了居高临下作战的优势,这在怪猎崛起中是没有的,一旦玩家所处的位置过高,怪物攻击不到玩家就会换图。
所以好的地形高低差作用就在于让玩家可以利用高处的优势和低处的地形优势来获得战斗有利,玩家则利用跳跃技巧(大多动作游戏中,能起到跳跃效果的动作往往不止一种,合适的时机选择合适的动作来跳跃就是一种技巧)游走于各种地形之间,在当时的局面下获得优势。这就是地形高低差带来的,相对于只有平地更多一层深度的玩法。
空中作战既然有了跳,就会有空中作战的技能,在传统的 2D 动作游戏和格斗游戏中,跳跃是一种进攻的有效渠道,在 3D 游戏中也是如此,比如《艾尔登法环》中的跳劈等,都是十分主流的玩法。
跳起来在空中可以发动的攻击,远远不止往下劈砍一种,许多游戏还有各种华丽空中连招,使得玩家玩起来赏心悦目。
因此跳起来就产生了“空中作战”这一维度,不仅可以有空对地技能,还会有空对空的技能,让玩家空地结合打出更多漂亮华丽的招式来,这也是跳跃所带来的玩法。
由此延伸出来的各种问题和解决思路
跳跃的存在,为我们带来了一些新的性质,这是好的,但真的要实现这些跳跃的内容的时候,又会延伸出很多麻烦的问题,主要包括:
悬崖坠落问题因为有了地形高低差,所以必然是会有“悬崖”的,由于是动作游戏,所以会有不少动作本身是带位移的,即使是一些 ARPG 游戏,也难免会有这样的技能,在悬崖边使用位移技能就可能会导致角色跌落悬崖。尽管并不是所有游戏跌落悬崖都会受到伤害等直接损失,但是如果在不情愿的情况下不慎跌落导致处于下风,也是玩家不愿意的,因此要不玩家自己靠熟练度克服悬崖边的问题,要不就是开发者帮助玩家保障一些动作绝对不掉落山崖,会顶着悬崖边缘。
“保障一些动作绝不掉落山崖”这句话说起来非常轻巧,可是“怎样保障哪些动作不会掉山崖”呢?这却是一个非常难以制作的细节,因为在 3D 动作游戏中,我们没有一个很好的逻辑可以说明“下一帧要走的位置是悬崖”。因为我们要阻止角色释放技能不掉下山崖,那么我们必须知道这一帧我们没有掉下山崖,而下一帧会掉下山崖,所以我们需要一个判断是否掉落山崖的逻辑。乍一想,这个逻辑可以是这一帧站在地上,下一帧没有站在地上:
但是这条逻辑也同样适用于角色起跳:
所以我们没办法单单靠这一条逻辑来决定角色是否掉落山崖,然后去把角色的移动停掉,这样盲目的停掉角色的移动,哪怕只是停掉水平方向(ue 的 xy 方向,unity 中是 xz 方向)上的移动,也会导致跳跃动作只能原地跳了,于是我们只能再追加一条,就是垂直方向(ue 的 z 方向,unity 的 y 方向)在水平或者向下移动,这时候如果 xy 方向有移动,并且会导致这一帧站在地上(脚下有阻挡),而下一帧脚下没有阻挡,就停掉这次 xy 方向移动。但是这个做法,也仅仅只是适合于悬崖边是水平的地面,假如地面是这样的:
所以如果我们启用了这个方案来避免角色冲下悬崖,那么就不得不注意地图编辑时候悬崖边的地形问题了。
而通常来说,类似《怪物猎人崛起》等游戏中,为了确保玩家从悬崖上跳下去的时候会做一个跳崖动作,所以是在悬崖边上拉了一个碰撞盒,进入这个碰撞盒会让角色自动做一个跳出悬崖的动作:
这样的设计,就跟上述的做法完全冲突了,因为我们很难知道角色下一帧会碰到碰撞盒(需要遍历全世界的碰撞盒来预测某个角色下一帧是否会碰到,毕竟“碰到盒子”容易,“不碰到盒子”难测),即便是知道,但是由于这个碰撞盒也有宽度,那么会导致角色位移停在悬崖边较远的地方,显得非常不自然,所以通常动作游戏也有放弃这个边缘不掉下去的风格,并且相对来说,悬崖边会掉下去更自然更好设计一些,只要让战斗区域更不容易接近(无法绝对保障,但是可以尽可能保证)悬崖边。
冲出悬崖问题看上去和上一个问题是反面,但实际上却又有较大的差异,这个问题的根本是 —— 每一个动作,如果冲出悬崖应该如何继续?这是一个地形高低差和空中作战技能带来的混合问题。
首先,我们要确定有一些技能总是能冲出悬崖的,即使我们用了上面的算法,但依然有一些技能,他们本身就会离地,所以应该是要能冲出悬崖的,那么冲出悬崖之后,就存在一个后续怎么处理的问题:
类似 KOF 和饿狼传说中,Terry 著名的灌篮强击就应该是一个会飞出悬崖的动作
这里就涉及到 2 方面的问题,首先是这个技能飞出悬崖之后的移动轨迹该是如何的,因为这个技能本身可能是一个角色稍微离地,但是更多往前飞行的技能:
还是比如一个类似 Terry 冲拳的技能,他的轨迹是这样的,略微离地然后前冲,那么如果在悬崖边上使用,他应该是怎样的轨迹?
A 轨迹是原来的路线的延续,因此动作是没有落地的时候始终会保持当前动作继续,那么最后只要悬崖够宽阔,飞到哪儿就变成了一个只有上帝知道答案的事情了,所以显然是不妥的,玩家都无法预知一个行为的大致结果,这是一个非常危险的设计。
C 则是做完动作以后直接变成下落动作往下掉,这样做也不是不能接受,只是可接受度最低,但实现起来却是最简单。
B 轨迹做出了一定下落修正,看起来是最有道理的,但是问题来了,这个下落的依据是什么?如何执行?用 RootMotion,那就有一个整个动画多长的问题,如果是用类似 UE 中 Montage 的 Section 做 Loop,就需要做一个每次 Loop 下坠变多的 AnimNotifyState。这看起来问题已经解决了?不,问题只是刚开始 —— 请问这个 AnimNotifyState 如何在正常的移动系统中让这件事情(每次变重)发生?
这里我们要说回移动系统,就是我们为了保证一些动作向上升的高度是预设的,或者说某些动作要始终保持一个水平移动,我们会让动作在有 RootMotion(或者其他信息)的情况下,角色本身向下落的重力不生效,形成“飞行状态”。我们明显能看得出,Terry 的冲拳阶段(或者说按照需求做成冲拳 loop 的那个阶段),应该是“飞行状态”的,因此我应该向哪儿去追加这个下落的力呢?于是我们有需要开一个力来做这件事情?这当然解决了我有一个力保证让我往下,但是问题是,这个力什么时候结束?所以一连串问题,使得这个看起来非常好的 B 轨迹,变成了一个最不容易实现的东西了,因为要捋清整个移动的逻辑才能去做。
因此我们在设计技能的时候,不仅要考虑空中作战技能需要一段 loop(直到地面)
由下而上问题上面我们说到的都是由上而下的问题,现在该来说说由下而上的问题了。由下而上会出现一个游戏平衡性的问题,比如我们希望击飞敌人的时间长度是由策划预设的,但是因为击飞的时候对方角色是一个由下而上的过程,这就会导致角色落在一个更高的平台上:
当然,相反的,高处被击落到低处的角色的抛物线时间应该会更长,如果游戏设定如此,当然从高往低我们可以设置击飞动作时间超过了“策划预设的硬直时间”就直接变成下落动作,但是自下而上的被击飞角色 B,就只能幸运的提早结束硬直了,要不就是在地上躺更久,但是显然提早结束是一个更好接受的结果。
这从实现上来说,是一个很典型的 bug,但是从游戏设计来说,是一个“也只能这么接受了”的设定,只能妥协的坑。
既然这些问题有解决方案,那还有什么难的?在实际的游戏开发过程中,很多事情即使有了方案,也未必等于解决了,因为方案到解决中间还有一个落实过程,而很多问题最终无法被解决,也是因为这层落实。比如我们上面说到的冲出悬崖问题,实际上它看起来是一个工作量的问题,只需要在每个动作里面配置“角色落地检测”,精确到 UE 的做法,就是每个 Montage 里面拉一段 AnimNotifyState,由这个 AnimNotifyState 来负责落地检测,同时负责跳转 Section,就能完成整个工作了,听起来这是一件非常简单的工作,但是连玩家角色带怪物,整个游戏可能有几十个角色,每个角色几十个动画,我们确实可以派人专门去做一次,0 到 1 按计划做出来看似没什么问题,但是一旦到了测试环节,或者某个环节出现 bug,要进行查错的时候,UE 的开发环境就会带来十分致命的效率打击。这就是游戏开发 —— 我们绝大多数时候并不是在做 0-1 的事情,而是在维护,不仅为了新的想法,也为了老的失误,就比如配置这个 AnimNotifyState,它不是一件机械化的事情,因为代码不知道这个 NotifyState 为什么要拉在哪儿拉多长,他必须人类来做,但是人类却要在机械化一般的环境下去做这些事情,最终就会难以避免问题。
当我们考虑整个工程(做完整游戏而非 Demo)的时候,就必须完全的考虑到这些问题,因为海量的数据会让原本看似可能(Demo 能行得通)的事情,变得完全不可控,甚至会为项目进度带来致命打击。
所以,当我们了解了在 3D 动作游戏中做一个跳的困难的时候,请量力而行,所谓的量力而行,指的是整个团队的人力、财力是否能支持去做,如果没有跳,可能半年就完成的东西,加了一个跳之后 1 年半还在找各种细节问题。
本文来自微信公众号:千猴马的游戏设计之道 (ID:baima21th),作者:猴与花果山