斯坦福课程 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. MySql 数据 管理表的操作

    管理表的操作 use scoredb; -- 查看数据库中有哪些表 show tables; show tables from bipowernode; -- 查看数据表的基础结构 show colu ...

  2. 其它——python操作kafka实践

    文章目录 1.先看最简单的场景,生产者生产消息,消费者接收消息,下面是生产者的简单代码. ------------------------------------------------------- ...

  3. 文心一言 VS 讯飞星火 VS chatgpt (108)-- 算法导论10.1 6题

    六.用go语言,说明如何用两个栈实现一个队列,并分析相关队列操作的运行时间. 文心一言: 使用两个栈实现队列的基本思想是利用一个栈(stack1)来处理入队操作,另一个栈(stack2)来处理出队操作 ...

  4. 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理

    我们在WPF应用端的界面中,使用lepoco/wpfui 来做主要的入口框架,这个项目它的菜单内置了不少图标,我们需要在动态菜单的配置中,使用它作为图标的展示处理,本篇随笔介绍如何基于图标枚举集合进行 ...

  5. 我试图扯掉这条 SQL 的底裤。

    你好呀,我是歪歪. 这次带大家盘一个我觉得有点意思的东西,也是之前写<一个烂分页,踩了三个坑!>这篇文章时,遇到的一个神奇的现象,但是当时忙着做文章搞定这个主线任务,就没有去深究这个支线任 ...

  6. Pandas 读取Eexcel - 间隔N行,读取某列数据

    间隔N行,读取某列数据 import pandas as pd def read_vertical(sheet_name, col_idx, gap): """ 竖着读数 ...

  7. 未能添加SSL证书,错误1312

    1.win+r打开运行,输入mmc 2.在控制台1[控制台根节点]->文件->添加/删除....->选择证书->添加-选择计算机账户->完成->确认 3.找到证书文 ...

  8. inventory 主机清单

    inventory 主机清单 //Inventory支持对主机进行分组,每个组内可以定义多个主机,每个主机都可以定义在任何一个或多个主机组内. //如果是名称类似的主机,可以使用列表的方式标识各个主机 ...

  9. P4022 [CTSC2012]熟悉的文章 题解

    题目链接 简要题意 给定 \(m\) 个模板串和 \(n\) 个匹配串,如果一个字符串是一个模板串的子串且长度不小于 \(L\) 则称其为"熟悉的",对于每个匹配串,求一个最大的 ...

  10. 【爬虫实战】用Python采集任意小红书笔记下的评论,爬了10000多条,含二级评论!

    目录 一.爬取目标 二.爬虫代码讲解 2.1 分析过程 2.2 爬虫代码 三.演示视频 一.爬取目标 您好!我是@马哥python说 ,一名10年程序猿. 我们继续分享Python爬虫的案例,今天爬取 ...