44. UE5 RPG 初始化敌人的属性

在正常的游戏中,我们应该考虑如何去初始化角色属性,并且要给角色分好类型。比如,在我们游戏中,我们如何去初始化小兵的属性,并且还要实现小兵随着等级的增长而增加属性。而且就是小兵也有类型的区分,比如我们将在后面设置小兵分为三种:战士,游侠,法师。而小兵的属性实现,一般需要策划进行配表实现,将小兵的属性统一性的存入到表格导入项目。
我们将使用枚举去定义敌人的类型并创建一个ECharacterClass,并创建一个DataAsset命名为UCharacterClassInfo用来设置每种类型使用的属性初始化的GE。对于随着等级变动的基础属性,我们将使用曲线表格去生成所有等级的数据。并且,在数据中,我们去设置敌人需要附带的技能。
所以,接下来,我们实现的步骤是:

  1. 创建一个枚举ECharacterClass来定义角色的种类
  2. 创建一个DataAsset类 UCharacterClassInfo用来设置角色所使用得数据
  3. 创建曲线表格,用来定义角色属性跟随等级变化的值
  4. 创建设置角色属性的GE
  5. 通过函数实现角色应用设置的数据。

创建DataAsset

首先,我们创建角色使用的DataAsset,打开UE创建一个新类,继承DataAsset,并将其命名为CharacterClassInfo
在这里插入图片描述
在项目中,首先创建我们第一步需要的枚举,这里创建的三个职业的枚举

//角色职业类型的枚举
UENUM(BlueprintType)
enum class ECharacterClass : uint8
{Elementalist, //法师Warrior, //战士Ranger //游侠
};

接着创建一个结构体,用于根据不同的职业选择不同的结构体,这是只添加了第一项,就是基础的属性设置,后面我们会增加更多的内容。

USTRUCT()
struct FCharacterClassDefaultInfo
{GENERATED_BODY()UPROPERTY(EditDefaultsOnly, Category="Class Defaults")TSubclassOf<UGameplayEffect> PrimaryAttributes;
};

有了枚举和角色初始化的结构体以后,我们就可以创建DataAsset类了,在数据类里,我们首先创建一个Map类型,用于创建不同职业对应的基础属性GE,然后对于SecondaryAttributes和VitalAttributes,我们共用一套。最后增加一个函数,用于通过职业类型去获取基础的属性GE。

/*** 根据职业选择初始化角色的数据*/
UCLASS()
class AURA_API UCharacterClassInfo : public UDataAsset
{GENERATED_BODY()public:UPROPERTY(EditDefaultsOnly, Category="Class Defaults")TMap<ECharacterClass, FCharacterClassDefaultInfo> CharacterClassInformation;UPROPERTY(EditDefaultsOnly, Category="Common Class Defaults")TSubclassOf<UGameplayEffect> SecondaryAttributes;UPROPERTY(EditDefaultsOnly, Category="Common Class Defaults")TSubclassOf<UGameplayEffect> VitalAttributes;//通过枚举获取对应的初始化类FCharacterClassDefaultInfo GetClassDefaultInfo(ECharacterClass CharacterClass);
};

函数的视线,就是通过Map返回对应的类型的GE。

FCharacterClassDefaultInfo UCharacterClassInfo::GetClassDefaultInfo(ECharacterClass CharacterClass)
{return CharacterClassInformation.FindChecked(CharacterClass);
}

创建完成后,我们编译打开UE,在里面创建一个可配置的DataAsset
在这里插入图片描述
选择我们前面创建的类
在这里插入图片描述
打开以后,就是需要我们填充的值,这里有个细节,就是如果你得枚举第一项就选择枚举默认值,那么后面将无法添加,因为Map类型的Key不允许相同,所以你可以先增加后面的枚举类型,然后再最后设置默认枚举值。
在这里插入图片描述

创建对应的GE

创建了数据以后,我们需要创建对应的GE文件去填充,但是GE里面的数据需要根据等级去变动,所以,我们需要通过数据表格去实现这个功能。
首先我们创建对应的GE,之前,我们创建了一套主角使用的GE,主要属性是设置的固定值,虽然后续我们不会这么使用,刚好,直接使用它复制三个对应三个职业使用,然后创建数据图表,让GE根据等级去数据图表中获取数据。
在这里插入图片描述
然后是它的SecondaryAttributes和VitalAttribues,我们在主角上使用的次级属性设置的GE是Infinite类型的,复制一个修改成Instant类型的。因为敌人在我们的游戏设计中,它们是不会升级也不会变动的,所以不需要实时监听数值变化。而VitalAttributes则直接共用一套就行,毕竟就是最后在生成将血量和蓝量填充到最满,不需要额外制作。
在这里插入图片描述

创建GE使用的数据表格

为了能实现敌人的初始属性,我们将创建曲线表格来实现角色等级增长带来的属性数值的变化。我们在实现时,只要给敌人设置不同的等级,它就会生成不同的属性的敌人。
在这里,我们选择使用曲线表格,曲线表格的好处是可以自动填充中间的数据,并且可以过渡的更好。
在这里插入图片描述
创建曲线表格时,它会提示我们创建什么类型的曲线,
Constant就是没有过渡,到了位置显示固定值,一般不会使用
Linear就是线性过渡
Cubic就是贝塞尔曲线,它会圆滑的过渡,这里我们选择Cubic
在这里插入图片描述
曲线表格推荐使用CT开头,比如我们创建的法师的类型的就叫做CT_PrimaryAttributes_Elementalist
在这里插入图片描述
打开曲线表格,我们将四个属性都添加进去,然后选择曲线编辑
在这里插入图片描述

选中一项属性,然后鼠标右键添加关键帧
在这里插入图片描述
可以在上面添加关键帧的等级和数值,然后点击缩放匹配
在这里插入图片描述
添加完成数值后,我们可以右键点击自动
在这里插入图片描述
它就可以自动平滑处理
在这里插入图片描述
在这种情况下,你没必要每个等级都设置,并且可以看到对应等级的值
在这里插入图片描述
填充完数据之后,我们将对应职业的属性设置上,在选择使用表格后,前面的数值将是得到的值的倍率,然后选择表格中的曲线,即可实现对应的效果。
在这里插入图片描述
后面我们将使用另外两种格式创建另外两个职业的数值,我们不知道它的格式是怎么样的,可以通过将当前创建的曲线表格导出CSV或者JSON格式
在这里插入图片描述
CSV文件打开就是表格的形式,我们可以以此为模版进行修改,但是CSV格式导入到UE无法进行平滑处理
在这里插入图片描述
CSV格式也可以通过文本打开
在这里插入图片描述

JSON格式就是标准的JSON字符串显示,我们可以通过修改KEY和Value实现值的配置。
在这里插入图片描述

从外部文件导入表格数据

上面我们查看了对应的格式,我们可以以此为模版修改数据,并且直接导回到UE内。
导入到UE分为两种形式,第一种是直接将文件导入,我们可以在内容浏览器选择导入,直接将所需的文件导入到UE内
在这里插入图片描述
选择文件后,需要选择数据表格式和数据表插值类型。
在这里插入图片描述
导入后,如果选的是Cubic,那可以实现光滑取消的效果
在这里插入图片描述
如果你是先创建的文件,然后重新导入
在这里插入图片描述
曲线就会变成线性插值
在这里插入图片描述

使用数据表格设置GE

上面我们实现了使用外部数据创建GE使用的数据表格,这里再仔细讲一下如何使用数据表格设置GE。在你设置属性时,后面有一个使用曲线表格,
在这里插入图片描述
选择我们所使用的表格
在这里插入图片描述
表格里面能够存储多个数据,所以,我们还需要指定使用哪一条曲线
在这里插入图片描述
现在的设置的浮点数将是对从数据表格中获取到的结果的缩放,比如在等级0时,返回5 * 1为最终返回的力量值。
在这里插入图片描述
接下来,我们将所有的属性都设置上去,三个职业对应三个GE。
在这里插入图片描述

实现通过设置的通用GE实例化敌人

创建完成对应的GE,我们并使用数据表格填充了对应的数据,接下来,我们将通过代码实现对应的逻辑去初始化角色。
首先,我们考虑将创建数据放在哪里,这里我们将其放到GameMode上面,对于GameMode来说,在不同场景可以设置不同的GameMode,刚好合适,比如你创建了一个关卡,可以设置上对应的数据,关卡内的敌人初始化时,就使用这一套GE去初始化。
在我们的GameMode基类上增加一个变量,用于设置数据

public:UPROPERTY(EditDefaultsOnly, Category="Character Class Defaults")TObjectPtr<UCharacterClassInfo> CharacterClassInfo;

接下来,就是如何使用这个数据,由于它是属于通用的函数,我们将应用的函数创建到函数库里面,之前刚好有个自定义的函数库,我们在其内部增加对应的实现函数
在函数库增加一个函数,用于初始角色属性

	//初始化角色的属性UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|CharacterClassDefaults")static void InitializeDefaultAttributes(const UObject* WorldContextObject, ECharacterClass CharacterClass, float Level, UAbilitySystemComponent* ASC);

在函数实现中,首先获取到关卡的GameMode,如果类型不一致,获取到的是空指针,将不执行后续操作

	//获取到当前关卡的GameMode实例const AMyGameModeBase* GameMode = Cast<AMyGameModeBase>(UGameplayStatics::GetGameMode(WorldContextObject));if(GameMode == nullptr) return;

然后获取到我们配置的DataAsset

	//从实例获取到关卡角色的配置UCharacterClassInfo* ClassInfo = GameMode->CharacterClassInfo;

从DataAsset获取到配置的对应职业的数据

	//获取到默认的基础角色数据const FCharacterClassDefaultInfo ClassDefaultInfo = ClassInfo->GetClassDefaultInfo(CharacterClass);

接下来就是三连应用,和角色一致,先应用主要属性,这个会通过等级设置的不同的属性值,然后根据基础属性生成次级属性的值,最后根据属性填充血量和蓝量的值。

	//应用基础属性FGameplayEffectContextHandle PrimaryContextHandle = ASC->MakeEffectContext();PrimaryContextHandle.AddSourceObject(WorldContextObject);const FGameplayEffectSpecHandle PrimarySpecHandle = ASC->MakeOutgoingSpec(ClassDefaultInfo.PrimaryAttributes, Level, PrimaryContextHandle);ASC->ApplyGameplayEffectSpecToSelf(*PrimarySpecHandle.Data.Get());//设置次级属性FGameplayEffectContextHandle SecondaryContextHandle = ASC->MakeEffectContext();SecondaryContextHandle.AddSourceObject(WorldContextObject);const FGameplayEffectSpecHandle SecondarySpecHandle = ASC->MakeOutgoingSpec(ClassInfo->SecondaryAttributes, Level, SecondaryContextHandle);ASC->ApplyGameplayEffectSpecToSelf(*SecondarySpecHandle.Data.Get());//填充血量和蓝量FGameplayEffectContextHandle VitalContextHandle = ASC->MakeEffectContext();VitalContextHandle.AddSourceObject(WorldContextObject);const FGameplayEffectSpecHandle VitalSpecHandle = ASC->MakeOutgoingSpec(ClassInfo->VitalAttributes, Level, VitalContextHandle);ASC->ApplyGameplayEffectSpecToSelf(*VitalSpecHandle.Data.Get());

初始化角色属性的函数完成了,接下来,我们要在敌人的基类里面实现使用此函数。
首先在敌人的基类身上增加一个变量,用来设置它的职业

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Character Class Defaults")ECharacterClass CharacterClass = ECharacterClass::Warrior;

在敌人基础的角色基础类里,有个函数叫InitializeDefaultAttributes,它是通过在角色的蓝图上面设置的三个GE进行初始化角色,也是当前的主角在使用的方式。
在这里插入图片描述
但是,在敌人这里,我们不需要在角色身上设置对应的GE,而是在GameMode的配置的数据里获取设置,所以,我们将其设置为虚拟函数

	virtual void InitializeDefaultAttributes() const;

在实现这里,只需要调用函数库实现的InitializeDefaultAttributes函数,即可实现对应的效果。

void AEnemyBase::InitAbilityActorInfo()
{AbilitySystemComponent->InitAbilityActorInfo(this, this);Cast<UAbilitySystemComponentBase>(AbilitySystemComponent)->AbilityActorInfoSet();//通过GE初始角色的属性InitializeDefaultAttributes();
}void AEnemyBase::InitializeDefaultAttributes() const
{UMyAbilitySystemBlueprintLibrary::InitializeDefaultAttributes(this, CharacterClass, Level, AbilitySystemComponent);
}

编写完成,编译代码打开UE,首先进入GameMode,将我们之前编写的数据设置上去
在这里插入图片描述
然后选择场景中的敌人,设置它的等级和职业
在这里插入图片描述

测试结果

配置完成,现在角色的设置是否成功需要我们去测试结果,我们有多种方式去测试。
第一种,打印数据,在应用完GE后,打印角色的属性值来查看

void AEnemyBase::InitAbilityActorInfo()
{AbilitySystemComponent->InitAbilityActorInfo(this, this);Cast<UAbilitySystemComponentBase>(AbilitySystemComponent)->AbilityActorInfoSet();//通过GE初始角色的属性InitializeDefaultAttributes();//打印生命值查看属性UE_LOG(LogTemp, Warning, TEXT("%s 的生命值为 %f"), *this->GetName(), Cast<UAttributeSetBase>(AttributeSet)->GetHealth());
}

在输出日志里,可以查看到打印的数值
在这里插入图片描述

另一种方法就是打断点,我们将断点打在应用完GE后
在这里插入图片描述
刚好可以查看它的属性值的值是否正确
在这里插入图片描述

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

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

相关文章

PhpAdmin-getshell

PhpAdmin-getshell 通过未授权成功写入&#xff0c;然后getshell 路径&#xff1a;C:\phpstudy_pro\Extensions\MySQL5.7.26\ 写入木马&#xff1a; into写入文件&#xff1a; 使用需看要secure_file_priv的值。 当value为“null”时&#xff0c;不允许读取任意文件 当value为…

Android 文件传输

经常写adb命令传文件&#xff0c;结果发现Android studio有自带的文件管理器&#xff0c;可以上传下载文件。

高扬程消防水泵在火灾中的关键作用/恒峰智慧科技

在火灾这一无情的灾难面前&#xff0c;每一秒都至关重要。而在这一分一秒的较量中&#xff0c;高扬程消防水泵无疑扮演着举足轻重的角色。它不仅是灭火战斗的得力助手&#xff0c;更是保障人民生命财产安全的守护神。 高扬程消防水泵&#xff0c;顾名思义&#xff0c;其扬程远超…

通过自然语言处理执行特定任务的AI Agents;大模型控制NPC执行一系列的动作;个人化的电子邮件助手Panza

✨ 1: OpenAgents 通过自然语言处理执行特定任务的AI代理 OpenAgents是一个开放平台&#xff0c;旨在使语言代理&#xff08;即通过自然语言处理执行特定任务的AI代理&#xff09;的使用和托管变得更加便捷和实用。它特别适合于日常生活中对数据分析、工具插件获取和网络浏览…

vue2编写主体页面

目录 一. 导入两张图片 二. 新建主体vue 三. 修改路由 1. 新增主体界面Main.vue的路由 2. 完整router/index.js代码如下: 在Vue 2中编写一个主体页面通常意味着创建一个包含导航栏、侧边栏、内容区域等的布局。以下是使用Vue 2和Element UI框架来构建一个简单的主体页面的…

2024年第二十一届 五一杯 (B题)大学生数学建模挑战赛 | 最大流问题,深度学习分析 | 数学建模完整代码解析

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享&#xff0c;与你一起了解前沿科技知识&#xff01; 本次DeepVisionary带来的是五一杯的详细解读&#xff1a; 完整内容可以在文章末尾全文免费领取&阅读&#xff01; 第一个问题…

张大哥笔记:学什么都不如学赚钱

很多人总是这样认为&#xff1a;好好读书&#xff0c;考上好学校&#xff0c;将来可以找到一份不错的工作&#xff0c;这样的思想观念&#xff0c;可能会导致你一辈子都无法实现财富自由。 财富的多少&#xff0c;和你的努力程度没有直接关系。我们可以清楚看到那些每天辛苦劳动…

Leetcode—2739. 总行驶距离【简单】

2024每日刷题&#xff08;121&#xff09; Leetcode—2739. 总行驶距离 实现代码 class Solution { public:int distanceTraveled(int mainTank, int additionalTank) {int consume 0;int ans 0;while(mainTank ! 0) {mainTank--;consume;if(consume 5 && additio…

【20】JAVASE-网络编程【从零开始学JAVA】

Java零基础系列课程-JavaSE基础篇 Lecture&#xff1a;波哥 Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台。…

python u是什么意思

u&#xff1a;表示unicode字符串&#xff0c;默认模式&#xff0c;里边的特殊字符会被识别。 作用&#xff1a;后面字符串以unicode格式进行编码&#xff0c;一般用在中文字符串前面&#xff0c;防止因为源码储存格式问题&#xff0c;导致再次使用时出现乱码。 用法&#xff…

人脸识别 人脸识别insightFace项目使用详解

人脸识别 人脸识别insightFace项目使用详解 recognition人脸识别模型Arcface(mxnet)训练项目地址 recognition人脸识别模型 注意:该模块是有深度学习框架mxnet实现,为了加速训练,需要GPU支持, Arcface(mxnet)训练 1、安装gpu版的MXNet,我的cuda版本是10.2 pip in…

【精选文献】JAG|基于时序Sentinel-1 SAR影像小农耕作区烟草空间分布制图

目录 文章简介 01 文章摘要 02 研究背景、目标及创新点 03 研究区域与数据集 04 研究方法 05 研究结果 06 研究讨论 07 研究结论 08 文章引用 文章简介 论文名称&#xff1a;Mapping tobacco planting areas in smallholder farmlands using Phenological-Spatial-Te…

《深入解析WIndows操作系统》第9章读书笔记

1、闪存类型&#xff1a;常见的闪存类型有NOR和NAND。NOR闪存在操作上最接近RAM&#xff0c;它的每个字节都可以被独立地寻址&#xff0c;而NAND闪存则被组织成以块为单位&#xff0c;就像磁盘一样。NOR类型的闪存用来设计保存计算机主板上的BIOS&#xff0c;而NAND类型的闪存被…

笔记本上打造专属的LLama3聊天机器人

1. 引言 万众期待的 Meta 第三代 Llama 发布了&#xff0c;我想确保你知道如何以最佳方式部署这个最先进的LLM。在本教程中&#xff0c;我们将在笔记本上部署该模型&#xff0c;并指导大家一步步具体操作步骤。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. LLama3 …

数据结构算法——链表带环问题——数学深度解析

前言:本节内容主要是讲解链表的两个问题 &#xff1a;1、判断链表是否带环&#xff1b; 2、一个链表有环&#xff0c; 找到环的入口点。 本节内容适合正在学习链表或者链表基础薄弱的友友们哦。 我们先将问题抛出来&#xff0c;友友们可以自己去力扣或者牛客网去找相应题目&…

【源码解析】深入Pandas的心脏DataFrame 含十大功能、源码实现与编程知识点

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

微信私域生态下的企业级私域建设:开源AI智能名片商城小程序源码六大模块功能价值深度解读

在数字化营销蓬勃发展的今天&#xff0c;企业如何在微信私域生态中构建并运营一个稳固的私域流量池&#xff0c;成为了摆在众多企业家和市场人面前的重要课题。本文将基于开源AI智能名片B2B2C商城小程序源码的AARRR模型&#xff0c;深度解读微信私域中企业级私域建设的六大模块…

文心一言 VS 讯飞星火 VS chatgpt (249)-- 算法导论18.2 2题

二、请解释在什么情况下&#xff08;如果有的话&#xff09;&#xff0c;在调用 B-TREE-INSERT 的过程中&#xff0c;会执行冗余的 DISK-READ 或 DISK-WRITE 操作。&#xff08;所谓冗余的 DISK-READ &#xff0c;是指对已经在主存中的某页做 DISK-READ 。冗余的 DISK-WRITE 是…

【C语言/数据结构】经典链表OJ习题~第二期——链中寻环

&#x1f388;&#x1f388;&#x1f388;欢迎采访小残风的博客主页&#xff1a;残风也想永存-CSDN博客&#x1f388;&#x1f388;&#x1f388; &#x1f388;&#x1f388;&#x1f388;本人码云 链接&#xff1a;残风也想永存 (FSRMWK) - Gitee.com&#x1f388;&#x1f…

第3篇:创建Nios II工程之Hello_World<二>

Q&#xff1a;上一期介绍完基本设计流程和实验原理&#xff0c;接着我们完成系统硬件设计部分&#xff0c;包括Platform Designer系统及Quartus工程。 A&#xff1a;依次搜索并添加Nios II Processor、JTAG UART、On-Chip Memory和System ID IP组件&#xff0c;连接各组件并As…