56. UE5 RPG 给敌人添加AI实现跟随玩家

在这一篇里,我们要实现一下敌人的AI,敌人也需要一系列的行为,比如朝向英雄攻击,移动,在满足条件时施放技能。这些敌人的行为可以通过使用UE的内置的AI系统去实现。
在UE里,只要是基于Character类创建的蓝图,都能够找到相应的AI配置项
在这里插入图片描述
可以自动控制角色的基类为AIController,配合AIController的还有两个类,分别是行为树(Behavior Tree)用于存储角色的行为逻辑以及用于存储AI使用的数据的黑板(Blackboard Data)。
接下来,我们将一步步创建适合RPG项目的AI系统。

创建AIController

首先基于AIController父类创建一个子类
在这里插入图片描述
命名为RPGAIController
在这里插入图片描述
在代码里使用AI系统,我们还需要在Build.cs里面添加AI模块
在这里插入图片描述
在RPGAIController类里面,我们需要初始化黑板和行为树控件,用于后续控制它们的实例
在AIController类里面,已经定义了黑板的控件
在这里插入图片描述
我们在RPGAIController里面再申明行为树控件

protected:UPROPERTY()TObjectPtr<UBehaviorTreeComponent> BehaviorTreeComponent;

记得设置它的构造函数


public:ARPGAIController();

我们在构造函数里进行初始化这两个控件,为了保证不出错,我们需要进行检查,如果出错会抛出错误。

ARPGAIController::ARPGAIController()
{Blackboard = CreateDefaultSubobject<UBlackboardComponent>("BlackboardComponent");check(Blackboard);BehaviorTreeComponent = CreateDefaultSubobject<UBehaviorTreeComponent>("BehaviorTreeComponent");check(BehaviorTreeComponent);
}

在敌人基类实现应用AIController

我们创建了一个自定义的AIController,我们需要应用到角色身上,才能够起作用,首先,我们在代码里增加角色对AIController的处理逻辑。
在RPGAIController里面,我们创建了黑板和行为树的控件,但是没有数据,我们在角色类上面增加一个参数,用于在蓝图中设置使用的行为树,并增加一个参数用于保存转换为RPGAIController实例后的参数。
所以在EnemyBase类里,增加以下参数

	UPROPERTY(EditAnywhere, Category="AI")TObjectPtr<UBehaviorTree> BehaviorTree;UPROPERTY()TObjectPtr<ARPGAIController> RPGAIController;

然后我们要覆写PossessedBy函数,这个函数之前我们在服务器初始化ASC时用过,它将在Pawn被控制器(PlayerController和AIController都行)控制时触发回调

virtual void PossessedBy(AController* NewController) override;

我们在回调里面,首先将控制器转换为RPGAIController并存储起来,然后从我们设置的角色使用的行为树里获取行为树使用的黑板数据初始化,然后调用运行行为树。
这些操作都需要在服务器上面运行,所有的客户端都复制服务器的结果来保证所有的行为统一。

void AEnemyBase::PossessedBy(AController* NewController)
{Super::PossessedBy(NewController);//当前设置只在服务器端运行if(!HasAuthority()) return;//AIController是在服务器端执行的,所以需要在PossessedBy函数回调中获取服务器返回RPGAIController = Cast<ARPGAIController>(NewController);//初始化行为树上设置的黑板RPGAIController->GetBlackboardComponent()->InitializeBlackboard(*BehaviorTree->BlackboardAsset);//运行行为树RPGAIController->RunBehaviorTree(BehaviorTree);
}

接下来我们编译代码打开UE,创建RPGAIController的蓝图类
在这里插入图片描述
顺便也将后面使用黑板和行为树创建
在这里插入图片描述
黑板推荐以BB_为前缀,行为树推荐以BT_为前缀
在这里插入图片描述
打开敌人的蓝图基类,将我们创建的BP_RPGAIController设置上去
在这里插入图片描述
设置行为树
在这里插入图片描述
打开行为树,在细节这里设置使用的黑板
在这里插入图片描述
在行为树上设置简单播放一个哥布林的动画,查看应用效果是否实现。
在这里插入图片描述
如果能够正常播放动画,证明实现了对应的效果。
在这里插入图片描述

实现行为树服务

对AI行为树不大了解的小伙伴可以查看一下之前我写Unreal Engine 5.1 AI行为树基础入门
我们打开行为树,首先在下面增加一个Selector节点。
elector节点主要用于在多个子树之间进行选择。它从左到右执行其下的子项(即子节点),当其中一个子项执行成功时,整个Selector节点即认为成功,并终止后续的子项执行。如果所有的子项都执行失败,那么Selector节点也失败。
在这里插入图片描述
我们可以在运行状态下查看,它在子项运行成功时直接返回,不再判断后续的子项,而是直接返回到Selector,Selector也作为成功返回。由于我们没有设置其它节点,行为树将重新运行,接着再运行第一项子项
在这里插入图片描述
我们右键Selector还可以看到给节点附加装饰器和服务的内容,它们的含义分别是:

  1. 装饰器节点用于控制是否应该执行一个节点。它们可以用来检查某些条件,如是否看到了玩家,或者是否在特定距离内。这些节点可以修改节点的行为,比如限制运行次数或添加延迟。在行为树中,它们通常被连接到任务节点或服务节点,用于在这些节点执行之前或之后添加额外的逻辑或条件判断。
  2. 服务节点不执行实际的行为,而是定期运行,用于设置和维护黑板(Blackboard)上的数据。黑板是一个用于存储AI状态信息的区域,可以被行为树中的其他节点访问和修改。服务节点通常连接到合成节点或任务节点,只要连接的节点被执行,它们就会以定义的频率执行。
    在这里插入图片描述
    为了能够实现对敌人目标的获取,我们下面实现一个自定义的服务类。
    这里我们创建一个新的类,基于BTService_BluePrintBase,这样,我们创建的类,也可以通过蓝图创建使用
    在这里插入图片描述
    我们将其命名为BTService_FindNearestPlayer,用于查找最近的玩家,在后续实现对其的攻击。
    在这里插入图片描述
    打开代码,我们先看一下父类上能够获取的内容,我们发现可以直接从父类获取AIController和AIController控制的Actor
    在这里插入图片描述
    在代码里,我们首先覆写父类的函数,TickNode,这个函数会在一定的时间后重复执行,用于更新数据使用。
protected:virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;

首先在代码里测试是能够获取到那两个参数的值

void UBTService_FindNearestPlayer::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);GEngine->AddOnScreenDebugMessage(1, 1.f, FColor::Red, *AIOwner->GetName());GEngine->AddOnScreenDebugMessage(2, 1.f, FColor::Green, *ActorOwner->GetName());
}

接着编译打开UE,创建一个基于自定义服务类的蓝图
在这里插入图片描述
打开蓝图,左侧我们可以覆写函数,不覆写无法触发对应的函数
在这里插入图片描述
类默认值这里,我们可以设置节点名称,方便在行为树里使用,服务这里为触发Tick事件的时间间隔,可以作为优化的性能点。
在这里插入图片描述
打开行为树,右键可以发现我们创建的两个类,一个是c++类另一个是蓝图类
在这里插入图片描述
选择蓝图类,会发现它会附着到节点上,并显示我们设置的节点名称
在这里插入图片描述
你现在直接运行会发现没有对应的打印,所以我们需要在服务里覆写Tick,来实现行为树对Tick的调用,你会发现接收Tick有两种,一个是普通版,还有一个AI专用的,这里我们覆写AI的
在这里插入图片描述
不需要执行任何操作,直接编译
在这里插入图片描述
运行这样会发现左上角的打印,我们发现两个打印的都是AIController,这代表它的AIController所有者和Actor所有者都是AIController
在这里插入图片描述
接着我们在蓝图中打印可以获取的数据
在这里插入图片描述
可以获取对应的AIController 控制的Pawn 以及更新时间间隔
在这里插入图片描述

在黑板添加属性

到现在了,我们还没使用黑板。黑板是一个比喻,相当于以前上学时候的黑板,而每个同学相当于一个行为树的节点,它们可以实现从黑板获取数据,也可以修改数据,黑板是节点和节点之间交互数据的一个渠道。
设置黑板属性你需要先设置它的类型
在这里插入图片描述
然后再选择它的基类
在这里插入图片描述
如果将实例已同步选中,那么,所有角色从黑板当前值获取的结果都是一样的。
在这里插入图片描述
我们在原有的基础上增加两个属性一个是需要攻击的目标,另一个是和目标的距离
在这里插入图片描述

我们在黑板上设置了属性以后,如何对其填充数据呢,在服务蓝图中,我们创建变量类型需要设置为黑板键选择器
在这里插入图片描述
并且要将右边的眼睛打开
在这里插入图片描述
回到行为树中,你可以选择当前属性需要绑定的黑板属性
在这里插入图片描述
在服务蓝图中,获取属性的值,你需要通过函数Get来获取对应的类型的值
在这里插入图片描述
如果你需要设置,你需要调用对应的Set方法
在这里插入图片描述
比如下面就是将控制的Pawn作为SelfActor设置
在这里插入图片描述

实现获取需要跟随的玩家

上面我们在蓝图中实现了对黑板属性的获取以及设置,这个我们也可以在C++中设置。但是获取到需要跟随的角色如果在蓝图中实现性能消耗比在c++中实现消耗大,所以,我们将在c++中实现对角色的获取。
打开服务类,我们创建两个参数用于记录需要跟随的玩家以及距离

	UPROPERTY(BlueprintReadOnly, EditAnywhere)FBlackboardKeySelector TargetToFollowSelector;UPROPERTY(BlueprintReadOnly, EditAnywhere)FBlackboardKeySelector DistanceToTargetSelector;

在Tick中,我们首先获取到AIController控制的Pawn

APawn* OwningPawn = AIOwner->GetPawn();

然后根据角色设置设置的标签来获取角色敌人是谁,这个标签不是GameplayTag而是FName类型的

const FName TargetTag = OwningPawn->ActorHasTag(FName("Player")) ? FName("Enemy") : FName("Player");

这个标签的设置你需要在蓝图中,找到Actor的配置项,填写即可,可以填写多个进行分类,这有助于提升性能。分别设置好英雄和敌人的标签。
在这里插入图片描述
这样,我们就可以去获取目标标签在场景的所有实例

	TArray<AActor*> ActorsWithTag;UGameplayStatics::GetAllActorsWithTag(OwningPawn, TargetTag, ActorsWithTag);

接下来我们就要遍历获取的实力,比对它们和OwningPawn之间的距离,来获取最短距离的目标。这里我们首先将默认距离设置为浮点数最大值,然后遍历获取每个和OwningPawn之间的距离和距离变量比大小,如果两个角色之间的距离比变量的距离小,证明又发现了一个距离更近的角色,将距离和角色都存储下来。遍历完成后,我们获取到的距离和角色将是当前最小的那个

	//遍历所有的角色获取到最近的角色和距离float ClosestDistance = TNumericLimits<float>::Max(); //默认设置float最大值AActor* ClosestActor = nullptr;for(AActor* Actor : ActorsWithTag){const float Distance = OwningPawn->GetDistanceTo(Actor);if(Distance < ClosestDistance){ClosestDistance = Distance;ClosestActor = Actor;}}

然后我们调用内置方法去设置黑板属性,它们封装在UBTFunctionLibrary内

	//设置黑板数据UBTFunctionLibrary::SetBlackboardValueAsObject(this, TargetToFollowSelector, ClosestActor);UBTFunctionLibrary::SetBlackboardValueAsFloat(this, DistanceToTargetSelector, ClosestDistance);

实现跟随角色

我们编译代码打开UE,在行为树中,绑定对应的属性
在这里插入图片描述
运行你会发现它们被正确赋值
在这里插入图片描述
然后在下面增加一个MoveTo节点
在这里插入图片描述
右侧修改黑板键位TargetToFollow
在这里插入图片描述
运行起来你会发现都会追你
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/333080.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java欢迪迈手机商城设计与实现源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的欢迪迈手机商城设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 欢迪迈手机商城…

D - Permutation Subsequence(AtCoder Beginner Contest 352)

题目链接: D - Permutation Subsequence (atcoder.jp) 题目大意&#xff1a; 分析&#xff1a; 相对于是记录一下每个数的位置 然后再长度为k的区间进行移动 然后看最大的pos和最小的pos的最小值是多少 有点类似于滑动窗口 用到了java里面的 TreeSet和Map TreeSet存的是数…

【C语言】结构体内存对齐:热门面试话题

&#x1f525;引言 书接上文&#xff0c;我们了解关于结构体的基本知识&#xff0c;这篇将深入剖析结构体中一个重要的知识点:内存对齐 关于内存对齐是属于热门面试话题&#xff0c;对此单独放在一篇来分享 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记…

Docker Portainer使用

Portainer是什么 Docker Portainer是一个轻量级的 Web UI 管理界面,可以用来管理Docker环境。它提供了一个直观的控制台,用户可以通过它来管理Docker主机、容器、网络、卷等Docker资源。 Portainer的主要功能和特点包括: 容器管理:可以查看、启动、停止、删除容器,以及查看容器…

计算机毕业设计 | springboot+vue房屋租赁管理系统(附源码)

1&#xff0c;绪论 1.1 课题来源 随着社会的不断发展以及大家生活水平的提高&#xff0c;越来越多的年轻人选择在大城市发展。在大城市发展就意味着要在外面有一处安身的地方。在租房的过程中&#xff0c;大家也面临着各种各样的问题&#xff0c;比如需要费时费力去现场看房&…

host修改

前言 想要修改 hosts 文件&#xff0c;您需要具有对系统文件的适当访问权限&#xff0c;并且知道如何编辑文本文件。hosts 文件是一个用于域名解析的本地文件&#xff0c;它允许您为特定的 IP 地址指定主机名。 以下是在不同操作系统中修改 hosts 文件的步骤&#xff1a; 一、…

项目集成SkyWalking,基于k8s搭建

一、搭建SkyWalking 官方文档&#xff08;英文&#xff09;&#xff1a;skywalking/docs at master apache/skywalking 中文可以使用&#xff1a;GitHub - SkyAPM/document-cn-translation-of-skywalking: [已过期,请使用官网AI文档] The CN translation version of Apache…

【qt】自定义代理类

自定义代理类 一.应用场景二.创建自定义代理类1.创建一个类2.共有继承父类3.添加宏4.初始化父类5.拿到我们需要重写的虚函数 三.实现父类的4个虚函数1.创建代理组件2.设置代理组件数据3.设置模型数据4.跟新代理组件的位置 四.使用代理类1.头文件2.私有成员3.视图设置代理 五.其…

Web上机:JSP+Servlet+JDBC的交互流程

目录 需求与设计 基础需求&#xff1a; 项目结构&#xff1a; 项目逻辑&#xff1a; 运行图示&#xff1a; 代码实现 Login.jsp InsertServlet SelectServlet Table.jsp user mysql表结构 Web开发技术迭代速度日新月异&#xff0c;对于技术的更新往往是基于底层一…

基于Python+flask+echarts的气象数据采集与分析系统,可实现lstm算法进行预测

背景 基于PythonFlaskEcharts的气象数据采集与分析系统结合了强大的数据处理能力和可视化展示技术&#xff0c;旨在实现对气象数据的实时采集、存储和分析。通过Python编程语言实现数据采集模块&#xff0c;利用Flask框架搭建后端系统&#xff0c;实现数据处理、存储和分析功能…

Python小游戏——俄罗斯方块

文章目录 项目介绍环境配置代码设计思路1.初始化和导入库&#xff1a;2.定义颜色和屏幕尺寸&#xff1a;3.定义游戏逻辑&#xff1a;4.游戏循环&#xff1a; 源代码效果图 项目介绍 俄罗斯方块游戏是一款经典的益智游戏&#xff0c;玩家通过旋转和移动各种形状的方块&#xff…

深入剖析—【服务器硬件】与【Nginx配置】:从基础到实战

服务器硬件部分&#xff1a; Processor (CPU)&#xff1a;服务器的计算核心&#xff0c;负责处理数据和执行程序。Memory (RAM)&#xff1a;用于暂时存储和快速访问数据&#xff0c;决定了系统的运行速度和并发处理能力。Storage (HDD/SSD)&#xff1a;长期存储数据的设备&…

爱设计AiPPT.cn赵充:营销工作的AI进化

爱设计&AiPPT.cn是一家 AIGC 数字科技企业&#xff0c;致力于打造「下一代个人与组织的 Ai 工作站」 。目前旗下产品包括AiPPT.cn、爱设计AIGC 内容中台、365 编辑器、爱设计在线设计工具、AiH5 等超过 10 余款应用 AI 能力的内容创作工具。日前&#xff0c;爱设计&AiP…

期货学习笔记-横盘行情学习1

横盘行情的特征及分类 横盘行情的概念 横盘行情时中继形态的一种&#xff0c;一般常出现在大涨或大跌之后出现横盘行情是对当前趋势行情的修正&#xff0c;是对市场零散筹码的清理&#xff0c;是为了集中筹码更便于后期行情的展开 横盘行情的特征 1.水平运动&#xff1a;该…

从零自制docker-15-【实现 mydocker run -d 支持后台运行容器】

文章目录 实现目的莫名奇妙的问题对之前upper层出现root补充对run某些命令出现找不到文件或目录的原因代码效果 实现目的 docker run -d时容器在后台运行&#xff0c;而不会进入命令行交互形式 首先是需要添加-d选项然后设置当添加-d选项时候主进程不会等待子进程&#xff0c…

从Python代码到pip包:打包Python项目

大家好&#xff0c;在软件开发的世界中&#xff0c;共享和重用代码是至关重要的。Python社区为我们提供了丰富的资源&#xff0c;使得我们能够轻松地与他人分享我们的工作&#xff0c;并从他人的工作中受益。将代码打包成pip包&#xff08;Python包管理器&#xff09;是一种常见…

Moto和Inter字节序

inter: 低地址按照start_bit位放低字节依次往高字节填充 MotoLsb: 低地址按照start_bit位放高字节&#xff0c;依次往低字节填充MotoMsb&#xff1a;高字节按照start_bit位放低地址&#xff0c;依次往高字节填充

uni-app实现页面之间的跳转传参(八)

界面之间的参数传递在 开发中经常会用到,这节主要将一下uni-app开发应用是的传参情况。如下图所示,我的一级界面将点检分成三类:日点检、周点检和年保养;在点击相应的会导航到相应的功能。 在uni-app中常用的方法有uni.navigateTo(OBJECT)、uni.redirectTo(OBJECT);简单的…

怎么图片转excel表格免费?介绍三个方法

怎么图片转excel表格免费&#xff1f;在日常工作中&#xff0c;我们经常需要将图片中的表格数据转化为可编辑的Excel格式。幸运的是&#xff0c;市面上有多款软件支持这一功能&#xff0c;并且部分软件还提供免费使用的选项。本文将为您详细介绍几款可以免费将图片转换为Excel表…

Flink DataStream API 介绍

一、介绍 官网 DataStream API 得名于特殊的 DataStream 类&#xff0c;该类用于表示 Flink 程序中的数据集合。你可以认为 它们是可以包含重复项的不可变数据集合。这些数据可以是有界&#xff08;有限&#xff09;的&#xff0c;也可以是无界&#xff08;无限&#xff09;的…