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

news2024/9/25 13:25:58

在这一篇里,我们要实现一下敌人的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.coloradmin.cn/o/1705945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【从零开始学习RabbitMQ | 第一篇】如何确保生产者的可靠性

目录 前言&#xff1a; 生产者重连机制&#xff1a; 生产者确认机制&#xff1a; Publisher Confirm&#xff08;生产者者确认&#xff09; Publish Return&#xff08;发布返回&#xff09; 总结&#xff1a; 前言&#xff1a; 在现代的分布式系统中&#xff0c;消息队…

5个将文本转语音的工具,高考复习的绝佳助手

高考倒计时10天&#xff01; 在这最后的冲刺阶段&#xff0c;同学们都在拼命刷题&#xff0c;但面对已经整理好的知识点&#xff0c;时间紧迫&#xff0c;如何高效复习呢&#xff1f; 别急&#xff0c;今天我要和大家分享一个绝佳的复习方法——文字转语音。这个方法可以让你…

鸿蒙4.2小版本推出,鸿蒙5.0已经不远了

上个月&#xff0c;市场上迎来了华为鸿蒙系统4字开头的小升级&#xff0c;版本来到了4.2版本。 我们先来看看4.2版本都给用户带来哪些特色&#xff1a; 界面切换更流畅&#xff1a;无论是响应速度还是操作手感&#xff0c;用户都将感受到更加迅速和顺滑的体验 搜星速度的显著…

人类和小鼠转录组上游分析

基础软件 conda install cutadapt, trimmomatic, samtools, hisat2, subread, deeptools -y人类转录组上游分析 # 样本名称 sample_namesample# 线程 threads4# 双端测序原始fastq1和fastq2路径 fastq1_path/path/${sample_name}_1.fq.gz fastq2_path/path/${sample_name}_2.…

【LeetCode】【9】回文数(1047字)

文章目录 [toc]题目描述样例输入输出与解释样例1样例2样例3 提示进阶Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;LeetCode 刷题指南&#xff1a;LeetCode刷题指南 题目描述 给一个整数x&#xff0c;如果x是一个回文整数&#xff0c;返回true&#xff1b;否…

CATIA二次开发VBA入门(2)——认识CATIA内置的VBA开发界面

目录 引出VBA界面介绍工具条介绍view下拉菜单insert下拉format下拉debug下拉tool下拉help下拉【重要】补充窗口窗体的模态【重要】 总结认识CATIA二次开发刘瑞欣 vb程序设计教程Excel中的vba开发catia中的vba开发 宏的录制、回放和编辑宏代码精简画圆柱阵列宏Macro文件的3种类型…

mysql内存和磁盘的关系

mysql内存和磁盘的关系 1.MySQL的内存和磁盘之间的关系是密切的。MySQL的数据存储在磁盘上&#xff0c;但为了高效地执行查询操作&#xff0c;它也会将数据页&#xff08;每个页通常为16KB&#xff09;读入内存。MySQL的缓冲池&#xff08;buffer pool&#xff09;是在内存中的…

数据通信基本概念汇总

1. 数据通信基础 网关: 提供协议转换&#xff0c;路由选择&#xff0c;数据交换的网络设备 报文: 网络中所传递的一个数据单元。 数据载荷: 最终要传递的信息 封装: 给数据载荷添加头部和尾部的过程(形成新的报文) 解封装: 给数据载荷去掉头部和尾部的过程(获取数据载荷) 终端设…

JAVA语言开发的一套云HIS医院管理系统源码 HIS源码 医院管理系统源码 HIS系统是否可以与LIS和RIS进行集成

系统概述 云HIS系统是一种基于云计算的医疗卫生信息系统&#xff0c;它运用云计算、大数据、物联网等新兴信息技术&#xff0c;按照现代医疗卫生管理要求&#xff0c;在一定区域范围内以数字化形式提供医疗卫生行业数据收集、存储、传递、处理的业务和技术平台。云HIS的主要功…

最简单的方式解决android studio 模拟器无法联网的问题

最简单的方式解决android studio 模拟器无法联网的问题 看了网上很多解决android studio内置模拟器无法联网的问题&#xff0c;基本上都是在模拟器手机上配置dns&#xff0c;个人试了多种办法也连不上网&#xff0c;现在给出一种&#xff0c;仅需要在命令行操作的解决安卓模拟…

在线预定酒店预约订房小程序源码系统 可轻松预定房间+随时取消预约 前后端分离 带网站的安装代码包以及搭建教程

系统概述 旅游业的快速发展和人们生活水平的不断提高&#xff0c;越来越多的人选择出行旅游。而在旅行过程中&#xff0c;酒店预订是一个必不可少的环节。传统的酒店预订方式往往需要用户通过电话、网站等渠道进行预订&#xff0c;流程繁琐&#xff0c;效率低下。为了解决这些…

Android15 Beta更新速览

Android15 Beta更新速览 前台服务变更 前台服务使应用保持活动状态&#xff0c;以便它们可以执行关键且对用户可见的操作&#xff0c;通常以牺牲电池寿命为代价。在 Android 15 Beta 2 中&#xff0c;dataSync 和 mediaProcessing 前台服务类型现在具有约 6 小时的超时时间&a…

【机器学习】探究DQN通过训练来解决AI序列决策问题

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

Linux一键安装Docker、kkfileviewer

Linux一键安装Docker、kkfileviewer 一、安装docker 安装docker脚本 vi initDocker.sh脚本内容 #安装前先更新yum&#xff0c;防止连接镜像失败 yum -y update#卸载系统之前的docker&#xff08;可选择&#xff0c;我这里直接注释了&#xff09; #yum remove docker docker…

Geotools--生成等值线

好久没用geotools去写东西了&#xff0c;因为近几年一直在接触所谓数字孪生和可视化相关项目&#xff0c;个人的重心也往前端可视化去倾斜&#xff0c;在后端的开发上到变得停滞下来。 这次用的是geotools 28.4版本&#xff0c;生成等值线的方法在 <dependency><group…

html+CSS部分基础运用7

项目1 设计简易灯箱画廊 1.实验所需素材 在trees文件夹中提供一个MP3文件和18个JPG文件&#xff0c;设计页面时可以使用。 2.编程实现简易灯箱画廊&#xff0c;鼠标单击任一个图像超链接&#xff0c;在底部浮动框架中显示大图像&#xff0c;效果如图4-1所示的页面。 图4-1 简…

中创算力与中国移动初步达成战略合作意向,共同构建智能生态圈!

2024年5月14日&#xff0c;为进一步深化合作&#xff0c;促进业务共同发展&#xff0c;实现双方优势互补。中国移动云能力中心高级专家、郑州移动总经理助理邵根波、管城分公司政企部经理张文孟、航海东路分局张旭红莅临中创算力。中创董事长许伟威、副总经理杨光、技术总监刘朝…

Spring+SpringBoot面试总结(近两万字)

SpringSpringBoot面试总结 一、Spring Bean1.1、bean的生命周期&#xff08;对象的创建使用销毁&#xff09;1.1.1、准备工作1.1.2、创建Bean对象1.1.3、注册销毁 1.2、 bean的作用域1.2.1、配置方式 1.3、 spring 自动装配 bean 有哪些方式&#xff08;存疑存疑&#xff09;1.…

AI绘画Stable Diffusion【艺术写真】:蒙版法图生图,局部重绘实现AI艺术写真

大家好&#xff0c;我是设计师阿威 之前我分享过几篇使用SD插件换脸方式实现AI写真的教程&#xff0c;主要存在2个大的问题。 &#xff08;1&#xff09;人脸相似度 &#xff08;2&#xff09;生成的图片整体色调有时候会比较怪异 对于上面的问题&#xff0c;在对图片质量要…

条款9:利用destructors避免泄露资源

对指针说拜拜。承认吧&#xff0c;你从未真正喜欢过它&#xff0c;对不&#xff1f; 好&#xff0c;你不需要对所有指针说拜拜&#xff0c;但是你真的得对那些用来操控局部性资源(local resources&#xff09;的指针说莎唷娜拉了。 举个例子&#xff0c;你正在为“小动物收养…