介绍

GDC原文链接:GDC Vault - Player Traversal Mechanics in the Vast World of ‘Horizon: Zero Dawn’

During this lecture, Paul will show what is needed to make Aloy traverse the vast world of ‘Horizon: Zero Dawn’ with its complex and organic environments. Various traversal mechanics will be covered from a gameplay programmer’s perspective, focusing on the interaction between code and animations. The different systems and techniques involved in the implementation of these mechanics will be explained, and he will look at the underlying reasoning and design decisions.

在本次讲座中,Paul将展示:让Aloy可以在《地平线:零之曙光》中广阔、复杂、而又有机的世界环境中穿梭所需的条件。他将从游戏程序员的角度介绍各种Traversal机制,重点关注代码和动画之间的交互。他将解释实现这些机制所涉及的不同系统和技术,并研究其底层原因与设计决策。

0. 总览


我的名字是Paul van Grinsven,我是 Guerrilla 的一名游戏程序员。

我想从一段简短的视频开始,该视频会让你对《地平线:零之曙光》里玩家在广阔世界中的各种Traversal机制有个大致印象。

(视频链接)

所以这个有着火焰般头发的女战士是谁呢?我们又在和谁交手呢?

首先让我向您介绍——Aloy

她是一个寻求自己来源的部落弃儿。她是一个冒险家,具有很强的敏捷性和耐力。在野外长大和多年的训练使她成为一名非常强壮的登山者,具有非常发达的战斗和狩猎技能。


《地平线》的世界很大,大部分地形都是程序化生成的。
世界有许多不同的生态环境, 河流、森林、植被、气候和野生动物种类繁多。
除此之外,世界还充满了室内和室外的人造建筑和定居点。
所有这一切使得游戏程序员实现一个可靠的Traversal系统非常具有挑战性。

现在您已经对《地平线》有了一个简短的了解,下面让我们快速了解一下本次演讲将涵盖的内容:

我将简要地解释我们Traversal机制的目标和约束。
简短地介绍我们的工具、工作流和动画管线。
然后我将逐步介绍我们是如何实现一个反应灵敏的导航的。
接下来是对我们高级的Traversal机制的更深入的描述。
最后,将讨论我们未来的一些计划。

1. 目标与约束

首先,我想说一些关于我们在项目开始时对玩家Traversal机制的目标和限制:

从设计的角度来看,总体要求是具有响应性流畅的运动。
《地平线》是一个开放世界的动作角色扮演游戏,因此响应能力对实际游戏玩法有很大影响。
Traversal机制的开发团队由2名程序员、3名动画师、1名设计师和1名制作人组成。
从技术角度来看,一切都需要以至少 30fps 的速度运行。
由于地形主要是程序生成的,我们还必须确保无论玩家身在何处,所有运动都能在世界各地正常运行。

2. 工具、工作流和管线

在 Guerrilla,我们的动画师同时使用 Maya 和 MotionBuilder。

我们使用 Morpheme动画中间件,它由动画创作应用程序(Morpheme connect)运行时引擎组成。
Morpheme Connect 允许我们的动画师以图形方式创作混合树、状态机和转换逻辑。角色的所有这些动画行为都存储在“animation network”中。
Morpheme运行时代码被Decima引擎所集成,它负责实例化此类 animation network ,并处理运行时的播放。

Decima引擎是我们自己内部开发的引擎,它由编辑器运行时组成。
Decima编辑器是我们的创作工具,它允许用户以图形方式构建巨大的动态游戏世界,包括所有系统和逻辑。

3. 反应灵敏的移动 & 高级的Traversal机制

我们希望 Horizon 的世界与我们的世界有很多相似之处,并涵盖许多不同的景观。

幻灯片上的图片显示了 Horizon 中一个典型的可穿越区域,你可以看到很多地形上高度差异,它一点也不平坦,不像我习惯的荷兰风景。

在演讲期间,我将解释我们开发的各种机制,这些机制将使站在左侧的 Aloy 能够到达屏幕的最右侧。

3.1 移动

当然,在我们想到达任何地方之前,首先需要确保 Aloy 能够开始移动。

在我们看来,对于第三人称角色 “控制灵敏度” 的感觉主要来源于启动停止
这就是为什么对于《地平线》,我们真的希望获得一个流畅且响应迅速的启动/停止系统

系统应该支持左脚和右脚,并且移动必须非常容易控制,角色应该始终朝着用户输入的方向移动。

先给大家介绍一下我们在早期的启停系统原型设计过程中遇到的第一个问题:

当尝试从停止过渡到开始时,动画系统不会直接处理输入,因为我们首先要等待停止动画完整地播放完。这样做是因为停止动画包含一些不错的辅助动作我们想要展示。

让我在下一个视频中表现一下这个问题的样子。

在右上角,你可以看到控制器的输入。 请注意输入和 Aloy 实际移动之间的延迟。

这个问题的解决方案很大程度上依赖于动画事件的使用。

在 Morpheme 中,每个动画片段都可以使用 “动画事件” 来进行注释,“动画事件” 具有时间点持续时间id 的属性。动画师可以控制事件何时以及多长时间处于活动状态,以及如何将事件与多个动画混合在一起。

每次更新后,Morpheme运行时都会向游戏提供 “当前活动事件” 的列表。这是让游戏逻辑基于动画系统来同步自身状态的重要工具。

我们通过允许在 “Exit allowed”事件 处于活动状态的时间段内进行早期反应,解决了从停止到开始转换时的无响应问题。

当在此事件期间时,给出任何移动输入都会触发开始动画的早期过渡,这样提高了响应速度。而如果没有给出移动输入,我们就继续播放停止动画直到结束,然后再过渡到空闲状态。

通常,“Exit allowed”事件仅在动画片段的末尾时才处于活动状态。

下一个视频演示了效果:

接下来让我们更深入研究下我们启动系统中“定向启动”的设置。

我们有三个状态控制如何启动。我们有一个向前的启动状态和左右各一个状态。向前状态包括的所有动画都以与向前45度角内的方向启动。(请注意,我们不支持后退,《地平线》里只有前进)

你可能已经注意到,在不同的启动状态之间有转换的可能性,这样做是为了确保我们可以在不同的方向触发新的启动。我们发现这进一步提高了响应灵敏度。


在启动系统中,我们有三个变量用于控制动画系统:

  • Move,布尔变量,表示我们是否想要移动。
  • Speed,浮点数,表示我们想要移动多块。
  • Heading,浮点数,表示我们想要移动的方向。

而代码中的移动由“速度”和“转向速度”来控制。“转向速度”取决于速度:我们移动得越快,我们转弯的速度就能越快。


当操纵杆快速转向相反的方向时,Aloy 有能力进行 180 度的移动转弯。但由于游戏手柄和手指速度较慢,因此移动方向的这种突然变化还是会需要几帧; 它们绝对不是“立即”发生的。

为了正确处理这个问题,我们保留了最后 3 帧输入的移动方向的历史记录,并以此为基础停止、转弯和启动。不过尽管如此,移动速度和方向的更新总是即时的。


此幻灯片上的图像表示“定向启动”的混合树。它包含四个Blend节点。

第一个Blend节点基于Heading(45、90、180 度)进行混合。这确保我们匹配了正确的开始方向。

而每个方向上我们可以实现三种不同速度范围的动画(慢走、快走和跑步),这些输入的动画会基于 Speed 进行混合。

现在你已经了解“定向启动”了,下面我们关注动画片段中的动画元数据。

此图显示了以 90 度方向启动的轨迹。

所有定向启动的动画片段中都标注了各种动画事件,其中一个非常重要的就是 “locomotion event”。这个事件在运行时告诉我们是否应该使用动画驱动的移动。

所以,在这个动画的第一部分,轨迹是旋转的,我们让动画完全支配我们角色的移动。这样做是为了确保我们按照玩家的要求以正确的方向结束,并减少脚部的滑动。而当“locomotion event”不再激活时,角色的移动将再次完全由常规的移动代码所驱动。

然而 “locomotion event” 的使用却导致了我们的角色在所谓的“微运动”中变得反应迟钝和无法控制的问题。

我们的移动逻辑的设置如幻灯片上的图表所示。

在这个例子中,如果在启动状态中释放操纵杆,我们将过渡到停止状态。而由于停止的动画包含了向前移动的动画驱动部分,因此从开始到停止的过渡会剥夺玩家的控制权,不给他任何机会转向或中止。

我们通过在开始状态引入 Steps 和 Shuffle 解决了这个问题,并移除了从开始到停止和从停止到开始的转换。

Steps 和 Shuffles 是“启动”与“停止”的结合。

  • Shuffles 考虑的是方向上的运动,脚步保持不变但是有快速的转向并有向前的小幅度位移。你可以通过在任何方向上快速敲击操纵杆来触发 Shuffles。
  • 我们定义 Steps 状态为一个小幅度的向前移动,玩家可以在其中切换脚步。一旦双脚开始相互交叉,就可以通过松开操纵杆来触发 Steps 。它只包含向前位移,不包含旋转。

然后,每个“定向启动”都包含事件,其决定着此时可以转换到 Steps 还是 Shuffles。

让我们回到以 90 度定向启动中的动画元数据:

  • 在我们想要停止移动的那一刻,动画中的当前播放位置决定了我们是会触发 Step 还是 Shuffle。
  • 当我们到达动画片段的末尾并且仍然想要继续移动时,我们允许提前过渡到循环。

All transitions from a start animation to either a step or a shuffle are synchronized transitions, meaning that the time in the source animation is synced with the destination animation.(尝试翻译:来自启动动画中的所有进入Step或Shuffle的转换都是同步过的转换,这意味着源动画的时间会与目标动画的时间进行同步

这是一个简短的视频,展示了它的实际效果:

3.2 落脚点

现在我们已经可以启动和停止了,看一看我们遇到的下一个障碍。

如图所示,地形有一些明显的高低差。在如此粗糙和狂野的表面上平稳地站立和走动会是一个相当大的挑战。

在详细介绍我们如何实现平稳的移动之前,我想先提一下我们必须考虑的约束条件:

对于玩家角色,我们不使用导航网格。这主要是出于内存预算的原因,而这让某些事情变得有一点困难。我们对于玩家的导航是通过在物理世界里对一个胶囊体进行模拟来完成的,也没什么神奇的地方。

我们实际有 50 度的最大倾斜角,Aloy 冲刺时的最大速度为 6m/s,Aloy 可以跳跃的最大高度是离地 1.5m。

而我们的胶囊碰撞体支持不同的预定义尺寸,默认的站立尺寸为 1.8 米高和 70 厘米宽。


为了在不平坦的地形上实现平稳的移动,我们需要知道地表的梯度是什么,而我们当然要忽略掉那些高频的高低差变化。因此,我们不能直接依赖胶囊体与地面接触点的地面法线方向,因为他们非常地敏感。

为此,我们的解决方案是:
在玩家的胶囊体周围放 4 个碰撞探针,以此来构造一个接触平面,然后随着时间的推移来平滑结果。

从顶视图来看,可以看到我们在玩家周围有 4 个探针,用蓝点标记。所有探针都有一个相对于玩家位置的固定偏移,并与玩家的方向对齐。随后我们就可以计算出接触平面的法线了:它是左右接触位置之间的方向与前后接触位置之间的方向的叉积。

然后,我们对这个计算出的法线进行平滑,以排除高频的噪声变化,最终产生结果法线。

请注意,在一些太极端的角度下,探针的某些交点会被拒绝。不过只要我们有至少 3 个交点,就可以计算出接触平面的法线。

一旦我们构造出了我们的接触平面,就只需将 Aloy 想要的运动投射到它上面就可以了,她会在地形上平稳地移动。前向和横向倾斜角度也由游戏逻辑发送到动画系统,让 Aloy 可以播放附加的动画。

为了防止我们在等待探测结果上浪费宝贵的周期时间,我们决定让这一步骤异步执行。

碰撞探测是经过调度的,因此它们可以在这一帧的稍后阶段在另一个线程上执行。然而,这意味着我们的探测结果缺少一帧,因为我们必须等待帧完成才能收集结果。

现在我们可以沿着不平坦的地形上平滑移动了,那么我们如何保持双脚与地面对齐呢?

对此的解决方案是由程序来调整腿和脚的位置和方向。

  • 动画中事件的持续时间决定了何时允许调整脚和腿。
  • 而代码根据游戏状态和碰撞探测的结果来决定脚放在哪里。

将脚放在不平坦的地形上的工作原理如下:

  • 我们首先从膝盖高度向下到碰撞网格投射线,以检测脚下的任何表面。
  • 交点被发送到两个骨骼 IK 解算器,他们将为“脚踝-膝盖-臀部”求解,同时调整骨盆。
  • 所有动画都用“Foot rest 事件”进行了注释,这些事件控制脚何时搁在地面上,因此我们知道何时要完全激活“程序上的调整”。(每只脚使用单独的事件)

所描述的方法在大多数情况下都非常有效,但是幻灯片上显示了一个它无法处理的特定情况:站在悬崖附近,一只脚悬在空中:

我们一直称其为“悬空脚问题”。当向下的射线没有碰到脚下的任何地面时,就会发生这种情况。

我们的解决方式为:当射线检测无法探测到地面时,则有一个应变方案:

当发生“悬空脚”问题时,我们将执行另一个相交测试——但是将使用 Swept Sphere(扫掠球)不是射线,这样可以覆盖整个脚部的范围内以找到与表面的交点。这种相交测试的范围更大,找到的交点将会与脚部有个水平方向的偏移,然而IK可以完美地处理它。

不过,这个应变方案只会对Aloy启用。因为NPC总是会在导航网络上,因此可以保证它们下面一定存在有效的表面。

下一个视频将演示结果:

3.3 翻越


Aloy 在穿越这个世界时不仅会遇到地形高度上的微小差异,还必须处理障碍物,如岩石、断树、机器人零件等。由于 Aloy 非常敏捷,因此我们希望她能够毫不费力地翻越途中的大多数障碍。因此,“翻越系统”诞生了。

我们的翻越系统能够执行三种类型的移动:step upstep overstep off

其所使用的的检测距离等参数,取决于当前运动的类型。例如:与正常步行相比,游泳或短跑具有不同的检测设置。


在翻越系统中,第一步是看我们是否真的被允许进行翻越。我们的关卡设计师可以通过放置触发体积来禁用某些游戏区域中的翻越。除此之外,还可以将单个游戏资产标记为“不可被翻越”,例如上面放有拾取物的桌子。

如果允许玩家翻越,我们开始安排碰撞检测探针。其方式与我们检测地表梯度的方式相同。同样,我们也会延迟一帧,其结果仅在下一帧中可用。

来到下一帧,我们将可以处理前一帧的检测结果。

我们所进行的碰撞检测是在 Aloy 前方的 扫掠球(Swept Sphere) 检测。探针将以站立高度为起点,在起点以下几米处结束。

  • 如果扫掠球体的交点高于我们当前的位置,这意味着我们应该开始分析障碍物的形状,以决定是应该 step up 还是 step over
  • 否则,如果交点相对于起始点有水平方向上的偏移,这意味着起点正下方没有地面,我们可能已经遇到了边缘,应该开始寻找可能的 step off


由于我们不对玩家使用导航网格,对于障碍物我们没有直接可用的形状数据。

因此,我们必须通过另一组碰撞检测来进行一些智能的形状分析:面前是一些彼此间隔固定的向下的射线检测,它们可以检测障碍物的深度并确定障碍物是否足够平坦以实际站立。通过查看交叉位置的高度差异,我们可以知道是否应该踏上障碍物,还是可以跨过它。对于 step off,逻辑是相同的,区别就在于交点应该比 Aloy 的当前位置更低。

一旦障碍物被分析好了,我们就存储它的形状数据,这使我们能够选择匹配的动画来触发。

而对于过渡动画的选择,我们会有一个“评分系统”。

对于每种过渡,我们都计算一个分数,最后触发得分最高的那个过渡。

首先,我们要确保对于这个障碍物数据来说,过渡是实际可能的。

每个过渡都按障碍物类型(正常或可攀爬)和翻越类型(step upstep overstep off)进行分类。

每个过渡都包含了对应翻越运动的数值。接下来,每个过渡都有所允许的范围、最大向上位移,最大向前位移等。注意,不同的过渡在范围数值上是有重叠的。

通过比较“当前障碍物的数值”与“过渡动画的数值”,我们就可以选择最适合的过渡了。又由于我们更喜欢攀爬,所以那些向可攀爬障碍物的过渡将会得到一个额外的奖励分数。

现在我们知道了如何找到合适的过渡动画,我们看看下一个问题。

由于每个过渡都支持一个可变的范围,我们将引入一个问题:障碍物的数值从不会和动画中的完全一样,那么我们该如何确保过渡动画与实际的障碍物形状相匹配呢?

我们使用 动画变形(Animation Warping) 来解决这一问题。

动画变形是对动画运动的弯曲和拉伸,以在特定时间到达特定位置。
这种方法的优点是不需要很多独特的动画来覆盖所有情况。
另一个好处时,你可以在播放过程中调整目标位置。

下图给出了“动画变形”的一个粗略的想法:

左侧是原始的运动,右侧是变形的运动。


为了随着时间的推移有效地进行扭曲调整,我们需要知道动画中任何时间点的剩余位移总量。这将需要对动画进行分析。

在《地平线》中,所有的变形都是在每个轴上独立地进行的。可以将下图视为骨骼的向前的轴上相对于起始位置随时间推移的位移轨迹:

假设我们的当前帧在橙色虚线处,那么你可以看到下一帧将包含大量向前位移,之后的帧几乎没有向前位移,之后的下一帧将有一些向后移动等等。

通过对剩余帧的位移求和,我们知道剩余的位移总量。

为了对这个动画变形,对于播放的每一帧我们都需要添加一点额外的位移和旋转。而每帧添加的量取决于“该帧的位移量”和“仍旧剩余的量”。

当前帧额外添加的位移 = (当前帧原本的位移 / 剩余的位移) * 所需的目标位移。
注意:额外添加的位移总是指向所需的目标的方向。

我们对基础的变形技术进行了若干的增强,大大提高了其易用性和变形的质量。

在我们变形的翻越运动的时候,我们需要确保手最终会精确地放在障碍物的形状上。为了实现这一点,我们扩展了我们的变形逻辑,使其可以让任何骨骼到达给定的目的地位置。

我们是这样做的:计算动画中所有点的手部骨骼与轨迹骨骼的偏移,然后减去与目标位置的偏移。这可确保手部骨骼最终到达目标位置。注意:当使用这种技术时,轨迹是由手骨的运动定义的!
(译注,这里意思有些疑惑,我的理解是:“轨迹”指的是“能使得被指引的骨骼最终到达目标位置的轨迹”,而被指引的骨骼将会在每一帧都将其修正到轨迹上。)


我们实施的另一个增强是“仅在特定时间范围内变形”。
例如,当双脚仍在地面上时,你可能不想启用变形,因为这会导致脚部滑动。
我们是通过动画事件来标注出什么时候可以启用变形的。一个动画片段中允许有多个事件。

虽然实现容易,但这是对变形一个很大的增强。
如果动画事件在一帧中未激活,我们在计算剩余位移时不会考虑这一帧的位移。下图说明了这一点:

还有一个问题是,有时你不想在动画结束时到达目的地,而是在动画中的特定时间点到达。

我们通过允许用户定义的到达时间解决了这个问题。
到达时间由动画中的特定事件指定。

当计算“剩余位移总量”时,我们将计算至用户定义的到达时间。

此后的位移不会影响变形,但可用于“到达后运动”,例如着陆动画。


我们实现的最后一个对于动画变形的增强是:保留动画的原始速度。
由动画变形引起的一个明显问题是添加位移会导致不自然的加速。
我们通过调整变形动画的播放速度解决了这个问题。

为了说明这一问题,可以看下图。

其中橙色位置的动画变形使得运动的距离变成了两倍,因此导致运动速度变为了两倍。

为了解决不自然的加速,我们修改播放速度以抵消速度的增加。在这个例子中,我们以一半的速度播放。

另外,我们也会限制动画可以播放地多快和多慢以防止看起来奇怪。我们让我们的动画师在每个动画上控制这些。

动画变形的使用让我们能够将翻越系统变成一个非常通用的系统,我们将其用于许多不同的地方。

比如潜入水中、爬出水面和抓住崖壁。

下面简短的视频展示了这些功能:

首先触发了一个step off,然后踏上可攀爬的障碍物,然后是迈过正常的障碍物。

如你所见,所有翻越动作都是自动触发的,我没有按任何按钮来选择触发哪种翻越。

我们有很多变种。有左手的,也有右手的。有跳入水中,也有从水中出来。

3.4 跳跃


并非所有的障碍都可以翻越,有时 Aloy 唯一的选择就是跳跃。
让我们来看看 Aloy 是如何跳到这条河对岸的壁架上的。

首先,我想简要地解释我们是如何用环境元数据来对世界场景进行标注的。
《地平线》的世界被分割为tile。

每个tile是 512m x 512m,目前我们有 100 多个tile。
每个tile都可以流式加载。我们会始终加载 Aloy 位置周围的 3x3 个 tile。

在一个tile中,将会包含多种环境元数据。

其中最常见的要属“碰撞网格”了。在我们这里它的精度较低,世界上到处都是。它告诉了 Aloy 哪里可以去哪里不能去。它也提供了有关撞击声音和特效的表面材料信息。

除此之外,我们还有指明是否可以游泳和躲藏的volume。

我们还有关于路的信息,它们的宽度,它们的连接以及路口在哪。

而对于Traversal系统来说,最重要的是几何体标注信息,它们指明了可以被traverse的路线。

世界上所有的几何体都可以用线来标注。他们可以被附加到静态几何体上,也可以附加到动态的可移动的几何体上。这些点与线被我们称为“标注信息”。
“标注信息”可以包含多个标签,这使得它们非常抽象通用,我将在下一页解释。
由于我们所有的几何体都是可流式加载的,因此我们的注释信息也会一起流式加载。
并且如前所述,它们为各种玩法系统提供语义,例如我们的Traversal系统。

在下图中,红线部分就是注释信息:

我们使用标签来指示对于被标注的几何体上允许使用哪种Traversal机制。

例如:“可攀爬(Climbable)”意味着 Aloy 可以将手放在被标注的几何体上;“可平衡(Balanceable)”意味着 Aloy 可以双脚站立;“不稳定(Unstable)”意味着需要播放一个附加动画来让它看起来随时可能掉下来;“可滑索(Ziplineable )”意味着被标注几何体能够充当滑索让她滑下去。

被标注的几何体上允许有多个标签。

在我继续解释Traversal机制如何利用这些标注信息之前,我想首先描述一下我们是如何设置我们的跳跃系统的,因为Traversal机制与标注信息的联系经常发生在一个跳跃中。

在《地平线》中,跳跃轨迹被分为两个阶段。
我们有由动画驱动的前阶段,和由代码驱动的后阶段。

正如之前所提及,动画驱动的运动会失去控制,因此我们添加了“空中转向”。在完整的起飞和下降阶段,我们允许调整 Aloy 的方向,这会导致轨迹弯曲。
另外,通过在玩家松开操纵杆时对前进动量应用阻尼,我们模拟了空中制动,这进一步提高了可控性。

我们使用动画驱动的起飞来确保轨迹与跳跃运动相匹配。
在之前的游戏中,轨迹完全是由代码驱动的,这使得我们的动画师很难为所有不同的速度创建匹配的动作。

尽管有了这些对可控性的改进,但朝特定目的地的跳跃是仍然不是小菜一碟。
下面的视频演示了这一点:

正如你所看到的,玩家很难确定时机并预测跳跃到特定的位置。(至少对于像我这样的业余爱好者来说很难)

我们怎样才能改善这种情况呢?

如果我们能够预测跳跃轨迹,那么我们就可以应用微小的调整来引导玩家到达特定的目的地,
一个重要的注意事项是我们绝对希望保持一种“角色仍旧在控制下”的错觉。

为了能够在运行时预测跳跃的轨迹,我们需要知道跳跃动画的完整运动。这就是我们在离线转换过程中分析我们的动画动作并将所有元数据存储在所谓的 motion table 中的原因。

motion table 是单个动画状态的动画元数据的数据库。
它们包含有关动画状态的位移、速度、时间等如何根据影响动画状态的动画变量而变化的信息。

例如,之前提到的翻越的过渡动画的数值就是存在 motion table 中的。


在离线中,我们分析输入变量的所有有效排列组合,将所有动画状态的结果运动填入我们的 motion table中。这是一项相当耗时的任务。

在运行时,我们就可以根据当前活跃的变量,来从库中查询一个给定的状态。
基于当前活跃的变量集,多个存储的结果会混合在一起以形成最终结果的元数据集。

这是一个如何工作的粗略示例:

此示例显示了一个将 3 个动画混合在一起的动画状态,混合将基于游戏逻辑设置的两个输入变量,这里是X和Y。
混合节点只是将两个动画混合在一起,权重取决于输入变量。在这里,当 X 为 0 时,100% 的 A 将与 0% 的 B 混合。而当 X 为 0.5 时,A 和 B 将等量混合。

对于我们的 motion table,我们希望针对输入值的所有排列组合预先计算出动画状态的指标。

我们只对输入的组合感兴趣。在这个例子中,X 可以是 0 或 1,Y 可以是 4 或 8。也就是有2x2=4的可能。然而,由于Y=8的时候,X等于什么都一样,所以最终只会有3种组合。因此,对于这 3 种输入值组合,动画数值会存储在表中。

现在,查询任意输入值的数据只是将存储的结果混合在一起的问题。

现在,我们能够查询跳跃运动的数据了。

我们可以通过将起跳动画的完整位移加上到起跳位置来轻松计算出顶端的位置。而这个位置将是下落阶段的起始位置。而起始速度会是起跳动画的最后一帧的速度,也可以从 motion table 中查询到。使用这些位置,可以轻松构建出如图所示的抛物线。


通过构建的抛物线轨迹,我们现在可以开始搜索附近的跳跃目的地。这是通过收集 Aloy 特定半径内所有可能的目标来完成的。我们以固定的时间步长迭代我们的跳跃轨迹,以查看在跳跃过程中的任何时刻是否可以到达目标。高于我们顶端位置的目标永远无法到达,因为那样会看起来很不自然。

如果目标足够接近我们最初预测的轨迹,我们通过将所需位移除以原始位移来计算抛物线的缩放系数。这样,我们就知道需要多少额外的速度才能到达目标。我们只允许速度变化在可调整范围内的目标。这使设计人员可以非常轻松地调整和控制这个引导系统的敏捷度。

刚才展示了如果目标位置正好在 Aloy 前面,我们如何预测轨迹,但由于我们支持空中转向,所以我们还应该允许轨迹弯曲以到达某个目的地。

我们通过构建一个经过我们当前位置和目的地位置的圆来计算所需的转弯速度来解决这一问题。我们在2D空间中做到这一计算,因为我们只关注朝向的变化。

通过计算从当前位置到圆心和目标位置到圆心的矢量之间的角度除以时间,我们得到了转向速度。如果所需的转向速度大于可调限制,我们将不允许引导至该目的地。

下面视频将全面展示这个跳跃引导系统:

3.5 攀爬


《地平线》的世界充满了这样的岩壁,毫无疑问,Aloy 可以攀爬。


为了给我们的设计师最大限度的控制,我们使用几何体标注信息来控制 Aloy 在哪可以攀爬。

当攀爬时,她物理上被连接于几何体的一个点上。比如当使用手来攀爬时,这个点和手相连。这也是 Aloy 的轨迹关节所在的位置。当 Aloy 是站在被标注的几何体上时,被连接的点在双脚之间。

当从一个被标注的几何体爬到另一个被标注的几何体时,我们会触发过渡动画。

首先,我们寻找所有附近的目的地,并找到距离和角度最佳组合的目的地。

然后我们遍历所有可能的过渡动画(我们有超过 100 个!)并找到需要最少运动变形的那个。每个过渡都有我们考虑的条件和范围,我们还会检查过渡的运动是否不会与任何东西发生碰撞。

最后,我们将触发我们找到的最适合的过渡动画并将其变形到我们找到的目的地。

在攀爬过程中,我们还在手和脚上使用 IK 来引导它们。

对于手,我们始终确保它们被放在几何体上。这个位置可以是当前的位置,在过渡动画中会是目标位置。

在攀爬时,我们还会不断检查我们的脚是否可以靠在我们面前的几何体上。如果脚部位置没有可用的几何体,则可以关闭脚部的 IK,让脚部悬垂。

为了找到 IK 静止位置,我们使用多个碰撞探针来找到相交位置。

对于手部,我们使用一个稍微倾斜的探针穿过手掌,对于脚部,我们使用两组碰撞探针,让 Aloy 能够更多地拉起她的腿。

四个肢体中的每一个都可以单独控制。

我们的动画控制手和脚什么时候应该放在几何体上,什么时候不应该。动画中具有驱动我们的 IK 和锁定四肢的事件。

下一个视频显示了探针和 IK 的作用。

这里 Aloy 在手脚上启用了 IK。 绿线可视化了相交测试。红线表示脚没有可以靠在上面的有效几何体。

我们面临的更大挑战之一是支持攀爬动态的物体,例如 Tallneck(形似长颈鹿的机器人)。

例如,并非我们游戏中的所有资产都具有相同的更新频率。Aloy 的更新频率高于我们的 Tallneck。
因此,当跳向Tallneck或攀爬Tallneck 时,我们必须针对更新频率的差异来补偿所有Tallneck的标注几何体的运动。

接下来,我们还必须正确地将我们所附着的物体的运动(当攀爬或降落在某物上时)应用到 Aloy 的身体上。

为了确保我们的脚和手正确地放在攀爬的资产上,碰撞探针也需要与所需的更新频率同步。由于 Tallnecks 以低得多的频率更新,因此它们的碰撞体积也以低得多的频率移动。因此,Aloy 的碰撞探针也应以相同的较低频率执行,以保持同步。

4. 事后分析

最后,我想讨论下我们认为哪些做得好,哪些做得不好。

首先,对我们来说很有帮助的是,在过渡动画中,能够基于事件区分早期反应和晚期反应。这真得让我们能够让角色在控制上感觉更灵敏,并帮助我们提高运动系统的整体质量。

几何体标注信息上抽象的标签,为我们的Traversal系统提供了语义,使我们能够创建一个非常可靠的跳跃预测系统以及一个非常灵活的攀爬系统。我们基于到达目的地所需的位移来选择攀爬的过渡动画。爬升系统甚至设法爬上了移动的动态几何体。

在跳跃引导系统中,只有两个变量需要调整:所允许的速度的最大变化和转向速度的最大变化,因此设计师能够在活跃性与真实性之间找到想要的平衡。引导轨迹还允许我们预测着陆,从而可以无缝融合到适合目的地的着陆动画;例如Tallneck、高空滑索、岩壁等。

那么哪些地方做得不好呢?

因为我们的大部分Traversal系统和工具都是在从 Killzone 切换到 Horizon 时从头开发的,所以我们的关卡设计师最初没有可视化和编辑器工具来优化Traversal区域。我们原本是想在世界上有更多的Traversal路径的。

虽然翻越系统最终运行良好,但这可能不是实现它的最好和最高效的方式。

另一个很大的改进是对玩家导航网格的支持。因为这也可以解决其他问题,例如防止玩家卡住或进入无法进入的区域。

最后但同样重要的是,我们最终的动画网络变得非常复杂且难以维护。

这主要是因为程序员和动画师都对其进行了编辑。

我们正在研究通过将更多的决策逻辑从网络转移到代码,以及对网络应用更严格的所有权,使事情更易于管理。

5. 未来计划

那么接下来我们有什么想法呢?

去年在 GDC 上对我们来说最有趣的演讲之一是育碧蒙特利尔公司的 Simon Clavet 关于Motion Matching的演讲:https://www.gdcvault.com/play/1022985/Motion-Matching%02and-The-Road。这与我们一直在《地平线》中所探索的东西有一些相似之处,因此我们对研究它的一些可能性非常感兴趣。

正如在事后分析中已经提到的,我们可能会研究通过使用导航网格来进行玩家导航。

我们也对将 FBIK 用于我们的各种机制非常感兴趣。我们没有在《地平线》中使用它,因为时间限制,而常规的两骨骼 IK 也提供了很好的结果。但 FBIK 听起来是一个非常有用的升级。

翻译:Player Traversal Mechanics in the Vast World of ‘Horizon: Zero Dawn‘相关推荐

  1. 【GDC翻译】“地平线零之曙光”中基于GPU的程序化实时放置系统

    原视频:GDC Vault - GPU-Based Run-Time Procedural Placement in 'Horizon: Zero Dawn' PDF:https://www.gdcv ...

  2. 沉浸式ui设计_有助于沉浸的视频游戏UI —武器轮

    沉浸式ui设计 Many action-adventure games rely on the feeling of thrills via bullets, fire, grenade, more ...

  3. 游戏用户体验指标_电子游戏如何超越游戏化的用户体验

    游戏用户体验指标 游戏UX (GAMES UX) During a time when the time spent on video games has reached record breakin ...

  4. 火焰和烟雾的训练图像数据集_游戏开发者是烟雾和镜子的大师

    火焰和烟雾的训练图像数据集 Video games are incredible. They transport us to new worlds, allow us to partake in ot ...

  5. 移动游戏机和PC已合并游戏的奇点

    As crossplay unifies player communities, gaming hardware differences are less relevant than ever. Th ...

  6. 最伟大的PlayStation游戏机

    Depending on what kind of games you enjoy, you're either nodding your head in excitement right now, ...

  7. ExoPlayer 开发者指导

    ExoPlayer 开发者指导 字数2134 阅读12051 评论36 喜欢32 原文地址 想深入了解ExoPlayer的童鞋可以查看我的另外一篇文章:ExoPlayer源码浅析 Developer ...

  8. 地平线黎明时分dlc评测_我从第一个玩法中学到的地平线零黎明的提示

    地平线黎明时分dlc评测 Horizon Zero Dawn is the best PlayStation game of 2017. I recently finished my first pl ...

  9. 阅读笔记 - Horizon Zero Dawn 广袤世界中的玩家漫游

    最开始我是忽略了这篇演讲的,因为Player Traversal是啥并没看懂- -b 后来看到 @顾露 大神在技术选荐中推荐了它,才拖下来看了看. 没想到这篇讲角色Locomotion的演讲中信息量意 ...

最新文章

  1. [CSAcademy]A-Game
  2. Linux查看CPU相关信息
  3. SAP Cloud for Customer 如何直接消费S/4HANA API
  4. MySQL nullif()函数
  5. 【01背包】洛谷P1282多米诺骨牌
  6. LeetCode 2. Add Two Numbers
  7. emlog通过pjax实现无刷新加载网页--完美解决cnzz统计和javascript失效问题
  8. 前苹果M1芯片设计总监跳槽英特尔 或将负责所有SoC架构设计
  9. 1.1.0-简介-P12-分布式锁的解决方案(二)
  10. python比较文件的修改时间,如何比较python中两个文件的修改日期?
  11. numpy广播机制小结
  12. 优化器,sgd,adam等
  13. html链接鼠标在悬停颜色,如何让HTML链接显示悬停样式?
  14. 四十五、Kafka生产者(Producer)API介绍
  15. 路由器连接上但上不了网是什么故障
  16. html根据地点名称查坐标,根据地址查询经纬度Js
  17. IPC Send timeout/node eviction etc with high packet reassembles failure
  18. python2exe_Python 使用Py2exe进行bin化
  19. idea中Hierarchy出现在页面右侧
  20. Lucene之——搜索实例

热门文章

  1. 下乡记nbsp;-nbsp;共20张照片
  2. Maya奔跑动画制作
  3. 计算机中职生假期打算600字,中职计算机教学计划范文两篇
  4. MYSQL递归查询,根据子类ID查询所有父类(宇宙第一详细教程)
  5. 【服务器使用】SSH公钥密钥连接
  6. Python找寻知乎最美最帅之人!我才是最帅的那个呀~
  7. awesome PyQt5 的奇技淫巧
  8. 空气质量监控难?飞凌基于i.MX6UL、AM335x设计扬尘在线监测仪显身手
  9. cocos2d-x项目打包发布到Win8应用商店
  10. 想不起歌名了?播放给“音乐猎手”听,让它告诉你答案 | 36氪