Unity中决策AI---GOAP

版权声明:本文为博主原创文章,未经博主允许不得转载。出自 游戏开发实验室 https://blog.csdn.net/u010019717/article/details/80904943

孙广东  2018.7.3

市面上Unity人工智能的书籍基本上都是介绍这几个方面:

说 goap 之前可以先了解一下, FSM和 行为树
  • 有限状态机由一组状态(包括初始状态),输入和根据输入及现有状态转换为下一个状态的转换函数组成。
Unity有名插件 PlayeMaker, Unity内的 Animator动画状态机。 游戏的启动器,选人创角流程, 场景转换流程,网络状态 都可以使用FSM进行设计。
状态机可以使用下面 状态转换矩阵来表达:


  • 行为树中最常用的节点包括:
l 动作节点(Action): 属于叶子节点,用于描述一个最终执行的动作。
l 条件节点(Condition): 属于叶子节点,用于描述一个条件是否成立。
l 选择节点(Selector): 属于组合节点,用于顺序执行子节点,只要它的一个子节点返回成功,则整个分支返回成功,反之返回失败,类似程序中的逻辑或(OR)。
l 顺序节点(Sequence):属于组合节点,用于顺序执行子节点,只要它的一个子节点返回失败,则整个分支返回失败,反之返回成功,类似程序中的逻辑与(AND)。
Unity中行为树编辑器 也是特别多的!

关于FSM和行为树百度一下遍地都是,他们在中国游戏中应用非常广泛, 但是 goap 的文章很少。 也听人说过goap 在国外用的多一些。





文章内的代码:http://github.com/sploreg/goap 
网上代码的改进版本: https://github.com/losetear/goap 主要GC和性能
针对原文的翻译: http://gamerboom.com/archives/83622

目标导向型行动计划(简称GOAP)是一种能够轻松呈现给你的代理选择的AI系统,也是帮助你可以无需维持一个庞大且复杂的有限状态机而做出明智的决策的机器。

什么是GOAP?


目标导向型行动计划是代理的一种人工智能系统,让你能够计划一系列行动去满足一个特殊的模板。特殊的行动序列不仅依靠于目标,同时也依靠于世界和代理当前的状态。这意味着如果同意的模板是针对于不同的代理或世界状态,你变回获得一个完全不同的行动序列,浙江能够让AI变得更加东太后且显示。让我们着眼于一个例子,即上述演示中所看到的内容。

:原文使用伐木工为例子, 其实都类似!
例如,假设代理人的目标是收集小麦。 人类可以通过许多不同的方式实现这一目标,例如:
• 寻找镰刀>>收割小麦>>获取小麦
• 寻找小麦青贮>>收割小麦>>获取小麦
• 用手收割小麦>>获取小麦

GOAP能够基于可行的先决条件去选择最佳事件序列。 即如果代理人agent 无法使用镰刀,那么他们必须手动收割。

上面定义的行为可以使用FSM实现,如下所示:



或像这样使用GOAP实现:


GOAP使代码更加模块化,易于维护,通过将状态彼此分离,变得更容易进行测试与维持,每个动作都可以在其他动作的同时进行 。 此外,GOAP允许自发地添加和删除动作; 代理agent 必须只有一个已定义的操作列表,这些操作由GOAP计划程序planner自动处理。


Plan & Goal计划和目标
plan 计划只是满足目标的一系列动作,其中动作将agent 代理从起始状态带到满足goal目标的任何状态。

goal目标是代理想要满足的任何条件。在GOAP中,goal目标只是定义满足目标需要满足的条件,达到这些满意条件所需的步骤由GOAP计划员planner实时确定。目标是能够确定其当前相关性以及何时满足。

Actions操作
每个agent代理都被分配了一些actions操作,这些actions操作是plan 计划中的单个原子步骤,使代理执行某些操作。actions动作的例子是播放动画,播放声音,改变状态,拾取鲜花等。

每一个动作都被封装,对其他动作一无所知。

每个定义的action 动作都知道何时有效执行以及它对游戏世界的影响。每个action 操作都有前提条件和效果属性,用于将action 操作链接到有效计划中。前提条件是操作运行所需的状态,effects效果属性是操作执行后对状态的更改。例如,如果agent 代理商想要使用大镰刀收割小麦,则必须首先获得该工具并确保满足前提条件。否则,代理人用手收获。

GOAP通过评估每个操作的成本来确定要执行的操作。 GOAP Planner 计划程序通过累加累计成本并选择成本最低的序列来评估要使用的操作序列。GOAP计划者能够排列行动,明确哪些是可运行的哪些不能,然后决定哪个行动是最突出的。

The GOAP Planner GOAP规划师
agent 代理人通过提供满足planner计划者的目标来实时制定计划。 GOAPplanner规划器查看动作前提条件和效果属性,以确定满足目标的动作队列。 target goal目标目标由代理提供,世界状态和有效操作列表;这个过程被称为“制定计划”

如果planner 计划者成功,则返回代理人遵循的计划。代理执行计划,直到完成,无效或找到更相关的目标。如果在任何时候目标完成或有另一个目标更相关,那么角色将中止当前计划并且计划者制定新目标。

planner 通过构建树来找到解决方案。每次应用操作时,都会从可用操作列表中删除。


Visualization of planning tree

此外,您可以看到planner 计划者将运行所有可用操作,以找到target goal目标目标的最佳解决方案。 请记住,不同的操作有不同的成本,planner 总是以最便宜的成本寻找解决方案。

在上图,您可以看到计划程序尝试运行‘“Use Tool”“使用工具”操作,但它验证为不成功的解决方案,这是因为分配给所有actions操作的前提条件和效果属性。

‘“Use Tool”操作无法运行,因为它的前提条件是agent代理具有通过使用 “查找工具” 操作找到的工具。 但是,无论是否有工具,代理商都可以收割小麦,但是如果有工具,它可以降低成本,因此代理商会优先找到可用的工具。

在Unity中,可以使用包含键值对的哈希集来实现使用c#前提条件,其中字符串是前提条件标识符并且value是object

HashSet<KeyValuePair<string, object>> preconditions;
HashSet<KeyValuePair<string, object>> effects;

这些仅用于计划,并且在实际运行操作之前不会影响Agent代理。

此外,某些操作需要使用来自世界状态的数据来确定它们是否能够运行。 这些先决条件称为程序性先决条件。 在图中,如果“查找工具”操作能够找到包含要使用的工具的某个容器,则“查找工具”操作将仅向planner规划者验证自身。 如果“查找工具”无法满足程序性前提条件,则planner计划者会告诉agent 代理手动收割小麦。

例如,收获小麦的程序条件在c#中看起来像这样
// This function checks for the nearest supply pile to deposit wheat at
// Harvesters in different areas of the world will go to their respective chests, but if they are destroyed they can intelligently find another closer one
public bool CheckProceduralCondition (GameObject agent) {
Chest[] chests = GameWorld.chests;
Chest closestChest = null;
float distChest;
foreach (Chest chest in chests) {
if (closestChest == null) {
closestChest = chest;
distChest = Distance (closestChest.position - agent.position);
} else {
// Check if this chest is closer
float distance = Distance (chest.position - agent.position);
if (distance < distChest) {
closestChest = chest;
distChest = distance;
}
// If no chest is found, terminate the action plan
if (cloestChest == null)
return false;
SetTarget (closestChest);
return closestChest != null;
}
}
}

当游戏使用大量代理时,这些程序条件会变得很累,在这种情况下,GOAP planning计划应该在一个工作线程上运行而不是渲染线程,这样planner计划者可以持续计划最小化对游戏体验的影响。

GOAP和FSM如何协同工作
在我的实现中,我使用了具有3个状态的FSM

Idle休闲
MoveTo 移动
PerformAction 执行操作
休闲状态是agent 代理程序启动的默认状态。 在此状态期间,代理程序将其定义的目标(可以是您选择的任何内容,并且可以有多个不同的目标)传递给planner 以及世界和代理程序状态。

它看起来像这样:
// Remember the hashsets we used to store preconditions and effects?
// We also use those to hold the worldstate (which is the agents initial precondition) and the effect (which is the goal for the plan)
// This makes the implementation more straightforward
HashSet<KeyValuePair<string, object>> worldState = getWorldState();
HashSet<KeyValuePair<string, object>> goal = createGoalState();
Queue<Action> plan = planner.plan(actions, worldState, goal);


在agent代理找到plan 计划后,它会检查它是否在范围内以执行命令,否则转换到MoveTo状态。 当在目标范围内时,代理转换到PerformAction状态,其中agent 代理执行 planner接下来指定的操作。

当没有剩余要执行的操作或代理已完成其目标时,代理将返回到空闲状态并等待新计划。

一些实施细节
标准的GOAP实现需要至少6个基类才能工作。 这些是:

  • FSM
  • FSM State
  • GOAP Interface
  • Agent
  • Action
  • Planner
GOAP Interface 接口由所有agents 代理实现,它是Planner访问 世界数据 并将FSM和Planner中的事件联系在一起的方式。

要使agent 代理工作,它必须具有以下组件:

  • Agent代理实现
  • GOAP Interface接口实现
  • Action实现
每个agent 代理都会完成他们的行动,直到他们完成目标。

技术演示:


Demo of agents using GOAP

如您所见,每个代理使用对它们的最佳可用操作序列来实现其目标。在GIF中,铁匠必须等到矿工在箱子中放入足够的矿石供他锻造工具,此外当矿工工具破裂但矿工尚未锻造时;他们要么手动收获,要么等待补充。

我将在simmer.io上传一个演示链接,这时我可以让Unity的网页版本真正为我工作。我还将使用演示上传源代码。 他没有上传 https://github.com/vedantchaudhari?tab=repositories


使用GOAP的游戏
过去十年中一些最伟大的游戏将GOAP作为其AI架构的核心。 F.E.A.R首先使用它开创了GOAP,成为有史以来最令人难忘的人工智能体验之一。

开发者希望创造一种“cinematic” “电影”体验,其中战斗与真人一样强烈。恐惧的AI agents代理可以使用GOAP进行掩护,盲目射击,通过窗户潜水,用手榴弹冲出玩家,与队友沟通等。开发人员自己说他们的FSM只包含3个states。


F.E.A.R’s Finite State Machine

GOAP非常适合需要多种多样的人工智能的游戏,可以根据情况执行多种不同的操作。只使用状态机也可以实现类似的效果,但结果通常会复杂得多。

“对于F.E.A.R.我们决定将这种逻辑转移到 planning system中,而不是将其嵌入到FSM中,因为游戏通常具有过去的“ - Monolith Productions
此外,由于AI可以使用 planning system规划系统自行决定状态转换,因此可以减少程序员和设计人员的大量开销,这些程序员和设计人员可以彼此独立地进行操作。

GOAP具有高性能,可以在各种系统和架构上运行。当然,它的复杂性随着活跃的代理数量和可用的动作量而增加。尽管如此,如前所述,planner 可以使用 协程或线程,以大幅提高性能。

结论
在大多数情况下,GOAP只是一个比繁琐的FSM更有效的解决方案。使用GOAP,AI可以是非常动态的并且具有大范围的动作,而不必手动实现互连状态。



参考和推荐:
 

没有更多推荐了,返回首页