斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论

概述

本文章对应课程第十一章 42节。这篇文章会进一步地为AI添加新功能,创建自定义任务,允许AI发射子弹,并且讲解观察器中断的用法。

最终效果:

目录

  1. 创建自定义任务节点
  2. 观察器中止是什么
  3. 优化AI

创建自定义任务节点 BTTask

这节课我们要为AI小兵添加进入攻击范围后进行射击的功能。正如上节课看到的那样,行为树支持各种各样的任务,包括朝目标前进、Wait等我们已经使用过的UE自带Task。这节课的目标是自定义Task节点,实现开火的功能。具体的细节,我们边做边说。

让我们像以前做的一样,右键编辑器,勾选搜索全部,找到BTTasksNode类,将其作为父类。

创建BTTaskNode的子类

接着修改代码如下,对于BTTaskNode的子类,我们目前只需要重写ExecuteTask函数即可。返回值为EBTNodeResult::Failed和EBTNodeResult::Successd,对应行为树的节点返回失败或者成功。

逻辑也很简单,就是获取对象等一系列API操作,作为初学者熟悉使用即可。

除此以外,我们还添加了一个自定义子弹类型的对象,这些操作在大家学习编写人物的时候已经相当熟悉了。

//SBTTask_RangeAttack.h
UCLASS()
class FPSPROJECT_API USBTTask_RangeAttack : public UBTTaskNode
{
GENERATED_BODY()
protected:
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; UPROPERTY(EditAnywhere, Category = "AI")
TSubclassOf<AActor> ProjectileClass;
}; //SBTTask_RangeAttack.cpp
EBTNodeResult::Type USBTTask_RangeAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
AAIController* MyController = OwnerComp.GetAIOwner();
if(ensure(MyController))
{
ACharacter* MyPawn = Cast<ACharacter>(MyController->GetPawn());
if(MyPawn == nullptr)
{
return EBTNodeResult::Failed;
} AActor* TargetActor = Cast<AActor>(OwnerComp.GetBlackboardComponent()->GetValueAsObject("TargetActor"));
if(TargetActor == nullptr)
{
return EBTNodeResult::Failed;
} FVector MuzzleLocation = MyPawn->GetMesh()->GetSocketLocation("Muzzle_01");
//方向向量=目标位置-当前位置
FVector Direction = TargetActor->GetActorLocation() - MuzzleLocation;
FRotator MuzzleRotation = Direction.Rotation(); FActorSpawnParameters params;
params.Instigator = MyPawn;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ensure(ProjectileClass);
AActor* NewProj = GetWorld()->SpawnActor<AActor>(ProjectileClass, MuzzleLocation, MuzzleRotation);
return NewProj ? EBTNodeResult::Succeeded : EBTNodeResult::Failed;
} return EBTNodeResult::Failed;
}

接下来验证我们的代码。修改行为树如下,相对于上节课添加了左边两个节点,并添加了一个装饰器,用于检测是否已进入攻击范围。

图中的RangeAttack则是我们刚才编写的自定义任务,添加方式和上节课的MoveTo一样,搜索即可找到。

修改行为树,别忘了设置子弹的类型

PS:笔者在实验的时候,经常贪图方便使用热重载。这次出现了子弹类型没有暴露在蓝图中的问题。关闭UE重新编译才顺利出现,果然不能信任热重载。

被炸烂惹

上图是运行游戏的结果。可以看到,当敌对小兵进入设定的攻击范围后,执行了后面的Wait节点,等待一段时间后,重新回到最左边,执行了RangeAttack任务,由于Selector节点的特性,反复执行了最左边的节点,连续发射了大量子弹进行了狂轰滥炸。到目前为止,我们的初期目标已经达成了。

因为真正的游戏中不会出现这么蠢的AI,我们还需要对这个AI进行一些优化,例如发射子弹有冷却时间,能够三连发,能够在进入范围后立刻开火等功能。

观察器中断的概念

我们首先要实现的是进入攻击范围后能立刻开火。在原来的设计中,当AI跑入攻击范围后,会先执行行为树最右边Wait任务,等待上一段时间。现在我们希望马上执行RangeAttack任务,除了调整节点的顺序以外,行为树还给我们提供了一个很有用的功能,就是观察器中止(Observer Aborts )。由于上节课没有说清楚,我这里详细的说一下。

点击装饰器,可以看到细节面板有观察者中止的选项。

观察器中止

官方文档的解释十分的晦涩难懂,这里我仅以实用的角度简单描述一下观察器中止的用法,实际效果还需要读者自行进行测试。

在行为树中,观察器中断(Observer Aborts)是我们控制行为树任务(BehaviorTreeTask,BTT)执行的最重要的设置之一,其能在满足行为树装饰器(BehaviorTreeDecorator,BTD)临界条件后有条件地中断被观察者中断标记为中断范围的所有节点的执行。

说人话就是,如果你设置了观察器,当装饰器观测到自己负责的这个黑板键值变化时, 这个装饰器就有权利中断后面正在运行的节点,转而从装饰器这个节点开始运行。

其中下拉框有四个选项,其作用分别是:

  • None:不终止执行。
  • Self:终止自己及节点以下的所有子树。
  • Lower Priorit:终止此节点右方(具体的说是拥有最深共同父级)的所有节点。
  • Both:终止自己及节点以下的所有子树及右方所有节点。

如下图所示,当Within Attack Range节点设置了Lower Priorit时,右边的的节点全部变成了蓝色,当Within Attack Range的条件达成后,不管蓝色的节点在运行什么任务,都会被直接中断,转而从这个节点开始运行。

设置为Lower Priorit

当设置为self时,发现只有子树节点变成了绿色。当观测到条件变化时,会中断子树的任务,从装饰器的节点开始运行。

设置为self

both则是两种设置的集合,不再赘述。

设置为Both

另外,通知观察者有两种方式, 用于控制行为树如何通知观察者中断:

一种是结果改变时(On Result Change):对应装饰器的Bool结果发生变化时通知中断

一种是值改变时(On Value Change):对应装饰器的值(一般是行为树对应的黑板中此装饰器使用的黑板键的值)变化时通知中断。

由于我们这里使用的都是Bool,所以都默认On Result Change即可。

通知观察者

优化AI

子弹发射冷却和循环

回到正题来,大家可以慢慢咀嚼一下刚才讲的东西,我们先把子弹发射冷却和循环给做出来。

将行为树修改成这样子。相比于之前,添加了两个装饰器节点和一个Sequence节点。斯坦福课程里使用的是Selector节点,而这里使用的是Sequence节点。因为只有一个子节点,在这里两者是没有区别的,请读者充分理解两者的区别后再提出疑问。

CoolDown和Loop都是装饰器节点

顾名思义,Cooldown节点会控制节点执行的间隔,当节点处于冷却时间时,会直接返回false。Loop装饰器则会循环执行下面的节点,具体情况读者可以自行实验。

到现在为止,这个AI的行为模式为:如果目标在攻击范围外,会向目标移动。进入攻击范围后,会等待一段时间(这里设置为1s)。然后开枪向角色以0.2s为间隔时间射击三次,冷却2s。在此期间,行为树会运行后面两个节点,直到wait结束后重新判断是否结束冷却。

设置优先级

当这个wait设置的时间很长(例如5s)时,有时即使冷却时间结束了他也不会立即攻击,会傻傻地等待wait结束。这时候就需要用到之前提到的观察器中断。

将Within Attack Range的观察器中断设置为Both,Cooldown的观察器中断设置为Lower Priorit,根据之前的理论,当角色进入攻击范围时或者冷却时间结束都会立刻执行当节点。由于Within Attack Range设置为Both,当角色离开攻击范围时也会立刻停止执行后面的节点并返回false。

另外,和上节课一样,将Out of Attack Range节点设置为Both,这样角色在进入攻击范围时就会立刻停下来了。这部分可以实验的东西相当多,请读者自行实验。

最后的效果如下:

这里用走位很细节的在AI射出两发子弹的时候离开了他的攻击范围,这时候他会立即停止射击,执行中间的节点。

设置攻击的时候面朝玩家

这个实际上就是一个小细节。要实现这个功能,我们通常会想到可以在执行攻击动作的时候使用SetActorRotation函数修改AI小兵的朝向,实际上行为树为我们自带了这样一个服务(BTService)。这给我们带来了思路,我们可以使用BTService,在节点激活的情况下定时调整角色的状态,当然这里我们直接使用即可。

设置朝向,你也可以将它放在selector节点

参考链接

https://www.bilibili.com/read/cv13400311

https://www.cnblogs.com/timy/p/9048267.html

斯坦福 UE4 C++ ActionRoguelike游戏实例教程 02.AI自定义任务和观察器中断的更多相关文章

  1. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程02:关键帧动画导入与切割》

    2. 关键帧动画导入与切割 动画的分割与导入概述: 在游戏当中,游戏角色在不同状态下会有不同的动作,这些动作在引擎里相当于一段段的动画片段.当导入模型资源的时候,连同模型动画都会一并导入到引擎中.开发 ...

  2. Cocos2d-x3.0游戏实例《不要救我》第十篇(结束)——使用Json配置数据类型的怪物

    如今我们有2种类型的怪物,并且创建的时候是写死在代码里的,这是要作死的节奏~ 所以.必须可配置.不然会累死人的. ; i < size; ++i) { int id = root[i][&quo ...

  3. Cocos2d-x3.0游戏实例之《别救我》第八篇——TiledMap实现关卡编辑器

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/musicvs/article/details/25368273 好吧.我真心全然搞不懂.我如今仅仅只 ...

  4. 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇:简介及目录》(附上完整工程文件)

    G-3D引擎2D射击类游戏制作教程 游戏类型: 打飞机游戏属于射击类游戏中的一种,可以划分为卷轴射击类游戏. 视觉表现类型为:2D 框架简介: Genesis-3D引擎不仅为开发者提供一个3D游戏制作 ...

  5. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

  6. Web 开发中应用 HTML5 技术的10个实例教程

    HTML5 作为下一代网站开发技术,无论你是一个 Web 开发人员或者想探索新的平台的游戏开发者,都值得去研究.借助尖端功能,技术和 API,HTML5 允许你创建响应性.创新性.互动性以及令人惊叹的 ...

  7. 值得 Web 开发人员收藏的20个 HTML5 实例教程

    当开始学习如何创建 Web 应用程序或网站的时候,最流行的建议之一就是阅读教程,并付诸实践.也有大量的 Web 开发的书,但光有理论没有实际行动是无用的.现在由于网络的发展,我们有很多的工具可以用于创 ...

  8. 对《[Unity官方实例教程 秘密行动] Unity官方教程《秘密行动》(十二) 角色移动》的一些笔记和个人补充,解决角色在地形上移动时穿透问题。

    这里素材全是网上找的. 教程看这里: [Unity官方实例教程 秘密行动] Unity官方教程<秘密行动>(九) 角色初始设定 一.模型设置: 1.首先设置模型的动作无限循环. 不设置的话 ...

  9. Cocos2d-x3.0游戏实例《不要救我》第一章——前言

    我们可以学习? 这是一个非常easy游戏.但更多的东西用(对于初学者).至少,对于它的一个例子,有点多. 笨木头花心贡献.啥?花心?不呢.是用心~ 转载请注明,原文地址:http://www.benm ...

  10. 源于《Unity官方实例教程 “Space Shooter”》思路分析及相应扩展

    教程来源于:Unity官方实例教程 Space Shooter(一)-(五)       http://www.jianshu.com/p/8cc3a2109d3b 一.经验总结 教程中步骤清晰,并且 ...

随机推荐

  1. 其它——ZeroRPC和SimpleXMLRPCServer

    文章目录 一 Python中RPC框架 二 SimpleXMLRPCServer使用 服务端 客户端 三 ZeroRPC使用 服务端 客户端 一 Python中RPC框架 自带的:SimpleXMLR ...

  2. 使用 Helm 管理应用的一些 Tips

    背景 Helm 是一个 Kubernetes 的包管理工具,有点类似于 Mac 上的 brew,Python 中的 PIP:可以很方便的帮我们直接在 kubernetes 中安装某个应用. 比如我们可 ...

  3. C++算法之旅、09 力扣篇 | 常见面试笔试题(上)算法小白专用

    刷题的目的是为了更好的理解数据结构与算法,更好的理解一些封装起来的库函数是怎么实现的,而不是简简单单的为了刷题而刷题. 时间.空间复杂度 事后统计法 提前写好算法代码和编好测试数据,在计算机上跑,通过 ...

  4. EMC ndmp NAS

    NAS 是带存储系统的专用高性能文件服务器.它可提供文件级数据访问和共享. NAS 使用网络和文件共享协议,包括用于数据传输的 TCP/IP 以及用于远程文件服务的 CIFS 和 NFS. 最简单的备 ...

  5. Opencv系列之一:简介与基本使用

    1 Opencv简介 Opencv是计算机视觉中经典的专用库,其支持多语言,跨平台,功能强大.Opencv-Python为Opencv提供了Python接口,使得使用者在Python中能够调用C/C+ ...

  6. 传输层协议:TCP/IP协议,UDP的协议

    传输层: 定义了⼀些传输数据的协议和端口号( WWW 端口 80 等),如:TCP(传输控制协议,传输效率低,可靠性强,⽤于传输可靠性要求⾼,数据量⼤的数据), UDP(⽤户数据报协议,与 TCP 特 ...

  7. 一文搞懂深度信念网络!DBN概念介绍与Pytorch实战

    本文深入探讨了深度信念网络DBN的核心概念.结构.Pytorch实战,分析其在深度学习网络中的定位.潜力与应用场景. 关注TechLead,分享AI与云服务技术的全维度知识.作者拥有10+年互联网服务 ...

  8. 练习回—编译安装nginx

    练习回 练习,编译安装nginx. 1.yum安装依赖环境 yum -y install pcre-devel zlib-devel gcc gcc-c++ make 2."下载" ...

  9. idea java项目启动后访问html页面乱码

    最近在做一个较久的项目,用的还是servlet+html(jsp),代码拉到本地后运行,访问登录页login.html既然乱码,先看个乱码的效果 怎么样,是不是很经典的乱码,别着急,我们一点点来分析乱 ...

  10. 为什么FPGA中推荐使用独热码?

    独热码只有一个比特位不同,所以在进行比较的时候: 假如我们要判断状态机是否处于某状态S1,代码如下 格雷码:assign S1 = (STATUS == 2'b01) 二进制码:assign S1 = ...