你好!我是弗拉基米尔·波波夫 (Vladimir Popov),战争机器人项目的客户端开发人员。在撰写本文时,《战争机器人》已经问世多年,在此期间游戏中出现了数十种新机甲。当然,机器人的各种能力很重要,因为没有它们,机器人就会失去其独特性,从而使游戏变得更有趣。
在这篇文章中,我将分享游戏能力系统在我们的游戏中如何运作以及它是如何演变的。而且,为了让事情更容易理解,我将用简单的术语来解释事情,而不需要太多的技术细节。
首先,让我们深入了解项目历史并查看不再使用的旧实现。
以前,能力的设计方式非常简单:它们有一个附加到机器人上的组件。在这种结构中,程序员充分描述了该能力的工作原理:它的流程以及它如何与其他能力交互。所有逻辑都在一个组件内描述,游戏设计者可以简单地将其连接到机器人并根据需要配置参数。值得一提的是,能力的流程是不可能改变的——游戏设计师只能改变参数和时间。
旧的能力只能存在于两种状态:活动和非活动,并且每个状态都可以分配给它的动作。
我们来看看机器人“Stalker”之前所拥有的“Jammer”能力的例子;它的工作原理是这样的:
在很长一段时间内,这个功能对我们来说已经足够了,但随着时间的推移,游戏设计者和程序员都不再满足于这种方法:程序员很难支持这些功能,因为代码已经变得巨大;这涉及一个很长的遗留链,必须描述每种情况。此外,游戏设计师缺乏灵活性——要对能力进行任何更改,他们必须向程序员下令进行修改,即使相邻的能力具有相同的功能。
因此,我们意识到有些事情需要改变。因此,我们开发了一个新系统,其中每种能力都表示为一组几个相关对象。功能分为状态能力和状态组件。
它是如何工作的?每个能力都有一个主要对象。这个中心对象将其他能力对象与外界连接起来,反之亦然;它还做出所有主要决定。
可以有任意数量的状态。本质上,本次迭代中的状态与旧版本中的活动/非活动状态没有太大区别,但现在可以有任意数量的状态,并且它们的目的变得更加抽象。我们会注意到,一项能力一次只能有一个状态处于活动状态。
与旧系统相比,主要的创新在于组件:组件描述一些动作,每个状态可以有任意数量的组件。
新能力如何发挥作用?一种能力只能处于其中一种状态;主对象负责切换它们。链接到状态的组件会对状态的激活/停用做出反应,并且根据此情况,可以开始执行某些操作或停止执行该操作。
所有对象都变得可定制;游戏设计师可以根据需要混合状态和组件,从而从预安装的块中组合出新的能力。现在,程序员只需要输入图片即可创建新的组件或状态,这使得编写代码变得更加容易:他们与小实体一起工作,描述一些简单的元素,而不再自己构建能力——游戏设计师现在这样做。
流程变成了这样:
随后,一次又一次地重复该过程。为了便于使用,状态不仅充当组件容器,它还确定何时切换到另一个状态并请求主对象进行切换。随着时间的推移,这对我们来说仍然不够,能力图变成了下面的样子:
主要对象、状态和组件保留在原来的位置,但也添加了新元素。
首先引起您注意的是我们为每个状态和组件添加了条件:对于状态,这些定义了离开状态的附加要求;对于组件,它们确定组件是否可以执行其操作。
电荷容器包含电荷,对其进行充电,必要时停止充电,并提供电荷供各州使用。
当多个状态必须具有共同的执行时间,但它们自己的执行时间未定义时,使用计时器。
需要注意的是,所有能力对象都是可选的。从技术上讲,对于一种工作能力,只需要一个主要对象和一个状态。
现在,虽然实际上并没有那么多能力完全在没有程序员参与的情况下构建,但总体而言,开发变得明显便宜,因为程序员现在只需要编写非常小的东西:例如,一个新状态或两个组件 - 其余的被重复使用。
让我们总结一下我们的能力的组成部分:
主要对象执行状态机的功能。它为状态和组件提供有关世界的信息,并为世界提供有关能力的信息。主要对象充当功能的状态、组件和服务部分之间的链接:费用和外部计时器。
该状态监听来自主对象的激活和停用命令,并相应地激活和停用组件,并请求主对象切换到另一个状态。 State决定何时需要切换到下一个;为此,它使用其内部条件:玩家是否单击了能力按钮、状态激活后是否已经过去了一定时间等,以及与状态相关的外部条件。
该组件侦听来自状态的激活和停用命令并执行某些操作:离散或长期。动作可以完全不同:它们可能会造成伤害、治愈盟友、打开动画等等。
该条件检查所需元素所处的状态并将其报告给状态或组件。条件可能很复杂。如果不满足条件,一个状态不会请求转换到另一个状态。如果不满足条件,该组件也不会执行操作。条件是可选实体;并非所有能力都有。
充电容器保存电荷,对其进行充电,必要时停止充电,并向各州提供电荷。它用于多次充电能力,当你需要允许玩家使用它多次,但连续不超过n次时。
当多个状态具有共同的持续时间但不知道每个状态将持续多长时间时,将使用计时器。任何状态都可以启动一个n秒的计时器。所有相关状态都会订阅计时器结束事件,并在计时器结束时执行某些操作。
现在让我们回到能力图。它的功能发生了怎样的变化?
各州可以使用收费作为附加过渡条件。如果发生这种转变,电荷数量就会减少。各州还可以使用通用定时器。在这种情况下,它们执行的总时间将由计时器确定,并且每个状态单独可以持续任意时间。
我们并没有为新的能力用户界面彻底重新发明轮子。
主要对象有自己的 UI。它定义了一些应始终位于 UI 中且不依赖于当前活动状态的元素。
每个状态在 UI 中都有自己的对,并且只有当其状态处于活动状态时才会显示状态 UI。它接收有关其状态的数据并可以以一种或另一种方式显示它。例如,持续时间状态的 UI 中通常有一个栏和文本来显示剩余时间。
在状态正在等待外部命令来继续执行能力的情况下,其 UI 会显示一个按钮,按下该按钮会将命令发送到状态。
我们将通过具体示例来了解这些能力是如何发挥作用的;首先,我们来看看一个叫做“审判官”的机器人。我们有四个相互关联的状态 - 在这些状态上方,您可以看到它们在 UI 中的显示。对于其中两个,我们还可以看到属于它们的组件;其他两个州根本没有组件。
以下是该能力的流程:
这一切都始于“WaitForClick”状态。这个时候,这个能力就没有任何作用;它只是等待命令。
一旦收到这样的命令,主要对象就会切换状态。下一个活动状态是“WaitForGrounded”。
该状态有一些组件,因此,当激活时,机器人会跳跃并播放声音和动画。除此之外,当状态处于活动状态时,机器人会受到干扰器效应的影响,从而禁止瞄准机器人。
当机器人着陆时,其能力将转移到下一个状态。
该状态包含三个组成部分:已经熟悉的声音和干扰器,以及抖动,它会导致半径n内的所有玩家的相机抖动。
由于此状态有Duration ,因此它会持续n秒,然后该能力会移动到下一个状态。
最后一个状态还带有持续时间,但它没有任何组件:它处于常规冷却时间。
完成后,该能力将返回到第一个状态。
另一个例子是《幻影》。它很像《审判官》,但有一些细微差别:
我们从 WaitForClick 开始。
然后,持续时间,其中安装了传送,改变了机甲的统计数据,并播放了声音和动画。
之后:DurationOrClick,其中机甲的统计数据发生变化,动画和 FX 被播放。
如果单击,我们会进入另一个持续时间,其中机甲会传送、统计数据会发生变化,并会播放动画、FX 和声音。
在此状态之后(或在 DurationOrClick 的时间到期之后),我们转到 Duration。
这里的主要区别在于我们看到带有分支的状态:如果指定时间已过,则 DurationOrClick 进入状态 A;如果玩家之前按下了能力按钮,则进入状态 B。
虽然我们的系统似乎已经从简单的东西发展到相当复杂的东西,但这种变化简化了程序员和游戏设计师的生活。现在,在添加小组件时,主要需要程序员的帮助,而后一组团队成员获得了更大的自主权,现在可以独立地从现有状态和组件中组装出新的能力。与此同时,作为另一个奖励,玩家还获得了机甲能力更加多样化和复杂化的收益。