我要ai最新版本是什么真3ai地图下载 有的发地址 要最新滴!!

行为树最后一个要讲的地方,是关于前提(Precondition),在第一部分里,我略微提到了一下,这次我们来仔细看看,再来看看关于前提的纯虚基类的定义:


      

      

      

      

    

每一个前提类,都需要实现这个判断的虚函数。我在提到,我们可以用类来表示逻辑运算,这样的好处是可以做到模块化,同样的判断条件可以复用,所以在库中,我也实现了这种逻辑的表达方式,定义了基本的逻辑运算类


      

      

      

      

      

    

从这些类的名字应该就可以明显的看出这些类的含义了,和逻辑操作符一样,有些类的构造函数需要两个参数,以此来表示二元的逻辑运算(AND,OR,XOR),有些只需要一个参数,以此来表示一元的逻辑运算(NOT)。前提类被用来附在行为树的节点上(每一个节点都可以附加),默认情况下,节点上是没有前提类的,也就是不存在“外在前提”,而只有“内在前提”,这和附了一个BevNodePreconditionTRUE(永远返回True)的“外在前提”的节点是等价的。

好了,行为树库的内容基本就是这些了。接下去我们来看看例子程序,介绍如何用库来创建行为树,例子的代码在BevTreeTest这个工程中,编译后可直接运行,这个例子分别演示了三个行为树,从简单到复杂,单击鼠标可以在这三个例子间切换。这个程序实现了这样一个功能,“在场景地图上,定时会产生一个目标点,智能体就会根据行为树的定义,用不同的行为模式移动到目标点”。

在这个程序中,我为智能体一共定义了4个行为:


      

      

      

    

再定义了2个“外在前提”:


      

    

我就用第一个例子来说,第一例子的行为树图如下:

这是一个很简单的行为树,根节点是一个带优先级的行为节点,所以MoveTo比Idle的优先级高,MoveTo带有一个“外在前提”,“当没有到达目标点”时,会选在MoveTo的行为,反之,则选Idle的行为。

在代码中,可以这样来定义这棵行为树

我在库中定义了一些工厂方法,帮助创建相关的节点。值得注意的是,我在这里演示了用类表示逻辑的用法。我在定义行为树的时候,会用一些格式上的缩进,来表示相应的父子结构,这仅仅是为了视觉上比较明了。当然,以后可以改进行为树的定义接口,更可以用数据文件来定义行为树。

这样定义完毕后,我们就可以用行为树来决策我们的行为了,代码相当简单


      

      

      

      

      

    

在例子中,我尽量把行为树中要输出的变量写到BevNodeOutputParam结构中(而不是直接修改智能体的信息),这样做的好处是可以让行为树的输入和输出的接口相当清晰,做成黑盒,可以参考我在的讨论。

第二个例子演示了并行节点的用法,第三个例子演示了序列节点的用法,就不多说了,大家可以自行看代码。

所有的代码可以通过以下方式获得:

(exe文件夹中已包含可执行文件)

也可用svn通过以下地址来得:

本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

上一次说到了节点的基类,它描述了在行为树上一个节点的基本结构。我们知道,在行为树上有两大类的节点,一种我称之为“控制节点”,像“选择节点”,“并行节点”,“序列节点”都属于此类,这类节点负责行为树逻辑的控制,是和具体的游戏逻辑无关的,属于行为树库的一部分,并且这类节点一般不会作为叶节点。还有一类称为“行为节点”,也就是行为树上挂载的具体行为,是和游戏逻辑相关的,不属于行为树库的一部分,需要自己去继承和实现,这类节点一般都作为叶节点出现。

先来看看“行为节点”的代码,我先从节点的基类继承了一个所有“行为节点”的基类


      

    

在它的Tick方法中,我做了一个简单的状态机(可以自行看代码),负责处理进入行为(Enter),更新行为(Execute),退出行为(Exit),所有的行为节点应该继承自BevNodeTerminal类,并且重写这些虚函数,在进入和退出行为里,可以做一个初始化和清理的工作:


      

      

      

      

      

      

    

值得注意的是,在Tick方法中,它有一个返回值,表示当前节点是否处理完毕,在库中,我定义了一个enum来表示节点的运行状态:


      

      

      

      

      

    

当返回k_BRS_Finish的时候,就表示当前节点已经处理完毕了,如果再次进入该节点,就认为是重新进入了。用上面描述的那个状态机的来说的话就是,如果是重新进入,会先调用_DoEnter方法,然后调用_DoExecute方法,如果_DoExecute返回正在运行(k_BRS_Executing),那么以后再进入这个节点就会直接调用_DoExectue,如果返回已经结束(k_BRS_Finish),则会调用_DoExit,以后再进入这个节点就会重新调用_DoEnter方法了。

对于控制节点来说,它的运行状态和子节点的运行状态是息息相关的,比如,选择节点的运行状态,就是它当前选择的这个节点的运行状态,并且,有时控制节点的控制逻辑也和子节点的运行状态有关,比如序列节点,当它前一个子节点运行结束,序列节点就会自动的切换到下一个子节点运行。所以在实现具体的行为类时,我们应该要正确的返回节点的运行状态。在例子程序中,我做的一个“空闲”(idle)的行为节点,就能很好的说明问题:


        

        

        

        

        

        

        

        

        
 10: m_WaitingTime = 
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

最近一直在忙新项目的准备,甚少涉及AI的东西,所以博客也疏于更新。春节前,收到一个网友的邮件,说看了行为树的一些东西,但还是不知道如何去入手实现,我就乘着春节假期,动手写了一个简单的行为树的库,和大家一起边分析代码,边说说行为树的具体实现方法。这个库很简单,一共也就800行的代码左右,不过麻雀虽小,五脏俱全,行为树中的主要部分基本都有涵盖,包括前提(Precondition),选择节点(Selector),并行节点(Parallel),序列节点(Sequence)等等。在分析代码前,如果有朋友对行为树的相关概念还不是很了解,建议先阅读本站上对于行为树介绍的相关文章。

这次的代码以及示例程序,还是基于我自己维护的一个框架,在系列文章的最后会给出下载链接。

行为树,由名字就可以看到,它是一个树结构,通过各个节点相互连接,所以我先定义了节点的基类:

要把树链接起来,需要在这个类中保留父节点指针,和子节点指针,我用了一个固定的数组来保存子节点指针,它的大小是16,也就是说,一个节点最多可以有16个子节点


          

          

          

          

          

          

        

有了这些变量的定义,我们就可以串联起一颗树了。到目前为止,这个节点类还仅仅是一个树的节点,作为行为树的节点还差了些东西,在以前的介绍中,我们知道行为树的每一个节点都可以绑定一个称为前提(Precondition)的部分,用来作为是否进入这个节点的条件,在我的实现中,我把这个前提拆分成了两个部分,一个称为“内在前提”,一个称为“外在前提”。“内在前提”是和节点类静态绑定的(也就是说,这个节点的固有前提),而“外在前提”是可以和节点做动态绑定的。这样做的原因是,由于在行为树上,节点是可以被复用的,在不同的子树上他的进入条件往往是不同的。比如,“移动”,这是一个常见的行为节点,逃跑的时候,可能需要“移动”,追击的时候也需要“移动”,但进入这个节点需要不同的“外在前提”,所以这里就需要让节点支持动态绑定的前提。“内在前提”,我用继承的方式来实现,而“外在前提”,我用了另一个类来实现


          

          

          

          

          

          

          

          

          

          

          

          

          

          

        

可以看到这里用到了一个叫做BevNodePrecondition的类,用来表示“外在前提”,他是一个纯虚函数,只有一个方法,先看一下它的定义,后面会有详细的讨论。


          

          

          

          

        

_DoEvaluate虚方法就是需要被子类继承并实现的“内在前提”,这两种前提在Evaluate方法中被结合了起来,用来检测进入条件,当返回True时,就表示当前节点可以被运行。返回False时,就表示当前节点进入条件不满足,不能被运行。

在节点基类的中,还有两个重要的方法是:


          

          

          

          

          

          

          

          

          

          

          

        

转移(Transition)的概念是第一次出现,转移(Transition)指从上一个可运行的节点切换到另一个节点的行为。这个方法会被在节点切换的时候调用,比如,在一个带优先级的选择节点下有节点A,和节点B,节点A的优先级高于节点B,当前运行的节点是B,然后发现节点A可以运行了,但带优先级的选择节点就会选择去运行节点A,这时就会调用节点B的Transition方法,所以在这个方法中,一般可以用来做一些清理的工作。Tick方法就是通常的更新方法,就不多说了。

再来看一下这三个重要方法的参数,一共有两种类型的参数,BevNodeInputParam和BevNodeOutputParam,前者是传入参数,可以认为是行为树的输入,用const作为限定符,表示只读,后者是传出参数,可以认为是行为树的输出,可以修改。其实,从代码中可以看到,这两种类型的本质都是一样的,都是一个名为AnyData的类


          

        

由于输入和输出参数是游戏相关的,所以这里用AnyData这个类来表示,这个类可以存放任意的数据结构,所以,这个类中真正的内容是需要玩家自己定义的。

最后来看看行为树是如何被定义和更新的(可以在示例程序中找到相关代码)


          

          

          

          

          

          

          

          

          

          

          

          

          

          

          

        
  1. 创建行为树,保存根节点指针(m_BevTreeRoot)
  2. 测试是否有可以运行的节点,如有则更新

————————————————————————
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

(ecto真不好用,居然写得东西都没了,害我只能把这篇文章再写了一遍,郁闷啊,跪求一个mac下的支持wordpress的离线博客编辑软件)

提到参数,在我们写程序的时候,作为一个常用的提醒是,一般都会避免去使用“魔数”(Magic Number),因为它含义不清,比如if(i>5){...},如果没有任何注释的话,很难有人会理解这个“5”是什么意思。在AI中,这样静态的参数的使用更频繁,有时为了定义一些阈值,有时为了可以给游戏设计人员调整AI行为等等,当这些参数慢慢多起来的时候,就需要更好的来管理,修改和查看,所以,如何设计一个好的参数系统就很有必要了。这个想法也源自我上一个项目的一些教训,在上个项目中,我们最后一共定义了超过1600个参数,由于历史原因,由一个简单的参数系统来维护,但实际操作中,这样的简单系统存在很多问题,首先参数没有很好的文档化,导致游戏设计人员很难搞清楚这个参数到底是干什么的,最后需要程序员的不停参与,工作的粘合度就提高了,另外也没有做到逻辑的关联,无法了解这些参数之间的相互关系。总之,这样那样的问题促成了我对于设计一个新的参数系统的想法。

相较与原来那个参数系统的种种问题,对于一个新的参数系统,我希望它能有如下的特性:

  • 仅支持静态的参数:对于游戏来说是只读的,也就是游戏在运行时不允许修改这些参数,但可以通过外部工具进行调试
  • 支持实时的修改:不需要重新启动游戏
  • 支持序列化到文件:可以将调整好的参数存成文件,以便下一次启动游戏时生效
  • 高效和便捷的定义和调用

以上列出的大部分特性还是比较容易理解的,对于第5点,我特别的说明一下,假设有一个参数A,当A的值发生变化的时候,逻辑上,参数(B1, B2, B3, … , Bn)也会相应发生变化,那我们就称A和(B1, B2, B3, … , Bn)有“逻辑关联”,也就是A的变化会影响到(B1, B2, B3, … , Bn)的值,反之不成立,也就是说,逻辑关联是单向的。举个例子,比如我们有一个可以调整游戏难度的参数,它关联到敌人的血量,攻击值,防御值,当把游戏难度调成“高”时,血量,攻击值,防御值会加倍,当把游戏的难度调成“低”时,血量,攻击值,防御值会减半。在我们原来的参数系统中,这种逻辑关联是隐含的,也就是说所有的参数并没有直接的相互关联,他们的结构是“平面化”的,彼此相互独立,所以当游戏设计人员想调高游戏难度的时候,他就需要记得相应的要去调整血量,攻击值,防御值。这样很容易造成错误和疏漏,而且这种隐含的逻辑关联无处可循,只能存与程序员活或者游戏设计人员的脑海中。

针对我们的需求,我定义了一个参数P所需要包含的各个部分

  • 当前值:该参数当前的值(一定是合法值),供游戏读取
  • 参数类型:表示这个参数是离散型的,还是连续型的
  • 上下限数组:给连续型参数检查合法性使用,如果值不在取值区间中,则为不合法的参数值,用数组的原因,主要是可能有多个取值区间,[Vmin1, Vmax1], [Vmin2, Vmax2], … , [Vminn, Vmaxn]
  • 候选值数组:给离散型参数检查合法性使用,如果值不在候选值数组中,则为不合法的参数值。
  • 参数描述:对于该参数的简要描述
  • 趋势描述:对于该参数的变化所带来的影响的简要描述
  • 逻辑关联项数组:该参数所影响的参数项列表(P1, P2, … , Pn)
  • 被逻辑关联项数组:能影响该参数的参数项列表(P1, P2, … , Pn)

作为补充说明,对于上下限的定义,还需要有一个标志来表示是开区间还是闭区间。在逻辑关联项的数组中,和每个参数项还需要绑定一个非常重要的“关联方式”的实现。关联方式是非常灵活的,可能非常简单,就像我们前面举的那个例子,仅仅是加倍和减半,但有时可能非常复杂,有更多的逻辑计算,所以,我想可以调用一段代码来计算关联方式,可以是虚函数,也可以是一段脚本。更新关联项的方式是递归的,直到所有的关联项都被更新为止。文档化有几个部分组成,一个是名称,要求要可以识别,一个是两段描述,这两个部分很难做到要写成什么格式(文档的东西一般都是自觉自愿),但作为规范是要求定义参数的人能详细填写的,另一个部分就是关联项和合法性提示,这个可以用代码来生成文档信息(试想一下开发环境中的代码提示功能)。

我想,有了这样一个参数结构的定义,要实现一个参数系统来满足我上面的要求,并不是一件很难的事情,当然现在的这些只是我一个粗略的想法,还没有实现,有兴趣的同学可以实现一个,我有时间的话,也会做一个出来,因为它很独立,可以作为第三方库用在任何的引擎中,实现技巧的话,我想用“反射”(Reflection)做的话,对于序列化,对于做一个可视化的编辑器,对于文档化,都是挺有帮助的,值得一试。

————————————————————————
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

《全民足球》是育碧开发的首款休闲类网络游戏。游戏以街头足球为主题,基于城市街区的真实地图概念,支持8名玩家进行同场竞技。游戏拥有独一无二的角色扮演加足球竞技的游戏模式,玩家可创建各具特点的角色球员,通过比赛和任务,提升其属性技能和天赋,获取奖励和成就。玩家并可基于所在城市街区创建足球俱乐部,和同城玩家一起创造辉煌。

拥有高品质全物理运算的核心游戏性,支持2对2, 3对3和4对4玩家间比赛,3大游戏模式: 单人训练, 自由对战和组队对战,守门员为AI。

玩家可以按自己喜好创建球员角色,自定义项包括性别、场上位置、惯用脚、身高、肤色、脸型、发型和着装等。

玩家也可以在游戏内商店购买其他服装和形象来装扮角色。

独一无二的角色扮演+足球游戏模式,3个球员职业: 前锋, 中场和后卫,6大球员属性: 力量, 速度, 传球, 射门, 带球和防守,每个只有拥有各具特色的技能和天赋。

所有的角色系统均可按玩家喜好升级。

团队配合可以增加士气能量。当士气槽积满后, 会触发团队高潮时刻,在团队高潮时刻, 球员属性会大幅提升, 部分技能也会触发更强大更炫酷的动作。

任务系统有一系列目标任务让玩家在了解游戏的同时获得经验和游戏币。主线剧情任务帮助玩家从玩家从普通球员成为一个球星。

真实地图系统在游戏中内建了和真实世界一致的地图,让玩家更容易和同城同区的其他玩家进行游戏。

玩家从自己所在的街区开始游戏, 通过角色升级逐步探索所在城市乃至世界。

其他社交功能包括: 好友系统, 聊天, 邮箱, 足球俱乐部系统等。

玩家可以在游戏内商店购买服装, 道具, 技能等物品,在购买服装前可以进行试穿。玩家同样可以把商品作为礼物赠送给其他玩家。

————————————————————————
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

上次我们说到,游戏中的运动系统一般有两种方式,“动画配合运动”以及“运动配合动画”。对于第一种方式,由于是采用运动函数或者经验数据表,所以可以很简单的将“未来时间”带入其中,来预测未来某一时刻的运动结果。但对于第二种情况,因为所有的运动结果都是从动画中取得的,如果不知道动画信息,就无法知道相应的运动结果,所以,简单的将时间带入是不能作出预测的,对于这样的情况,如果我们要做预测的话,就不得不将动画的因素考虑在里面。

举个简单的例子,一个人做一个跑动中转身停下的行为,假设他当前时刻T1,处于A1位置,速度是V1,朝向是F1(假设和速度方向一致),他的目标状态是速度是0(没有速度),朝向是F2,位置不指定。这时当我们给出未来时间T2,应该怎样来做这个运动预测呢?

和第一种运动系统不同的是,这里我们需要一个额外的模块,称之为“动画选择模块”。在我们实际要去完成这个运动目标的时候,我们会用“动画选择模块”去选出每一步的动画,比如最直观就是“直线跑”,然后会产生一个“急停”使速度从V1到0,最后是“原地转身”使朝向从F1到F2,当这些动画做完后,我们就可以得到这个人最终的“目标位置”。由于是运动配合动画的,所以对于他的最终位置,我们完全是靠这些动画实际的运行结果而得到的。

当我们要做预测的时候,显然我们也是需要用“动画选择模块”做预先的动画预测的,所以,在设计可预测的运动系统的时候,就需要将“动画选择模块”独立出来。如下图所示:

动画选择模块的输入是“当前状态”和“目标状态”,输出是一个动画的序列,这就有点像以前我们讨论过的“计划器”(Planner),相当于为我们的运动做了一个计划(对于这样的计划器的实现,就不在这次的讨论中了)。有了这样的计划后,当我们将时间T2带入,就可以通过读取动画信息的方式来获得T2时刻的运动信息了,见下图,AT指动画的时间(Animation Duration)

对于给定的“当前状态”和“目标状态”,都会有一组动画序列与之对应,所以如果是基于目前的运动状态所作的预测,那我们可以重用我们已有的动画序列来提高效率。如果是对于假想的运动状态的预测,那我们就需要用“动画选择模块”重新做一个新的动画序列,然后再得出未来时间的运动状态,这也是为什么我们需要独立的“动画选择模块”的原因,但如果频繁做较长步骤的假想预测,可能会产生一些性能上的问题,这是需要注意的地方,当然,这也取决于“动画选择模块”实现的复杂度和动画的丰富程度,比如在上个例子中,如果我们有一个“急停转身”的动画,那我们就可以减少动画序列的个数,也就是减少了计划的步长了。

另外,在实践中,我们会采用两种运动方式混用的情况,比如对于“直线跑”,我们会用运动函数来实现,而对于“转身”,“急停”这样的行为,我们会采用第二种方式来实现,对于这样的运动系统,也可以用到这样的“计划器”,只是这个计划中的某一步换用函数方式罢了,对于上面例子,我们可以参考下图:

除了预测未来时间的运动状态外,可能我们还会预测到达某一个状态所要用到的时间,当我们有了上面的系统后,这也会非常容易做到,比如上面的例子,如果我们要预测他到“目标状态”需要多少时间,那我们只需要把动画序列中每一个动画的时间求和就可以了,T = T1 + AT(Run) + AT(Scram) + AT(Turn)。

可预测的运动系统对于某些游戏可能是一个非常重要的系统,希望上面的讨论对大家有所帮助。

————————————————————————
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Dota6.81cAI中文版,最新版本的AI地图,增加了英雄选择准备时间,修复错误。

全阵营选择模式下,英雄选择时间结束后,尚未选择英雄的玩家每秒将损失1金钱

全阵营选择模式的英雄选择时间从60秒增加到75秒

全阵营选择下出兵前准备时间从90秒减少至75秒

现在Roshan的生命值/攻击力/护甲在45分钟后将继续增加

修复了大地之灵在被重新选择出来后无法正常实用巨石的错误

修复了一个有时能看到敌方视野的错误

修复了在老式电脑上延迟的错误

修复了金箍棒无法切换的错误

修复了静谧之鞋在破损时卖价比原本的多的错误

2.复制文件到游戏地图目录

可选中1个或多个下面的关键词,搜索相关资料。也可直接点“搜索资料”搜索整个问题。

参考资料