— AI分享站

Archive
Tag "共享数据"

这篇文章针对于已了解行为树和黑板的读者,如果不是很了解,请参考此处(123)。

黑板(Blackboard)是一种数据集中式的设计模式,一般用于多模块间的数据共享,我在做行为树的过程中,发现黑板非常适合作为行为树的辅助模块来使用,这次就来谈谈如何在行为树中使用黑板。

行为树的决策一般要依赖于外部的输入,如下图所示。

输入内容的来源取决于行为树用在整个AI架构的哪一层,可以是游戏世界的信息,或者是上层模块的输出。输入的形式,可以是分散的(Decentralized),也可以是集中的(Centralized)。举个例子来说,如果我们做一个战士是移动,还是攻击的决策,这是决策层的行为,所以输入内容就是游戏世界的信息,它可能包括战士自身状态(在模块A中),敌人状态(在模块B中),装备物品情况(在模块C),地图场景情况(在模块D中)等等,所以,当我们搜索和执行行为树时,我们需要从4个模块中获取信息来帮助决策,这样的方式就是我上面说的分散的方式,它的好处是调用非常直接(可能是用多个Singleton提供的接口),没有数据冗余,缺点是使得行为树对于数据的依赖度太分散。

集中的方式的话,就是我们可以定义一个数据结构专门用于行为树的输入,将上面提到的需要用到的数据,在进行行为树决策前,先从各个模块中收集到这个数据结构里,然后再递交给行为树使用。集中式的输入减少了输入和行为树之间的接口数量(只和预定义的数据结构通信),但缺点是,存在数据冗余。不过,我们可以看到集中式的数据输入使得行为树的表现更像一个黑盒了(可以伪造数据来测试行为树),这也是我们一直以来想要的。可以参看下面对于两种方式的示意图:

BehaviorTreeInputModule_1

基于上面的原因,黑板(Blackboard)这样一个概念正好符合我们的需要,所以我们就可以用黑板从各个模块中来收集行为树决策和执行的过程中需要用到的数据,然后提交给行为树使用。值得注意的是,这块黑板对于行为树来说是只读(Readonly)的,行为树不允许修改和添加任何信息到这块黑板上面。因为很难从程序上去限制(就算用const,有时为了方便还能强转成非const),所以限制只能是一种规则,或者说约定。

说完了外部世界的黑板,我们再说说另一块可能会被用到的黑板。这也可以看成是对上面这块只读黑板的补偿吧, :)

在行为树的使用过程中,发现有时候节点和节点间,行为树和行为树之间确实需要有数据共享,比如对于序列(Sequence)节点来说,它的执行行为是依次执行每一个子节点,直白一点说的话,就是执行完一个再执行下一个。一般用到序列的行为,其子节点间总会有一些联系,这里就可能存在节点间通信的问题。再比如,在一些团队AI的决策过程中,当前AI的行为树决策可能需要参考其他AI的决策结果,所以这样就存在了行为树之间需要通信的情况。

所以,在实践过程中,我们还会定义另一块黑板来负责行为树间和节点间的通信需求,示意图如下

BehaviorTreeInputModule_2

可以看到这块黑板是又可以读又可以写的,为了防止黑板混乱的问题(可以参看我以前对于共享数据的文章),我们必须在使用时规定一些限制,可以称之为黑板数据的“作用域”,我们知道很多编程语言里,变量都是存在作用域的概念的,有全局,有局部等等,借鉴于此,我也在这块黑板上规定了作用域,由上面的分析我们可以将黑板上的数据分成如下几种作用域

  • 全局域(G):此数据可以给其他行为树访问
  • 行为树域(T):此数据可以给行为树内的任意节点访问
  • 指定节点域(N):此数据可以给指定的行为树内的某节点(可以是多个)访问

这样的话,黑板的混乱程度就会好很多了,我们可以提供相关的接口来帮助操作黑板上的这些变量。

好了,基本上黑板在行为树中的应用就聊这么多了,希望对大家在使用行为树的过程中有所帮助。

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

AI结构中,有一种情况非常容易出现,就是程序员会非常希望有一块区域,大家可以读取,存储一些信息,并且这块区域是全局共享的。不管是写作看似丑陋的全局变量,还是有一定组织的Blackboard结构,或者作为参数一路传到底,这种结构可以被统称为共享数据(Share Data)。作用就是在不同的模块间传递变量。

共享数据的需求,从某种程度上说,源自程序员的一种”偷懒“(当然这里没有任何贬义),试想一下,一块可以非常方便,存我想存的,读我想读的结构,一块非常松散且容易理解的结构,一块非常容易架构(或者几乎不用架构)的结构,相信很多人都会作为第一选择吧。确实,共享数据的优势很大,很有吸引力,在开发中,可以作为很多问题的解决方案,几乎在所有的引擎中,都或多或少存在着一些共享数据的模块。

在网上的很多文章里,探讨了多种共享数据的结构,OO开发,使得这种结构多了更多的灵活性,不再是以前一个structure走天下了,比如前面说到黑板系统,这个可以说是现在AI中经常会用到的结构,在FEAR的GOAP里就用到了Blackboard,用作在Planner间作数据共享(以后可以写篇文章介绍一下GOAP,我非常喜欢的一个AI架构)。但不管这么样,共享数据的本质还是相同的。

共享数据是一种优点和缺点同样明显结构,下面我们来看看它的缺点

首先,就是共享数据很容易乱,这是伴随它的随意性而生的,共享数据的内容会随着开发进程不停的被修改,或添加新的变量(这种情况居多),或删除冗余的变量(由于开发过程是多人协作的,所以,非常有可能的一种情况就是存在两个作用完全相同的变量,只是因为是不同的人加的),如果没有好的维护和清理,共享数据就会逐渐变成”垃圾堆“ -- 随便说一句,这也是我对共享数据的昵称

其次,共享数据会比较难debug,因为修改和读取都是匿名的,也就是说,谁也不知道,谁会在什么时候修改什么变量!这会导致变量被莫名修改,当然,加数据断点,或者好的统一入口,会使情况有所改善。

还有,共享数据很容易造成程序员对他的依赖,会认为任何变量都可以存在其中,而忽视了对本身模块的架构,在某些情况下,它会成为解决问题的最后一棵稻草,问题是,共享数据可以是,但不能每次都是!

sharingdata-1

像我写的标题,”黄金屋“还是”垃圾堆“,不取决于共享数据本身,而是取决于,我们如何去实现,如何去维护,如何去规范,如何去使用。想到这个问题,也源自我最近一段时间的实践和体会,作为一个新的系列的开始吧,下一篇,写点我对架构共享数据模块的想法。

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More