AISystemPlugin
创建 Behavior Tree 和 Blackboard
创建行为树和黑板:

在黑板中添加一个Object类型的Key:

将Base Class设置为Actor:

将行为树的Move To函数中的BlackboardKey设置为刚刚设置的AttackTarget:

使用 AIController 运行 Behavior Tree 并给 Blackboard 中的变量赋值
创建AIContorller C++类:

需要包含AIModule模块:

创建AIController蓝图类:


为Enemy中的AI Controller Class赋值:

将行为树和黑板键AttackTargetKeyName传入:

运行 Behavior Tree 并给 Blackboard 中的变量赋值

添加Nav Mesh Bounds Volume

按P键显示:

Custom Task
创建Task类C++类:
注意:Task类是UBTTaskNode而不是UBTTask_BlueprintBase蓝图类型

在行为树中添加自定义Task:


Task中的继承函数:

在BaseEnemy中创建Attack函数并创建AttackEnd委托:

方法一:在AttackMontage动画播放完成后调用委托:


在执行Task函数中绑定委托并调用Attack函数,然后返回InProgress
当Attack函数调用委托时,就会调用HandleAttackEnd函数来手动结束任务
如果是异步执行,必须要有AbortTask和OnTaskFinished函数来兜底,同步执行(ExecuteTask 立即返回 Succeeded/Failed)可以不加

Custom AnimNotify
方法二:使用AnimNotify来结束Montage动画:
创建UAnimNotify C++类:


调用委托:

在Montage动画中添加AnimNotify:


Focus Target Task
用于在攻击时Focus Target:
创建Task:

创建黑板键,用于在行为树中赋值:

获取黑板,黑板通过KeyName来读取TargetActor,通过AIController设置Focus为目标角色:

在行为树中添加Task并给Attack Target key赋值:

使用Use Contorller Desired Rotation,取消Orient Rotation to Movement:

Clear Focus Task
用于在行走时清除Focus:




Spawn Weapon Task
创建Weapon类:



在Enemy中创建Spawn Weapon的函数:


创建Task:




Decorator(修饰器)
创建修饰器,用于判断Enemy是否装备了武器:


添加修饰器并反转:

在Enemy中添加bool变量用于判断:

当Spawn武器后,bWeaponEquipped设置为true:

修饰器:


按路线巡逻
创建Actor类型用作Patrol Route:

添加更新PatroulRoute的函数和获取当前PointLocation的函数:


创建蓝图类:


将蓝图拖到Map中:

创建Interface:

创建一个获取PatrolRoute的纯虚函数:

在Enemy中实现这个纯虚函数:


创建按路线巡逻Task:


通过Actor来获取Interface,从而获取到对应的PatrolRoute,通过PatrolRoute获取到对应的点位置,MoveTo到点位置,MoveTo完成后调用OnMoveCompleted函数,在回调函数中更新PatrolRoute,若被打断,则停止Move:

其中AbortTask中必须加上RemoveDynamic,否则中断时会静止不动:

设置Enemy的Patrol Route:

设置行为树:

设置移动速度
创建Interface用于设置速度:

创建速度类型的Enum:

在Enemy中重写纯虚函数:


创建Task:

在行为树中赋值SpeedType:

使用接口调用SetMovementSpeed:

在行为树中使用:

随机巡逻
添加修饰器,判断是否有路线:



如果没有路线则进行随机巡逻:
在Build中添加NavigatonSystem模块:

在Enemy中添加获取随机巡逻点的函数并设置巡逻范围,以及MoveTo可接受范围:

使用NavigationSystemV1来获取可导航区域的随机点:

创建随机巡逻的Task:


在Task中MoveToLocation,如果当前的RandomLocation已经处于AcceptRadius范围内,则直接返回成功,否则会导致Enemy不返回任何值从而卡住:

其中AbortTask中必须加上RemoveDynamic,否则中断时会静止不动:

在行为树中添加:

创建State

在黑板中添加Enum:

设置EnumeName与cpp中的Enum名称一样:

在Selector中添加BlackBoard的Decorator:

如果BlackBoard中的State==Attacking,则进入:

如果BlackBoard中的State==Passive,则进入:

添加ClearFocus:

添加AIStateKeyName并添加设置EnemyState的函数:

初始化设置为Passive:

添加拔剑和收剑
在Enemy中:
创建动画结束事件的委托
创建PlayMontage函数
设置Sword绑定位置的DrawSword函数:(SheatheSword同理)


创建DrawSword的Task:

在Task中调用Enemy来播放蒙太奇动画并绑定DrawSwordEnd委托:

创建DrawSword的AnimNotify:

调用Enemy的DrawSword用于切换Sword的绑定位置:

创建DrawSwordEnd的AnimNotify:

在DrawSwordEnd结束事件中调用在DrawSword Task中实现的委托OnDrawSwordEnd:

创建IsSwordDrawed的修饰器:

返回bSwordDrawed:

在Montage中添加DrawSword和DrawSwordEnd的AnimNotify:

取消Enable Root Motion,否则无法Focus Target:

在行为树中添加DrawSword和SheatheSword:

添加AI Perception Component(AI感知系统)和Team(队伍系统)
创建TeamType的枚举类型,并添加转换的辅助函数:

创建TeamComponent,用于在Character中设置Team:

继承IGenericTeamAgentInterface,并重写GetGenericTeamId函数,初始化TeamId为Player:

返回TeanId:

创建蓝图类:

在Character中添加TeamComponent:

在AIController中添加AIPerception并重写Team类:

重写Team函数:

配置AIPerception:

在Character中临时加入测试:

按下”键并按下小键盘4键:测试AIPerception

配置AIPerception
创建感知处理函数
创建AIPerception自带的感知回调函数
创建兴趣点,用于Enemy调查兴趣点
创建最大检测距离和最大移动距离,用于Enemy受到Damage时,不会一直走到兴趣点,而是朝向兴趣点移动:

绑定函数:

通过SenseID来调用不同的感知回调函数:

在感知状态中设置对应的EnemyState:


Investigate中设置兴趣点:

创建设置EnemyState的Task:



要想使用OnTargetPerceptionForgotten,必须在AIPerception中设置Age(最大记得的事件)不能为0,并且勾选Forget Stale Actors:

在黑板中创建PointOfInterest:

创建Investigate的行为树:

EQS
创建EQS:


在蓝图中创建用于测试EQS的Pawn:


将Pawn拖入世界中:

创建Context类用于将攻击目标设置为查询系统的上下文数据:

从EnemyAIController中存储AttackTarget:



在EQS中设置Circle Center为AttackTarget,这样就会围绕角色生成一圈的球:
通过PathExist确保球体是Enemy可以到达的:
通过DIstance确保Enemy只会在左右最近的两个球体中选择:


创建一个新的Context用于Test:

获取Player Start作为中心点用于测试:


在行为树中添加Strafe并为Attack添加Cooldown:

完善Strafe的EQS
创建Decorator判断是否处于目标范围内:

在Interface中创建获取AttackRadius和DefendRadius的函数:

在Enemy中初始化:

在Enemy中实现Interface:

判断Enemy与AttackTarget的距离是否小于目标值:


创建MoveToTargetRange的Task,将Acceptance Radius作为黑板参数传递:



在黑板中创建两个Radius:

在Strafe中加上判断是否处于防御范围内,若不处于,则MoveToTargetRange到防御范围:

添加FindCover的EQS
创建EQS:

添加Trace,确保在Target看不见的地方,并设置Item Height:

在行为树中添加FindCover:

添加子类Enemy并添加SubBehaviorTree
创建BaseEnemy的子类,用于设置近战Enemy和远程Enemy:


创建两个树用于作为子树:

将树中的Investigate逻辑复制到子树中:

创建一个新的Rotate To face PointOfInterest,确保直接转向目标:

将树中的Patrol逻辑复制到子树中:

在树中使用Run Behavior来替代原有逻辑:

为近战Enemy和远程Enemy分别创建行为树:

将BaseEnemy中的大部分逻辑删除:

近战Enemy:

在不同的Enemy中设置对应的BehaviorTree,然后再传递为AIController:

设置远程敌人的BehaviorTree
创建开火的Notify


通过射线检测来ApplyDamage并ReportDamageEvent给AIPerception:

在Notify中调用Fire:

创建Decorator判断Enemy是否能看见Target:


创建Health和Damage:


创建恢复Health的Task:


创建判断是否Health低于阈值的函数:


如果在Attack中,Health低于阈值,则执行FindCover的EQS移动到Cover并HealHealth:
如果Enemy看不见Target,则MoveToSeeTarget:

添加Sword
在Weapon中添加Box和Start,End:

创建Box的OnOverlap函数,当Overlap时,通过Start和End的位置来BoxTrace判断是否碰到Block Visibility Channel的物体:

设置Weapon的Owner:

设置Enemy的Mesh为WorldDynamic,从而与Capsule进行区分,并将Visibility设置为Block,从而可以进行BoxTrace:

在Enemy中创建EnableCollision和DisableCollision并清空IgnoreActors防止重复检测:

创建AnimNotifyState:

开始时启用Collision,结束时关闭:

在CanSeeTarget中也需要Ignore目标,不然会被Target的Visibility通道阻挡并返回,从而导致一直CanNotSeeTarget:

在AttackMontage中添加AnimNotifyState:

在剑中添加Box和Start,End:

添加Dead和HitReaction动画
添加Service,用于当Enemy的Attack目标死亡时,将Enemy的State重新设置为Passive:



在Attack上添加Service:

在Enemy的TakeDamage中添加GetHit和Die的函数:

GetHit通过角度来判断受击方向,HitReactMontage设置为可以与其他动画一起播放,如果在Attack,则不播放GetHitMontage:

Die不使用动画,使用模拟物理来达到布娃娃效果:

在Attack中设置是否被打断,打断则调用OnAttackEnd:

创建各个方向的GetHitAnimation,并创建一个新的Slot专门用于播放GetHitMontage:

在Anim中使用LayeredBlendPerBone来融合播放GetHitMontage:


浙公网安备 33010602011771号