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
在这里插入图片描述
运行起来你会发现都会追你
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

螺旋矩阵(算法题)

文章目录 螺旋矩阵解题思路 螺旋矩阵 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]解题思路 模…

YOLOv8+PyQt5动物检测系统完整资源集合(yolov8模型,从图像、视频和摄像头三种路径识别检测,包含登陆页面、注册页面和检测页面)

1.猫狗猴检测YOLOv8&#xff08;https://mbd.pub/o/bread/mbd-ZpaTl51u&#xff09;_哔哩哔哩_bilibili 资源包含可视化的动物检测系统&#xff0c;基于最新的YOLOv8训练的动物检测模型&#xff0c;和基于PyQt5制作的可视化动物检测系统&#xff0c;包含登陆页面、注册页面和检…

Java---Cloneable接口---浅克隆和深克隆

在Java中&#xff0c;我们如何实现一个对象的克隆呢&#xff1f; 在Java中实现对象的克隆&#xff0c;我们要用到Cloneable接口。克隆也分为浅克隆和深克隆。 1.实现浅克隆 1.重写clone方法 当我们想直接通过前面已经建立好的对象来调用Object类中的clone方法时&#xff0c;…

摸鱼大数据——Hive表操作——分区表

1、介绍 特点: 分区表会在HDFS上产生目录。查询数据的时候使用分区字段筛选数据&#xff0c;可以避免全表扫描&#xff0c;从而提升查询效率 注意: 如果是分区表&#xff0c;在查询数据的时候&#xff0c;如果没有使用分区字段&#xff0c;它回去进行全表扫描&#xff0c;会降低…

positivessl泛域名证书500元13个月

随着创建网站的门槛变低&#xff0c;不论是个人用户还是企事业单位用户创建的域名网站也越来越多&#xff0c;怎么维护网络环境的安全成为了各个用户需要解决的问题。为了保护网站的数据安全&#xff0c;防止恶意攻击和数据泄露&#xff0c;大多数用户选择为域名网站安装数字证…

Laravel 图片添加水印

和这个配合使用 Laravel ThinkPhP 海报生成_laravel 制作海报-CSDN博客 代码 //水印 $x_length $imageInfo[0]; $y_length $imageInfo[1];$color imagecolorallocatealpha($posterImage, 255, 255, 255, 70); // 增加透明度参数alpha$font_size 40; //字体大小 $angle …

YOLOv10涨点改进:如何魔改注意力进行二次创新,高效替换PSA | NEU-DET为案列进行展开

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进&#xff1a;替换YOLOv10中的PSA进行二次创新&#xff0c;1&#xff09;EMA替换 PSA中的多头自注意力模块MHSA注意力&#xff1b;2&#xff09; EMA直接替换 PSA&#xff1b; 在NEU-DET案列进行可行性验证&#xff0c;1&#x…

WebGIS 智慧城市三维可视化综合管控

智慧城市可视化建设不仅提升了城市管理的科技含量和效率&#xff0c;还促进了城市可持续发展&#xff0c;提升了居民的生活质量。随着技术的不断发展和应用&#xff0c;智慧城市可视化建设将会更加丰富和完善&#xff0c;为城市发展带来更加广阔的前景。 图扑应用自研 HT for W…

Linux一键安装Docker、kkfileviewer

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

mybatis新增到数据库后返回当前ID

描述 在开发中&#xff0c;插入一条数据并返回当前的ID的场景很多 之前用mybatisPlus自带的api非常简单&#xff0c;调用完save or insert之后再getId即可。 今天使用mybatis的时候也遇到了这个场景&#xff0c;在此记录一下。 解决问题 直接再insert标签里面表明属性 核心…

数据挖掘与机器学习——回归分析

目录 回归分析定义&#xff1a; 案例&#xff1a; 线性回归 预备知识&#xff1a; 定义&#xff1a; 一元线性回归&#xff1a; 如何找出最佳的一元线性回归模型&#xff1a; 案例&#xff1a; python实现&#xff1a; 多元线性回归 案例&#xff1a; 线性回归的优缺…

抖店重磅新规!保证金下调,一张营业执照能开多个店铺了!

哈喽~我是月月 抖音平台为助力小商家实现开店低成本&#xff0c;轻负担&#xff0c;高收益的模式 在5月30日正式实施了两个政策&#xff1a;保证金下调&#xff0c;一证多开 政策一&#xff1a;保证金下调 这次政策&#xff0c;涉及的类目优惠包含了&#xff0c;平台近70%的…

假暴力,cf1168B. Good Triple

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1168B - Codeforces 二、解题报告 1、思路分析 一眼没思路&#xff0c;打个暴力试试 因为如果 s[l, r] 是一个好字符串&#xff0c;那么s[i, r]一定也是好字符串&#xff0c;其中i < l 那么…

【Mongo】索引结构

结论 Mongo3.2版本开始&#xff0c;索引的结构默认是B树。 起因 面试的时候&#xff0c;面试官问为什么Mongo DB底层使用B树而不是B树&#xff1f; 面试完赶紧恶补&#xff0c;结果发现面试官好像给我埋了个坑。。。 MongoDB官方描述&#xff1a; 翻译一下就是&#xff1…

Java 类加载机制解密一探到底

类加载是 Java 程序在运行期执行之前的重要环节&#xff0c;它决定着程序的运行效率和稳定性。本文将为您深入剖析 Java 类加载机制的整个生命周期&#xff0c;揭开神秘面纱&#xff0c;让您彻底掌握这一核心知识点。 一、类的生命周期概述 类的生命周期在Java中指的是从类被加…

【全开源】CMS内容管理系统源码(ThinkPHP+FastAdmin)

基于ThinkPHPFastAdmin的CMS内容管理系统&#xff0c;自定义内容模型、自定义单页、自定义表单、专题、统计报表、会员发布等 提供全部前后台无加密源代码和数据库私有化部署&#xff0c;UniAPP版本提供全部无加密UniAPP源码。 ​构建高效内容管理的基石 一、引言&#xff1a…

数据结构—队列(C语言实现)

文章目录 前言一、队列的概念二、队列的实现Queue.hQueue.c 三、设计循环队列问题数组实现链表实现 总结 前言 嗨喽喽&#xff01;&#xff01;小伙伴们&#xff0c;大家好哇&#xff0c;欢迎来到我的博客&#xff01; 今天将要分享的是另一种数据结构—队列&#xff0c;以及…

迈向F5G-A,开启全光万兆新时代——南通移动完成全市首个50G-PON技术验证

近日&#xff0c;南通移动在崇川区完成全市首个50G-PON万兆技术现网验证&#xff0c;标志着南通成为首批具备F5G-A(The 5th GenerationFixed Network-advanced)的万兆光网城市&#xff0c;使其成为网速最快、覆盖最全、时延最低的城市之一。 作为全光万兆的关键技术&#xff0c…

计算机图形学入门01:概述

1.什么是图形学? The use of computers to synthesize and manipulate visual information. 图形学是合成和操纵视觉信息的计算机应用。 百度百科&#xff1a;计算机图形学(Computer Graphics&#xff0c;简称CG)是一种使用数学算法将二维或三维图形转化为计算机显示器的栅格…