`
txf2004
  • 浏览: 6852998 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

创建游戏中的人类世界:数据驱动的开放世界AI框架

阅读更多

//原文链接:http://www.gamasutra.com/view/feature/1862/creating_all_humans_a_datadriven_.php?page=3

A Gamasutra featured article

Creating All Humans: A Data-Driven AI Framework for Open Game Worlds

创建人类世界:数据驱动的开放世界AI框架

Populating an entire game world with characters that give an impression of life is a challenging task, and it's certainly no simpler in an open world, where gameplay is less restricted and players are free to roam and experience the world however they choose. The game engine has to be flexible enough to react and create interesting scenarios wherever the player goes. In particular, the demands on the AI are different from a linear game, requiring an approach that, while using established game AI techniques, emphasizes a different aspect of the architecture.

赋予游戏世界生动逼真的人类生活是一项很具挑战性的工作。在开放游戏世界下,游戏范围受到限制但是玩家可以自由的漫游在开放的世界并且时时刻刻体会他们选择的这个虚拟的世界,开放游戏世界下完成这项工作不会带来任何简化。游戏引擎必须担负这样的一项使命:即无论玩家走到哪里,它都必须创建一个生动活泼的场景。特别的这项工作对AI的要求和线性游戏AI的要求不一样,虽然采用的仍然是现存的AI策略,但是整个AI的架构不尽相同。

This article discusses the data-driven AI architecture constructed for Pandemic Studios' open world title Destroy All Humans 2. It describes the framework that holds the data-defined behaviors that characters perform, and how those behaviors are created, pieced together, and customized.

这篇文章讨论了数据驱动的AI架构,该架构由Pandemic工作室的开发世界为游戏‘毁灭人类2’构建,它描述了框架如何产生角色所需的行为动画,将其放置在一起,并定制最后呈现在场景中。

The premise of an open world game with sandbox gameplay is to give players the freedom to do what they want, the freedom to create their own game within the world the developers provide. Their play is not linear, which is fantastic for a sense of immersion, but reduces the ability of the game developer to control, limit, and pre-script scenarios that the players encounter.

受限的开放世界游戏的出现就是给予玩家自由在这个世界里创建他们自己想要的游戏,其游戏性是非线性的,无疑带来很高的沉浸感,但是同时降低了游戏开发人员控制,限制,预先制定场景脚本的能力

The AI code needs to be built on a foundation that is flexible enough to respond to any eventuality. It needs to handle a domain of gameplay that is broader in scope than a linear title and react to situations that might not have been anticipated. In effect, the AI needs to have a strong emphasis on breadth of behavior over depth. That is, the architecture must promote the ability to create large numbers of behaviors and make applying them to characters as easy as possible.

这样的AI代码需要构建在这样的基础上:灵活并且能够响应各种偶然事件。它需要处理比通常的线性游戏更大的游戏范围,也就是要处理那些没有预期到的场景。从实质来说就是AI构建要更加注重广度而不是深度,即AI框架必须要能够创建非常广泛的行为并且将其应用于各种各样潜在的场景。

One solution to this challenge is to make the behaviors data-driven. They should be created without requiring changes to code, pieced together and reused as shared components, and substituted out for specialized versions. Ideally, the developer should be able to tweak not only the settings of a behavior, such as how long a timer lasts or how aggressive an enemy is, but also the very structure of the behavior itself. For example, what steps are needed to complete a given task or define how those steps are performed? By allowing our behaviors to fit into multiple situations, be reusable, and be quick to create and customize, we can more effectively create all the actions that the characters will need to give the game life.

解决方法之一就是使行为的定义变成数据驱动的,行为应该的创建应该不影响到编码,应该可以被串接起来并且能够复用。同时能够被泛化的版本替代;理想的说开发者不仅应该把握行为包含的属性例如:持续时间多长,攻击力为多少;同时也要能够改变行为的过程,例如:完成指定的任务需要哪些步骤。通过定义这样的行为,他们易于应该到各种场景,能够复用,并且能够迅速的创建和定制,这样我们就能够更加有效的创建给予游戏中声明丰富行为的开放世界游戏。

Behavior Foundation: Performing Tasks with Sub-Tasks

行为基础:将任务分解为子任务

The basis for the behavior system in Destroy All Humans 2 is a hierarchical finite state machine (HFSM), in which the current state of an actor is defined on multiple levels of abstraction. At each level in the hierarchy, the states will potentially use sub-level states to break their tasks into smaller problems (for example, attackenemies is at a high level of abstraction and uses the less-abstract fireweapon below it to perform part of its function). This HFSM structure is a common method used in game AI to frame a character's behaviors. It has several immediate benefits over a flat FSM. (For current information about HFSMs, see Resources)

毁灭人类2中的行为系统的基础是树状的有限状态机hierarchical finite state machine (HFSM),该技术下角色的当前状态是通过多层不同程度的抽象完成的,该行为树状体系中的每一层 其对应属性都会使用其子节点的抽象来构建更高一层的行为状态。例如:攻击敌人行为是一种高层的行为抽象,其子节点则可能定义了开火这样的行为抽象。HFSM架构是游戏AI中架构游戏角色行为AI的一种常见手段,和平展的状态机相比有许多优点。(关于HFSM的更多信息可以参见附录)

In our implementation, each state in the HFSM is called a behavior and makes up the basic architectural unit of the system. Everything that characters can do in the game is constructed by piecing together behaviors in different ways that are allowed by the HFSM. A behavior can start more behaviors beneath it that will run as children, each performing a smaller (and more concrete) part of the task of the parent. Breaking each task into smaller pieces allows us to reap a lot of mileage out of the behavior unit-reusing it in other behaviors, overriding it in special cases, dynamically changing the structure, and so forth-and spend more time making the system intuitive and easy to modify.

在我们的实现中,HFSM中的每一个状态被称为行为并且构成了系统的行为基本单位。角色能够完成的所有行为都是通过以不同的路径串接这些基本的行为得到的,通过一个行为的下属子行为角色能够完成更多不同的行为,每一个子行为都进行一种相对于父行为更为具体的任务,将一个任务分解为一系列子任务使得我们能够最大程度的复用行为,创建特殊情况下的行为,动态的改变行为等等。当然这样的系统更具启发性并且更易于改写。

Starting children. There are many ways to break a task into smaller pieces, and the correct choice ultimately depends on the type of task. Does the task require maintaining certain requirements, performing consecutive steps, randomly performing an action from a list, or something else? In our implementation, we allow several methods of breaking down a behavior into smaller pieces by allowing different ways to start children behaviors.

启动子行为. 有很多种策略将行为分解成更小的子行为,正确的方法取决于任务的类型,例如任务需要满足特殊的需要么?连续的执行各个子步骤还是随机的执行子步骤,或者其他?我们的实现中,我们通过允许以不同的方式启动一项任务这样将一项任务分解为各项子任务

Prioritized children. The first and most common way to start children is as a list of prioritized behaviors. Behaviors that are started as prioritized will all be constructed at once (memory is allocated for them and they are added as children of the parent) and set into a special pending mode. (See Figure 1.)

优先的子行为. 最常见开始子行为的方式是通过列出优先排序后的行为完成的,优先启动的行为马上就会被创建(内存分配后被添加为父行为的子状态节点)然后被设置为一种特殊的悬挂模式(见图1)

FIGURE 1 A combat behavior starts prioritized children, which in turn starts more prioritized children. Pending behaviors are orange and active ones are blue.
图1 一个战斗行为启动了一个优先子状态,该子状态启动了更多的优先子状态
悬挂状态为橙色,活动状态为蓝色

When a behavior is in pending mode, it is not updated; instead, it waits until the behavior itself decides it can activate, based on its own settings. When activated, the behavior will in turn start any children it has. Only active behaviors can have children, so a pending behavior will wait before starting its children.

As a rule, only one active behavior can run beneath a given parent, which creates a problem: what to do when multiple behaviors are able to run. We need to set a priority to determine which sub-task is more important. When starting children as prioritized, we define their priority implicitly based on the order the behaviors are added to the parent. The earlier a behavior is listed, the higher its priority (see Isla in Resources).

当一种行为处于挂起状态的时候,它不会被更新;相反,它会等行为自身通过判断其本身的一些设置决定它本身是否应该被激活,当行为被激活后它可以激活任意的子行为。只有活动的行为才会有子行为,所以挂起的行为会等待被激活然后启动其子行为。作为规定,只有一个活动的状态可以在一个给定的父状态下运行,这也带来了一个问题:当一个父行为对应若干个子行为可以运行的时候应该运行哪个行为呢?我们需要设定一个优先级来决定到底哪一个子行为更加重要,当启动的子行为被优先排序后我们可以隐式的定义优先权为它们被添加到父状态的顺序。行为越早被加入到父状态节点中行为就拥有越高的优先权(参见附录中的ISLA)

This solution avoids the problem that would have resulted had we determined priority strictly by number, such as priority-creep, in which priorities become larger and larger, trying to trump the rest. Here we localize the priority definition, so it's only relative to the small subset of behaviors that are started as siblings.

这种办法避免了严格依照预定义的数字作为优先权的问题,例如优先权怪物问题:优先权的值变得越来越大因为对应的行为要优于某行为。这样我们就限定了优先权的定义,只有节点的一个小子集可以作为优先权限定。

In the example above, we see a hierarchy of behaviors and the children available under each, with fire active as a child of attack, which in turn is active as a child of combat. If the currently pending behavior (dodge) were to determine that it needs to start (when the NPC detects it is being fired upon), it will interrupt its active sibling (attack) and revert it to pending, which in turn will delete all children of attack. Once no other active sibling behaviors are running, dodge may begin.

在上面的例子中,我们看见了行为体系树和它可用的状态,FIRE是ATTACK行为的子行为并被激活,而ATTACK又是COMBAT的子行为,如果当前的挂起状态闪避决定它要被激活(例如NPC检测到了攻击行为),那么它就会打断其兄弟行为 攻击的执行,将其转换到挂起状态,这一操作会导致攻击状态的所有子状态全部被删除,一旦没有其他的兄弟行为正在运行那么 闪避状态就可以转入运行了

This method of applying priority implicitly works well in most cases, but sometimes the importance of the tasks cannot be described with a simple linear ordering. To handle cases in which a behavior is doing something important and should not be interrupted by non-critical tasks (even if they're higher priority), we can implement a feature called "can interrupt." Essentially, an active behavior may receive a boost in priority, preventing interruption during specific parts of its execution.

通常这种采用优先排序的隐式策略能够很好的工作,但是有时复杂任务不能通过简单的线性排序完成,为了描述系统正在完成一项复杂的工作而不应被非重要任务打断的时候,我们引入了一种称为“可以打断”的策略。实际上,一个激活的行为可以优先接受一个优先级提高这样就可以避免该任务在执行的时候被打断了。

With this boost, priorities can be specified in ways more complex than simple linear ordering. For example, while melee is listed at a higher priority than dodge in Figure 1, it should still be allowed to finish its animation even if dodge decides to start-by giving it a boost in priority while running, we prevent dodge from cutting it off mid-animation.

通过这种优先级提升,优先权的定制就可以更加复杂了而不是原先的线性控制了。例如在图1中‘混战’的优先级要高于‘闪避’,但是闪避仍然可以完成其动画的执行,只要在开始激活的时候给予其优先级提升,这样就可以避免闪避动画被‘混战’中断。

Sequential and random children. Other ways to start child behaviors are known as sequential and random. Behaviors that are started sequentially are run in the order that they are listed. If the first can run, it will do so until it completes on its own, followed by the second, and so on. When the last behavior in the sequence finishes, the parent finishes as well. For a group of child behaviors started randomly, only one will be chosen to run, and the parent will complete once its child finishes.

顺序和乱序的子状态. 其他的启动子行为的方法被称为顺序和乱序. 顺序启动的行为并按照其列出的顺序执行完,这样的行为执行成为顺序执行,如果第一个行为可以执行,那么它启动并且完成它的任务,然后第二个执行以此类推,当序列中的最后一个行为执行完毕那么该序列行为也执行完毕。如果一个群组中的子行为任意的开始,只有一个会被选中执行,当该选中任务执行完毕后,改乱序执行也就执行完毕

Non-blocking children. Behaviors can also be started as non-blocking, in which case they may activate even if there are already other active behaviors running beneath the parent. They exist outside the prioritized list. These behaviors are useful for performing tasks that work simultaneously with others, such as firing while moving, or playing a voice over on a specific condition, or activating and deactivating effects. Generally, anything performed by a non-blocking behavior must not interfere with any other sibling behaviors that might be running, since a non-blocking behavior will only be interrupted when its parent is deactivated (and never by a sibling behavior).

非阻塞的子行为 行为同样可以有非阻塞的行为,即使已经有其他激活的行为在其父行为下运行,该非阻塞行为也可以运行,非阻塞行为存在于优先队列之外。这样的行为对于执行那些并发的行为是非常有用的,例如行进的时候开火,例如在特定的情况下发出声音,又如激活和阻塞效果。总之,任何非阻塞行为进行任何动作不能妨碍当前正在运行的行为的执行,只有其父行为被阻塞,非阻塞行为才会被阻塞;非阻塞行为永远不会被其兄弟行为阻塞

By using various combinations of these methods up and down the tree, we can form decisions and task handling over multiple levels that would be difficult to define in a single behavior.

通过采用这些策略的各种组合,穿梭于该多层决策树,我们可以形成决定和通过多层协调的任务的解决方案,而这一些如果只用单层平展的决策状态机是很难完成的。

Building New Puzzle Pieces构建新的解谜部件

From this framework, we have defined the basic unit that will be used to construct HFSMs: the behavior. Now, by making behaviors sufficiently data-driven, we can expose not only their values and settings for modification, but the structure of the actions they perform as well. (See Figure 2.)

从这个框架,我们已经定义了用来创建HFSM的基本单位,而行为就要通过数据驱动的策略来构成,我们不仅可以暴露行为和值的设定以供修改,而且行为的组成也可以拿出来修改

FIGURE 2 When either of this behaviour unit's preconditions are satisfied, it will activate, passing the stimulus source parameter to its two children. Since the children are prioritized, the higher-priority flee child will run whenever it can, allowing cower to activate only when it fails.
图2 当任意两个该单元被激活的前置条件满足的时候,该行为就会被激活,同时将刺激源参数传递给其子状态,由于其子状态是优先排序的,因此当条件满足的时候只有‘逃跑’状态会被激活,当逃跑失效的时候,‘退缩’状态才可能启动

Each behavior is self-contained. Everything about it is determined within the behavior itself: when it can activate, when it can no longer run, what interrupts it, and what it does on activation, deactivation, and update. Most significantly, though, it defines what children it starts. Each of these features of a behavior are set within a .behavior file, one per behavior, which is parsed and read in with the rest of the game data.

每个行为都是自包含的,所有关于该行为的策略制定都是在其内部完成的;什么时候被激活,什么时候停止运行,什么状态能够打断其执行,激活的时候完成什么,阻塞的时候完成什么,更新的时候完成什么;最重要的是它决定了它的哪一个子行为应该启动,所有的行为的这些特性都是通过.behavior文件内部的定义来完成的,每个行为对应一个behavior文件;该文件于游戏的运行的时候被解析并使用。

Once a behavior is created in a pending state, its duty from then on is to decide when it can activate, since it contains its own activation requirements. These requirements are defined by attaching a list of precondition objects to the behavior. A precondition is a set of rules that may be evaluated to be true or false after checking conditions from the world. Game events, the health level of the character, and commands issued from a squad leader are all conditions that can contribute to activating a behavior, and a precondition can be configured to query any of them.

一旦一个行为被创建并被挂起,那么自那时起其任务就是要决定它什么时候应该被激活,状态控制文件里包含了它被激活的前置条件;这些条件通过为该行为设置一系列的前置条件而起作用;所谓的潜质条件就是一系列能够在运行时判定真假的规则,在检测游戏条件,游戏事件,角色的健康状态,以及命令发射情况等等,这些做法都可以作为行为被激活的前置条件。当然任何的前置条件也应该被设计为可以查询这些游戏条件。

The settings that define a precondition are stored in a separate file and can be reused by other behaviors as well. This allows us to construct a library of preconditions that are easily selected for a new behavior simply by listing them in the behaviors configuration file.

这些前置条件的设置被保存在另外单独的文件中以便于其他行为的复用,这样就使得我们构建一个前置条件库。这样做以后设计前置条件的时候我们只需要在行为配置文件中列出前置条件库中对应的前置条件而已。

Once a behavior is activated, it can perform its actual purpose, which in most straightforward cases is data-defined as well. Any behavior can start an asset on the character (play an animation, sound, or effect) or start more children without requiring any code side changes, which gives us tremendous flexibility in being able to quickly flesh out the structure of new behaviors. For more complicated actions, we use code-supported behaviors.

一旦一个行为被激活,那么它就可以完成其使命,行为的目的大多数的情况下也是通过数据来定义的。任何的行为都可以给角色带来一系列的变化(例如播放一段动画,声音,或者是效果),也可以在不改变代码端的情况下引起更多的行为,这样的实际就给我们迅速的设计新的行为组织带来了巨大的便捷.对于更多复杂的行为,我们将采用代码端支持的特殊定义行为法

A code-supported behavior has all the configuration settings available to a base-level behavior, but with some extras available that plug into a corresponding module created in code. Fireweapon, pathfollow, and melee are examples of behaviors that have a code side associated with them, performing actions that would be too specific to generalize and make available for all behaviors. In fireweapon we have, for example, settings for "delay between shots" and "shots to fire in a burst," that would be useless on most other behaviors. These code-supported behaviors fit into the tree like any behavior and are typically found as the leaves of an HFSM, used at the bottom level as children of more abstract behaviors. Generally, the role of the higher-level behaviors is to separate out objects and tasks that need performing on those objects, and then start children to make them happen.

代码端支持的特殊性为具有基本级别行为的所有设定,不同之处在于它额外的需要代码端创建的一个相应的特殊的插入模块.武器开火,行进路径,逃跑都是具有代码端支持的特殊行为,这些行为执行时候具有一般行为所不具有的特殊性。例如在‘武器开火’行为中我们具有 开火延迟 突然开火这样对立的突发状态,这些状态对于其他大多数的行为来说是没有意义的。这些特殊的行为也可以整合进原来的行为状态树而且通常作为叶子节点出现,也即作为最具体的行为出现。总而言之更高级别抽象行为的任务就是要分理处这样的行为并决定作用于哪些对象,并且在指定的时候启动这些子行为。

However, because the objects we're dealing with aren't defined until the game is actually running (such as the current target of a character during a battle or the last person to damage the player), we need a way to reference and make decisions about those objects within our behaviors. We need a parameter system.

然而,因为行为所处理的对象要到游戏运行的时候才会产生(例如当前的攻击目标,攻击我的玩家),我们需要一种方式来引用这些对象,并且在行为内部制定决策如何决定这些对象,我们需要一种参数系统。

Connecting Objects to Behaviors

将作用对象关联到行为

Giving behaviors a list of settings in their configuration file adds a lot of generalization to the system, but it does not allow objects to be dynamically manipulated at run time, responding to arbitrary situations. For example, the target of fireweapon cannot be known when that behavior is being written, as it will vary. Each behavior must be able to understand and make decisions about arbitrary game objects in the world, decided at run time.

给予行为配置文件一系列设定的做法是的系统的兼容性大大提高,但是它并没有使得行为的作用对象在运行时得到处理,例如 ‘开火’行为的作用对象无法获知,因为该对象在运行的时候是不断变化的。每一个行为必须能够理解并决定在游戏运行时候的任意的游戏对象。

The solution we used in Destroy All Humans 2 was to allow parameters to be passed from a parent to its children when the children were created, letting those behaviors query and manipulate the object passed in as a parameter or pass it along to their own children.

我们在游戏 毁灭人类2 中用到的解决策略是当子行为创建的时候允许参数能够从父行为传递到子行为。允许这些被创建的子行为查询或者使用这些游戏对象或者它们将其传递给它们自己的子行为

Any behavior that needs to accept a parameter defines a slot for it, which must be filled by whatever parent behavior activates it as a child. From there, the variable in that slot can be used in a number of ways: it can be passed on to its own children, sent an event or message, or, for code-supported behaviors, made available to the code side. Pathfollow will seek to the object passed as its first parameter, for example, while melee will swing in the direction of its first parameter.

任意需要接受参数的行为就定义了一个插槽,这些插槽需要其父行为在创建他们的时候填充。从那时起这些插槽就可以派上不同的用处了:可以传递给自己的子行为,发送一个事件或者行为,或者对于代码支持的行为,这些对象就可以为代码插件所用。 例如寻找路径行为将寻找传送给它的第一个对象,而逃跑则会扔掉传递给他的第一个参数

With this addition, the states in our HFSM can essentially send objects along their transitions (in our case, between parent and child), plugging them into other behaviors that expect them and use them as a target of their functionality. It's a way to blend some script-like functionality into the more rigid structure of an HFSM, giving extra flexibility without sacrificing organization.

另外,我们HFSM中的状态能够在状态转移的时候传送游戏对系那个(在我们的例子中在父行为和子行为之间交换游戏对象),也可以将这些游戏对象插入到那些需要这些游戏对象(将这些游戏对象作为其功能目标)的行为中,这是一种将脚本式的功能和严格的HFSM结构融合的一种方法,这种做法给游戏AI带来了额外的灵活性并且不牺牲任何的结构组织代价

Between concrete and abstract are partially-implemented behaviors. The ability to bypass parameters opens up a lot more ways to use behaviors, and many uses of parameters became quite common in our implementation. To help us with some of the standard ways in which parameters could be used, a few partially-implemented behaviors were created that would handle some standard tasks on the code side, even though they themselves were not full code-supported behaviors. That is to say they didn't belong as leaves of the HFSM; they were just helper behaviors that were still abstract until they were given the data that configured their actions.

在具体和抽象之间是部分实现的行为,通过传递参数使得我们可以以更多的方式来使用我们的行为,在我们的实现中参数的多种使用方法变得非常普遍,为了帮助处理一些行为标准参数传递方法,一些部分实现的行为被创建并处理代码端的标准任务,即使这些行为不是完全的代码支持的行为;也就是说这些部分实现的行为并不是HFSM的叶子节点,他们只是一种帮助行为,一般情况下这些帮助行为是抽象的,直到给予特定的数据参数配置其行为。

The most commonly used partially-implemented behavior was rangetest, which would accept a target parameter that it would track and store throughout its existence, using it to make a number of decisions.

Because parameters are passed as soon as a behavior is created, even before it is activated, we can use them to determine whether we should activate it. In the case of rangetest, extra settings were available to test the range of the passed target (the first parameter) and deactivate it if it left a second range.

最常使用的部分实现的帮助行为是‘范围测试’,它会接受一个作用对象参数并且在其生命周期内保存这些信息,使用这些信息来制定一系列的决策。因为参数是在行为一创建的时候就传递给的,即使在行为被激活之前我们仍可以使用这些帮助行为中的信息来决定是否应该激活这些帮助行为;在范围测试的例子中我们需要额外的设定来激活测试传递对象的范围,同时也可以通过其是否离开了第二个范围来阻塞该帮助行为

This functionality proved generally useful. Because the behavior can serve as the parent for both data-defined behaviors and code-supported behaviors, like pathfollow or melee, it is able to break tasks into responses based on properties of objects in the world and provide failcases implicitly when targets are too far away. By nesting rangetest behaviors and applying them to different objects at different times, we can define complex decisions about dynamic objects with just a few simple pieces.

这种特性被证实为是非常有效的,因为这样的帮助行为可以作为数据定义的,以及代码支持的行为的父行为(例如寻径和逃跑),他能够根据当前世界中对象的属性将任务分解为若干的响应,同时在对象距离过于远的时候提供行为失效;通过合理的安排‘范围测试’并且在不同的时候将这些信息作用于不同的对象,我们能够通过非常简单的几个行为对运行时的对象定义非常复杂的决策

Protect Behavior保护行为

The behavior protect illustrates this point: when it is activated, it's cued to protect as its first parameter. It then starts a list of prioritized behaviors, the first of which is to stay at all times within a reasonable distance of the character being protected, represented by approach. Approach is implemented as a rangetest behavior that activates when the target is too far away, calling into the movement system to run closer. (See Figure 3.)

所谓的行为保护阐述了这样一个特性:当它被激活的时候,它将保护其第一个参数,然后它会启动一系列优先排序的行为,首先是要全时间驻留在一定范围内使得角色得到保护,这样的行为也被称为接近,‘接近’行为被实现为‘范围测试’行为的繁华,这样当一个目标距离本体太远的时候,它将会唤醒移动系统接近目标 (见图3)

Second, a character in protect should engage any nearby enemies, which in our implementation are detected by a query to a target selector module running on the character (modules like these run separately from the behavior system). Combat is started in order to maintain this requirement (another rangetest behavior), and if an enemy is too close, it will activate and start children to engage the target.

If neither approach nor combat are able to start (because their preconditions are not satisfied), wander will activate by default, since it is the lowest priority behavior and has no preconditions.

The character will then patrol randomly around the actor being protected.

第二,收到保护的角色应该能够接近附近的敌人,在我们的实现中,这些游戏对象通过对目标选择器的查询获得(像这样的模块通常独立的运行于行为系统之外)为了维持这种需求,作战行为被启动(另一种范围测试的子行为),并且如果敌人太靠近,它将激活并且启动其子行为与目标对象相交接。如果‘接近’和‘作战’都不能启动(它们的前置条件都不能满足),那么‘巡逻’行为将被激活,因为他优先级最低并且没有前置条件,这样该角色就可以随机的在受保护对象附近进行巡逻

By creating behaviors that accept parameters at runtime, we are able to define a structure entirely in data to perform two very different tasks, while acting on multiple entities involved in those tasks-the person who should be protected and the enemies that pose a threat.

通过在接受运行时的参数来创建行为,我们可以完全依靠数据来定义行为结构从而进行截然不同的任务,例如作用于一个受保护的单位和一个收到威胁的单位

Sharing and Reusing Behaviors

共享和复用行为

A distinct advantage of defining a character's actions hierarchically is the ability to reuse, replace, and remove behaviors from the hierarchy easily and intuitively.

采用树状的集成体系结构来定义角色的行动带来的最显著的好处就是能够方便而直观的从树状体系中复用,替代,去除行为

Every behavior in this system (except for the simplest) has its settings defined in its .behavior settings file. However, when a behavior spawns its children, it does not start them using the filename directly; it uses an alias. Any character that starts behaviors will define a list of aliases, mapping each to a .behavior file. By abstracting behavior referencing by one layer, we are able to customize and reuse the behavior components by changing how that mapping is defined.

该系统中的每一个行为(除了最简单的行为)都有其对应的.behavior设置文件.然而当一个行为创建一个子行为,它不会直接的使用文件名来启动相应的行为,它会使用一个别名,任何启动行为的角色都会有一系列的行为别名,这些别名分别对应着不同的.behavior文件定义,通过增加这样的一层抽象层,我们就能够改变映射的定义通过定制和复用行为系统的组件

One of the down sides of using a flat FSM instead of an HFSM to drive your characters is the inability to quickly modify an existing behavior into a new one by changing only one aspect of it. To solve this in a FSM, you would need to recreate the entire state machine again, save the one difference.

使用平展的FSM体系而不是HFSM所带来的另一个缺点就是:他不能够在仅仅改动行为的一个方面的情况下迅速的重建这个行为。在FSM中如果你要解决这个问题,那么你不得不重新创建整个状态机来保存这个不同点。

We avoided this problem with our implementation by adding the ability to swap out behaviors at any layer in the hierarchy. By changing the mapping of a behaviors alias for a given character, you can swap out the actual behavior that's created when it references that alias, regardless of where the behavior sits in the hierarchy. This feature was commonly applied to give characters customizations on the behaviors that were widely shared, such as giving ninjas a special variant of melee or fireweapon. (See Figure 4.)

通过增加这样的一种功能:即换出行为树体系中的任意一层的能力,我们避免了这个问题.通过改变对应角色的映射行为别名,你可以换出当前实际的行为而不管该行为位于体系中的某一层,这个功能使得定制角色的行为的时候能够大量的共享行为,例如给予忍者一种特殊的‘逃跑’和‘交战’行为 (见图4)

Common combat behaviors map通用战斗行为映射
MapAlias("Combat", "pedestrian_combat.behavior")
MapAlias("Melee", "pedestrian_melee.behavior")
MapAlias("PathFollow", "pedestrian_pathfollow.behavior")
MapAlias("Patrol", "pedestrian_patrol.behavior")
MapAlias("Protect", "pedestrian_protect.behavior")
MapAlias("Approach", "pedestrian_approach.behavior")...

Ninja behaviors map忍者行为映射
INCLUDE("Common Combat Behaviors Map")
OverrideAlias("Melee", "ninja_melee_claw")
OverrideAlias("FireWeapon", "ninja_throw_shuriken")
OverrideAlias("Flee", NONE)
FIGURE 4 A common behaviour map, and a specialized one for ninjas, who have different melee and firing methods, and never flee, are listed.
图4 通用的行为映射,以及针对忍者的行为的泛化,它们有不同的逃跑和开火方法,因此没有列出

Aliases are typically defined in the file associated directly with the actor, but we gain some extra flexibility by allowing aliases to be redefined anywhere in the behavior tree as well. For example, consider the protect behavior described earlier. There are actually two variants of the approach behavior that are used as children within the HFSM, one to follow the character they are meant to protect and another for approaching enemies to engage in combat.

别名通常是在行为关联角色的时候被定义的,但是通过允许在行为继承树中重新定义这些别名我们获得了额外的灵活性。例如考虑前述的保护行为,HFSM中实际上有两种不同的‘接近’行为变种,一种是接近他们要保护的角色,另一种则是接近敌人以进入战斗。

Conceptually, these behaviors do the same thing (move the character to a target), but the way they do it is different. For example, the combat variant can strafe around and move in a more aggressive way, while the protect variant simply runs to the target's location of the character it is sent to protect.

We can allow this customization in separate branches with the override alias setting that any behavior can contain. When this setting is present in a behavior, it triggers any child behaviors that it or its descendants activate to instead use the new mapping. For example, we can override approach in the combat branch to use a more aggressive version, while the opposite branch separately overrides it to use a less aggressive version. Now, whenever any of the descendants activate that alias, it will filter up through each parent in the HFSM, checking in turn for a new alias mapping until it finds the ones we assigned.

概念上来讲这两种行为实际上做的是同一件事情(移动并接近目标),但是他们完成的方式是不同的。例如交战时的接近行为应该是迅猛的更具攻击性的接近,而保护的接近行为则是简单的跑到保护对象的附近,我们可以定制这些不同的泛化保护行为并为它们赋予相同的别名,这样当这套设定呈现在行为定义中的时候,这样他就可以激活任意的子行为状态而不是采用一个新的映射.例如,我们可以覆盖交战行为中的一般接近版本而采用更富攻击性的版本,而相反的分支上则采用平缓的移动版本.这样无论哪个后继子行为激活该状态,他会过滤他在HFSM中的每一个父行为,然后检查并寻找专业的一个别名,直到找到

Another simple method that was very successful in customizing behaviors was adding the ability to not just customize, but remove entire behaviors from beneath a parent. This was accomplished by mapping a behavior alias to a special "none" keyword, which when encountered would simply not start the behavior. This was very useful in producing variants of enemies that didn't throw grenades or didn't dodge, for example.

另一个非常有用的特性是能够从一个父行为节点下删除一整个行为,实现方法也很简单,只要将该行为映射到一个特殊的'空'行为节点,而该空行为一旦遇到只是简单的忽略而已,这对于创建那些不会进攻,不会逃跑的角色非常有用

AI for the Masses

In a game that features sandbox-style play, the AI needs to provide enough different and interesting characters to interact with in the world, and the size of the world doesn't have to get very big before it becomes unfeasible to hard code them all. Sometimes, even exposing behavior settings isn't enough-the structure of the tasks and subtasks must be exposed as well, in a way that's powerful but also simple to use.

在一个受限的开放游戏环境中进行游戏,AI角色应该能够呈现出不同的有趣的方式来和世界交互,游戏环境不需要很大,但是即使是这样有的时候,即使是这样的暴露使得行为设置仍然不能够完整架构出任务和子任务之间的结构,这样的架构某种意义上来说是强大而易用的。

In Destroy All Humans 2, the choices we made regarding AI architecture were intended to promote those traits. It's an adaptable, puzzle piece-like system in which functions are exposed in a generic way. It attempts to skirt the line between behaviors that are entirely hard coded and ones that are entirely script-defined, left to the technical designers to manage.

在毁灭人类2中,我们决定重新分类AI结构是为了突出这些特性,这是一种适用的,解谜式的系统,这样的系统下函数以更泛化的形式暴露,至于说如何决定脚本定义的行为和硬编码的行为之间俄准则,还要靠各自技术人员根据需要把握

Instead, a system like Pandemic Australia's packages that complexity and exposes it as individual pieces to be fit together at various levels of abstraction. As a result, we generate extra flexibility in the way those pieces can be reused and expanded, multiplying their usefulness and bringing us a step closer to populating a virtual world with virtual life.

相反的,想PANDEMIC AUSTRALIA的系统封装了复杂性并且暴露了各个层次的行为的抽象体系,结果我们采用了一种新的体系使得这些行为个体能够更好的被复用和繁华,这样就大大提高了这些行为的有用性,使得我们距离真实的虚拟世界又迈出了重要一步。

[EDITOR'S NOTE: This article was independently published by Gamasutra's editors, since it was deemed of value to the community. Its publishing has been made possible by Intel, as a platform and vendor-agnostic part of Intel's Visual Computing microsite.]

Resources附录

Alt, G. "The Suffering: A Game AI Case Study," in The Proceedings of the Nineteenth National Conference on Artificial Intelligence, July 2004.

Atkin, M. et al. "Hierarchical agent control: a framework for defining agent behavior," in The Proceedings of the Fifth International Conference on Autonomous Agents, May 2001. pp. 425-432.

Freed, M. "Managing Multiple Tasks in Complex, Dynamic Environments," in The Proceedings of the Fifteenth National Conferences on Artificial Intelligence, July 1998.

Fu, D. and Houlette-Stottler, R. "The Ultimate Guide to FSMs in Games," in AI Game Programming Wisdom 2. Hingham, Mass.: Charles River Media, 2003.

Isla, D. "Handling Complexity in the Halo 2 AI," in The Proceedings of the 2005 Game Developers Conference, San Francisco, March 2005.

Yiskis, E. "Finite-State Machine Scripting Language for Designers," in AI Game Programming Wisdom 2. Hingham, Mass.: Charles River Media, 2003.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics