• AI行为树


    如何完成行为树的实现的呢,要找清楚黑板,行为树,AI_Controller的关系,黑板实际上就是行为树的数据库。

    1.       先进行整体架构,创建Character,AIController,行为树,黑板,将其关系理顺,首先将控制器与角色链接在一起,让控制器去控制角色,其次在控制器中实现行为树,Run BehaviorTree,再者就是将黑板绑定到行为树上去使用,在行为树中Detail面板中的BlackBoard绑定上刚创建的黑板,这样就将四者联系在一起了。

    2.       架构行为树,Root节点下只能链接一个Composites节点,架构过程中暂时全都用Selector,之后有需要进行更改,设想一下还是有四个状态,Patrol,Chase,Attack,Back,接着在下面分别添加四个Selector,每个下面添加一个Task.(写逻辑)

    3.       如何实现四种状态下的切换呢,还是要用到枚举,写入四种不同的状态,状态有了,如何切换呢,那么想到要用到枚举种不同的状态,黑板是行为属的数据库,想用枚举这个数据就要将枚举添加到数据库中,四种状态的Selector分别都添加一个黑板但是每个黑板所呈现的枚举状态不同,这样就可以实现,改变黑板上的State来实行切换状态。

    这是添加到Selector上的黑板可编辑的东西,FlowControl值得是这条分支的流程,如果其中的Result或者Value改变那么就会出现改变Flow,(Observer aborts(改变什么))。

    下边的Blackboard呢,Key Query是判定条件,(等于,大于,小于等等)BlackboardKey是黑板中的关键值,需要什么选什么,KeyValue是关键值得大小或者选项等等。通过改变KeyValue来对四纵不同的状态进行定义。

    4.       基本整体架构就是这样,下面接着先写Patrol,首先要完成的自己巡逻状态,打开Task,覆写一个Tick事件作为驱动,首先用的节点就是SimpleMoveToLocation,这里需要输入两个值,Controller和Location,Comtroller的输入有两种方法,第一中就是将Controller写进黑板,初始化赋值之后使用。第二种较为简便,其实Tick事件上的Owner Actor就是一个Controller,只需要将其Castto为需要的AI_CON就可以了,那么Location就要需要黑板传入了,这里只介绍一次,黑板上的值如何初始化和使用,之后一笔带过,但这也是行为树中比较重要的地方。首先在黑板上创建一个Vector的变量,命名为HomeLocation,然后在Task中调用,先创建一个BlackBoard类型的变量,从中引出一个GetBlackBoardValueAsVector。但是此时并不能保证所连的Vector就是HomeLocation,将黑板变量眼睛打开,编译,点击行为树中的Task就会发现多了一个Default,而变量就是黑板变量,在黑板变量中选择Homelocation就OK了,这样就保证了引出的Vector就是Homelocation。还需要一个让其四处移动的一个节点,GetRandomPointinNavigableRadius(在AI->Navigable中可以找到),设置好后链接到Goal上,这时还是不可以移动,那是因为HomeLocation还是个空值,没有对他进行赋值,这时候就要在Controller中进行赋值了。

    5.       在赋值的时候有一个细节不可以忘记SetValueasVector节点上的KeyName是说要付给值得名字,这时候要创建一个Name变量,但是切记创建完变量后编译后一定要将DefaultValue改成要改变的名字。

    6.       120帧是1秒。

    7.       将动画设置好,那么自动巡逻就写成了。

    8.       但是不意味着Patrol这个状态就结束了,这个状态下还是要写发现Player并跳转到Chase状态,因为这里跳转状态会经常用到,那么我就直接将将其写作一个Task,ChangeStateTo.

    9.       就用上述的方法,创建一个黑板变量引出SetBlackBoardValueasEnum,可以直接改变Value的值来控制切换的是哪个状态,但是这样很不方便,为了直观,可以再创建一个Enum变量并打开眼睛,使其在Task中可以改变。这样改变状态是完成了,可是如何跳转下去呢,在Task中完成跳转的节点是FinishExecute,如果Success就会返回到根节点,如果Success没有选中,那么回从左向右跳到下一个子节点。但是这里我们改变状态当然是希望跳转回根节点,重新进入分支。

    10.   这时发现还是缺少一个事件,就是FindTarget,创建并打开,因为Find的话就要每帧都去检测,所以复写一个Tick事件,还是用之前的方法进行检测,PawnSensing,在Controller中添加事件OnSeePwan,附一个值传给黑板,在Task中判断这个值是否有效,有效则进行跳转。不要忘记在最后写上FinishExecute,但是这个要是Success不选中的,因为想让其条状到ChangeTask上。

    11.   这是发现问题了,巡逻Task是不断在改变位置的,不能进行跳转,换句话说是常态,不能进行判断跳转,并且跟Find是并列关系,所以这里可以考虑用SimpleParallel,平行关系,但是主支可以操控跳转,副支跟随着主支跳转,自己不能控制。正好适用我的这个情况。这样Patrol就算写完了,接下来写Chase.(这里简述一下SimpleParallel,(插播概念以及细节)它与Selector,Sequence统称为Composites节点,Composites节点定义一个分支的根以及该分支如何执行的规则,规则可以有Decoratars(装饰者)节点决定,此外也可以将Services节点附着在上面,不过Services要在其子节点执行之后才开始生效。行为树的节点也就分为四种:Composites,Decoratars,Service,Task。Composities节点中需要注意的是执行顺序,还有要注意的是SimpleParallel中的FinishMode,其中两种方式分别是Immediate(主任务完成后,整个背景树执行终止)Delayed(主任务完成后,背景树继续执行),在Decorators中暂时先更新一个BlackBooard,在Detail面板中FlowControl下Notify Observer(节点检测)下有两个选项,OnresultChange(条件发生变换时继续评估)OnValueChange(观测的黑板键发生变化时再次评估,存在疑问),下面的ObserverAborts(检测中止abort流产中止,其中LowerPriority是指终止此节点右边的节点),剩下的都提过,)

    12.   先下ChaseTask,追踪的话还是用节点,SimpleMoveToActor。事件写完之后就要考虑可能出现的情况了,距离小于攻击距离+50就切换至Attack状态,距离大于追踪距离就切换至Back状态。这里如果用之前的Patrol状态的方法也可以,但这里使用一个新的方法,Service.

    13.   Interval间隔时间,休息时间,deviation偏差值,这是Service的俩个可编辑的值,主要意思是Service有一套自己的时间轴,可以编辑的间隔时间和偏差值。只有进入此支线时Service才起作用,在其中写什么呢,主要写当距离小于攻击距离+50就切换至Attack状态,距离大于追踪距离就切换至Back状态。这里就不需要用finish来进行跳转了,那么如何完成跳转呢,上面说过,在黑板上有控制FlowControl,这里就用到了,将这个Selecter上的黑板上的FlowControl修改,这里是改变了StateEnum所以将NotifyObserve改为OnResultChange,Observe aborts改为Both,就会出现当Service中将State中的值改变了,那么久会直接回到根节点。因为Service的时间轴是独立的,所以会有误差。

  • 相关阅读:
    DB2 for Z/os Statement prepare
    Foreign key (referential) constraints on DB2 LUW v105
    复制Informational constraints on LUW DB2 v105
    DB2 SQL Mixed data in character strings
    DB2 create partitioned table
    MVC中使用EF的技巧集(一)
    Asp.Net MVC 开发技巧(二)
    Linq使用技巧及查询示例(一)
    Asp.Net MVC 开发技巧(一)
    Asp.Net MVC Identity 2.2.1 使用技巧(八)
  • 原文地址:https://www.cnblogs.com/wbx-Blog/p/7147601.html
Copyright © 2020-2023  润新知