52. UE5 RPG 应用自定义FGameplayEffectContext到项目

在前面一篇文章中,我们创建了自定义的FGameplayEffectContext结构体,用于存储所需的内容。在自定义的结构体内,我们主要是为了增加暴击和格挡两个参数,用于后面的UI显示给玩家,让玩家知道当前触发的状态。并且我们还对齐做了序列化处理,能够在后续处理中,数据能够成功传递到服务器端展示。
在这一篇中,我们将实现如何将自定义的FGameplayEffectContext应用到我们的代码逻辑中,替换默认的FGameplayEffectContext。

模版特化

模板参数在某种特定类型下的具体实现称为模板的特化。
在GameplayEffectTypes.h文件中,有这么一段代码
它是针对于FGameplayEffectContext进行了模版特化处理。当你在项目中使用到了对应的类型结构体时,模版特化修改的内容也将起作用。

template<>
struct TStructOpsTypeTraits< FGameplayEffectContext > : public TStructOpsTypeTraitsBase2< FGameplayEffectContext >
{enum{WithNetSerializer = true,WithCopy = true		// Necessary so that TSharedPtr<FHitResult> Data is copied around};
};

这段代码是在针对于FGameplayEffectContext 类型时,实现的具体设置,它继承至TStructOpsTypeTraitsBase2,接下来我们看一下TStructOpsTypeTraitsBase2。
TStructOpsTypeTraitsBase2是专门用于针对C++结构体进行模版特化,它作为结构体的默认设置。

/** 用于为自定义的脚本结构体提供类型特性 **/
template <class CPPSTRUCT>
struct TStructOpsTypeTraitsBase2
{enum{WithZeroConstructor            = false,                         // 结构体是否可以通过将其内存占用填充为零来构造为有效对象。WithNoInitConstructor          = false,                         // 结构体是否具有一个构造函数,该构造函数接受一个 EForceInit 参数,用于强制执行初始化,而默认构造函数执行“未初始化”。WithNoDestructor               = false,                         // 当结构体被销毁时,其析构函数是否不会被调用。WithCopy                       = !TIsPODType<CPPSTRUCT>::Value, // 结构体是否可以通过其复制赋值操作符进行复制。WithIdenticalViaEquality       = false,                         // 结构体是否可以通过其 operator== 进行比较。这与 WithIdentical 应该是互斥的。WithIdentical                  = false,                         // 结构体是否可以通过一个 Identical(const T* Other, uint32 PortFlags) 函数进行比较。这与 WithIdenticalViaEquality 应该是互斥的。WithExportTextItem             = false,                         // 结构体是否具有一个 ExportTextItem 函数,用于将其状态序列化为字符串。WithImportTextItem             = false,                         // s结构体是否具有一个 ImportTextItem 函数,用于从字符串反序列化对象。WithAddStructReferencedObjects = false,                         // 结构体是否具有一个 AddStructReferencedObjects 函数,允许它向垃圾收集器添加引用。WithSerializer                 = false,                         // 结构体是否具有一个 Serialize 函数,用于将其状态序列化为 FArchiveWithStructuredSerializer       = false,                         // 结构体是否具有一个 Serialize 函数,用于将其状态序列化为 FStructuredArchive。WithPostSerialize              = false,                         // 结构体是否具有一个在序列化后被调用的 PostSerialize 函数。WithNetSerializer              = false,                         // 结构体是否具有一个 NetSerialize 函数,用于将状态序列化为用于网络复制的 FArchive。WithNetDeltaSerializer         = false,                         // 结构体是否具有一个 NetDeltaSerialize 函数,用于序列化与先前 NetSerialize 操作中的状态差异。WithSerializeFromMismatchedTag = false,                         // 结构体是否具有一个 SerializeFromMismatchedTag 函数,用于从其他属性标签进行转换。WithStructuredSerializeFromMismatchedTag = false,               // 结构体是否具有一个基于 FStructuredArchive 的 SerializeFromMismatchedTag 函数,用于从其他属性标签进行转换。WithPostScriptConstruct        = false,                         //  结构体是否具有一个在蓝图中构造后被调用的 PostScriptConstruct 函数。WithNetSharedSerialization     = false,                         // 结构体的 NetSerialize 函数是否不需要包映射来序列化其状态。WithGetPreloadDependencies     = false,                         // 结构体是否具有一个 GetPreloadDependencies 函数,用于在加载时序列化结构体时返回所有将被 Preload() 的对象。WithPureVirtual                = false,                         //  结构体是否具有 PURE_VIRTUAL 函数,并且在 CHECK_PUREVIRTUALS 为true时无法构造。WithFindInnerPropertyInstance  = false,							// 结构体是否具有一个 FindInnerPropertyInstance 函数,该函数可以在给定属性 FName 时提供一个 FProperty 和数据指针。WithCanEditChange			   = false,							// 结构体是否具有一个仅在编辑器中使用的 CanEditChange 函数,该函数可以有条件地使子属性在详细信息面板中变为只读(与 UObject::CanEditChange 相同的想法)。};static constexpr EPropertyObjectReferenceType WithSerializerObjectReferences = EPropertyObjectReferenceType::Conservative; // 当结构体(或类)的 Serialize 方法遇到这些类型的对象引用时,可能会进行序列化。默认情况下,使用 Conservative(保守)策略,意味着如果对象引用的处理方式未知,那么对象引用收集器(可能是负责序列化和反序列化的组件)应该序列化这个结构体。
};

所以,我们自定义的也需要一个模版特化进行处理,基于FGameplayEffectContext我们自定义个即可。
在这里,我们将网络序列化和可复制设置为true,但是对于Hit Result这种复杂类型来说,它只会复制引用。

template<>
struct TStructOpsTypeTraits< FRPGGameplayEffectContext > : public TStructOpsTypeTraitsBase2< FRPGGameplayEffectContext >
{enum{WithNetSerializer = true,WithCopy = true		// Necessary so that TSharedPtr<FHitResult> Data is copied around};
};

所以,我们还需要在结构体内实现复制函数,对Hit Result进行深拷贝,这里直接复制父类的复制函数修改即可。

	/** 创建一个副本,用于后续网络复制或者后续修改 */virtual FRPGGameplayEffectContext* Duplicate() const override{FRPGGameplayEffectContext* NewContext = new FRPGGameplayEffectContext();*NewContext = *this; //WithCopy 设置为true,就可以通过赋值操作进行拷贝if (GetHitResult()){// 深拷贝 hit resultNewContext->AddHitResult(*GetHitResult(), true);}return NewContext;}

使用自定义的FGameplayEffectContext类

既然要使用自定义的类,那么我们需要找到创建的位置,比如我们当前火球术使用的技能类里面,会去创建它
通过ASC的MakeEffectContext()去创建的
在这里插入图片描述
我们进入此方法查看它是如何它的内部实现
在函数内部,它输出的是FGameplayEffectContextHandle类型,FGameplayEffectContextHandle内部包含FGameplayEffectContext,而FGameplayEffectContext是通过UAbilitySystemGlobals::Get().AllocGameplayEffectContext()实现的创建
在这里插入图片描述
而AllocGameplayEffectContext的实现则是直接new FGameplayEffectContext()了一个新的实例返回。
在这里插入图片描述
看到这里,我们就明白了,我们需要去修改UAbilitySystemGlobals使用,然后实现以后创建FGameplayEffectContext时,都是创建我们自定义的FGameplayEffectContext来实例化。所以,我们我们需要自定义一个AbilitySystemGlobals类,然后覆写这个函数
打开UE,在AbilitySystem目录下面新增一个C++类,选择AbilitySystemGlobals
在这里插入图片描述
命名为RPGAbilitySystemGlobals
在这里插入图片描述
在.h文件中,我们设置覆写AllocGameplayEffectContext函数。

UCLASS()
class AURA_API URPGAbilitySystemGlobals : public UAbilitySystemGlobals
{GENERATED_BODY()virtual FGameplayEffectContext* AllocGameplayEffectContext() const override;
};

在函数实现的cpp文件中,我们返回创建的自定义的FGameplayEffectContext

FGameplayEffectContext* URPGAbilitySystemGlobals::AllocGameplayEffectContext() const
{return new FRPGGameplayEffectContext();
}

接下来就是重要的一点,如何使用自定义的AbilitySystemGlobals,我们需要在配置项内去设置,然后修改了初始化AbilitySystemGlobals的类指向我们自定义的AbilitySystemGlobals类。
在这里插入图片描述
然后编译,在创建FGameplayEffectContext的地方打断点,查看创建的实例里面是否包含我们自定义的属性。并且我还发现我的属性名称写错了,竟然写成了大写,bool类型的第一个字母推荐小写b,我去改一下。
在这里插入图片描述
还有就是在AttributeSet里面是否能够获取到,因为AS是在服务器端运行的,如果能够在AS里面获取到,证明我们真的成功了
在这里插入图片描述

实现设置获取格挡和暴击属性

既然我们实现了自定义FGameplayEffectContext,并且值已经能够获取得到,那么我们需要在获取格挡和暴击的位置去设置布尔值。
在 UGameplayEffectExecutionCalculation中,我们可以获取到GE的Spec,我们通过Spec的函数GetContext获取句柄,并通过句柄的Get获取到Context

FGameplayEffectContext* EffectContext = Spec.GetContext().Get();

可以在句柄的代码中找到获取函数
在这里插入图片描述
接下来,我们将FGameplayEffectContext转换成我们创建的自定义类型,然后在转换这里一定要用static_cast,不然会报错。
static_cast是强制类型转换操作符

FRPGGameplayEffectContext* RPGEffectContext = static_cast<FRPGGameplayEffectContext*>(EffectContext);

如果你使用内置的这种:

FRPGGameplayEffectContext* RPGEffectContext = Cast<FRPGGameplayEffectContext>(EffectContext);

它会编译引发错误
在这里插入图片描述
获取到自定义类型的context的上下文后,我们可以通过调用函数设置格挡

RPGEffectContext->SetIsBlockedHit(bBlocked);

为了方便使用,我们准备将设置和获取方法写入到蓝图函数库中,可以通过直接传入参数获取内容,并且还可以在蓝图中调用。
在蓝图函数库中创建两个静态函数,用于获取暴击和格挡

//获取当前GE是否触发格挡
UFUNCTION(BlueprintPure, Category="MyAbilitySystemLibrary|GameplayEffects")
static bool IsBlockedHit(const FGameplayEffectContextHandle& EffectContextHandle);//获取当前GE是否触发暴击
UFUNCTION(BlueprintPure, Category="MyAbilitySystemLibrary|GameplayEffects")
static bool IsCriticalHit(const FGameplayEffectContextHandle& EffectContextHandle);

然后在实现中,我们需要从Handle中获取Context并转换为我们自定义的格式
我们需要用到static_cast去强制转换类型。

const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get())

然后通过内置的函数去获取是否暴击或者格挡

bool UMyAbilitySystemBlueprintLibrary::IsBlockedHit(const FGameplayEffectContextHandle& EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get())){return RPGEffectContext->IsBlockedHit();}return false;
}bool UMyAbilitySystemBlueprintLibrary::IsCriticalHit(const FGameplayEffectContextHandle& EffectContextHandle)
{if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get())){return RPGEffectContext->IsCriticalHit();}return false;
}

编译在UE的技能蓝图中就可以通过名称去获取对应的值,我们只需要传入对应的Handle,就可以获取到对应的Context的暴击和格挡是否触发。
在这里插入图片描述
接下来,我们还要在函数库实现设置的两个函数,这里有个问题,就是没办法传入常量(前面加const)会出现问题,我们将函数编写完成以后编译在UE里面查看
设置需要传入两个值,一个是修改的Context的Handle,另一个则是bool值,用于设置的值,我们无法设置成静态函数,也就是

UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|GameplayEffects")
static void SetIsBlockHit(FGameplayEffectContextHandle& EffectContextHandle, bool bInIsBlockedHit);UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|GameplayEffects")
static void SetIsCriticalHit(FGameplayEffectContextHandle& EffectContextHandle, bool bInIsCriticalHit);

在实现这里,还是老套路,强制转换为自定义类的实例,然后通过实例方法设置

void UMyAbilitySystemBlueprintLibrary::SetIsBlockHit(FGameplayEffectContextHandle& EffectContextHandle,bool bInIsBlockedHit)
{FRPGGameplayEffectContext* RPGEffectContext = static_cast<FRPGGameplayEffectContext*>(EffectContextHandle.Get());RPGEffectContext->SetIsBlockedHit(bInIsBlockedHit);
}void UMyAbilitySystemBlueprintLibrary::SetIsCriticalHit(FGameplayEffectContextHandle& EffectContextHandle,bool bInIsCriticalHit)
{FRPGGameplayEffectContext* RPGEffectContext = static_cast<FRPGGameplayEffectContext*>(EffectContextHandle.Get());RPGEffectContext->SetIsCriticalHit(bInIsCriticalHit);
}

如果我们不设置const,编译出来Handle会在右边,无法在蓝图中设置Handle。但是设置了const为常量,就无法修改数值
在这里插入图片描述
声明函数时,我们在前面添加UPARAM(ref),即可实现在蓝图节点的左侧

UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|GameplayEffects")
static void SetIsBlockHit(UPARAM(ref) FGameplayEffectContextHandle& EffectContextHandle, bool bInIsBlockedHit);UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|GameplayEffects")
static void SetIsCriticalHit(UPARAM(ref) FGameplayEffectContextHandle& EffectContextHandle, bool bInIsCriticalHit);

在这里插入图片描述

实现在AttributeSet获取

有了我们上面设置的函数库的函数,我们可以很方便的去设置对应的参数,首先我们去计算伤害的地方获取到Handle

	//获取GE的上下文句柄FGameplayEffectContextHandle EffectContextHandle = Spec.GetContext();

然后在计算完成格挡后,将格挡的变量设置,需要传入句柄和布尔

	//设置格挡UMyAbilitySystemBlueprintLibrary::SetIsBlockHit(EffectContextHandle, bBlocked);

同理,设置暴击

	//设置暴击UMyAbilitySystemBlueprintLibrary::SetIsCriticalHit(EffectContextHandle, bCriticalHit);

设置完成后,我们需要在AttributSet类中,将显示UI伤害数字的函数,增加格挡和暴击的参数

	//显示伤害数字static void ShowFloatingText(const FEffectProperties& Props, const float Damage, bool IsBlockedHit, bool IsCriticalHit);

在PostGameplayEffectExecute函数中,我们调用了SetEffectProperties,整理了结构体方便获取数据
在这里插入图片描述
所以我们在设置ShowFloatingText之前,获取到暴击和格挡

//获取格挡和暴击
const bool IsBlockedHit = UMyAbilitySystemBlueprintLibrary::IsBlockedHit(Props.EffectContextHandle);
const bool IsCriticalHit = UMyAbilitySystemBlueprintLibrary::IsCriticalHit(Props.EffectContextHandle);

然后传入设置即可。

//显示伤害数字
ShowFloatingText(Props, LocalIncomingDamage, IsBlockedHit, IsCriticalHit);

这一篇文章就更新到这里,接着,我debug查看一下是否生效。
这里我用测试数值设置格挡率百分百,现在在AttributeSet里面实现了获取值为true。
在这里插入图片描述
在下一篇,我们将更新在格挡和暴击时,UI上面将显示对应的效果。

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

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

相关文章

分布式与一致性协议之PBFT算法(一)

PBFT算法 概述 前面提到了拜占庭将军问题之后&#xff0c;有人可能会感到困惑:口信消息型拜占庭问题直接在实际项目中是如何落地的呢&#xff1f;事实上&#xff0c;它很难在实际项目中落地&#xff0c;因为口信消息型拜占庭问题之解是一个非常理论化的算法&#xff0c;没有与…

区块链数据集(一)Xblock

一、Transaction Datasets Ethereum On-chain Data [Dataset] 2021-10TransactionData/Code AvailableEthereum Introduction: This is the dataset of paper “XBlock-ETH: Extracting and Exploring Blockchain Data From Ethereum”. Data / Code Paper CiteDownloads: …

【Python大数据】PySpark

CSDN不支持多个资源绑定&#xff0c;另外两个数据文件下载&#xff1a; 订单数据-json.zip search-log.zip Apache Spark是用于大规模数据(large-scala data)处理的统一(unified)分析引擎 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服…

openFeign 调用后 返回 出现 application/json 错误

项目场景&#xff1a; 远程调用时返回json格式错误 项目场景&#xff1a;从分页插件式改换为原生分页的时候 通过openFeign调用时发现了问题 问题描述 不需要openFeign 调用的时候 返回的数据和格式是对 通过openFeign 调用后返回 出现 application/json 错误 &#xff1a; …

5万字带你一文看懂自动驾驶之高精度地图前世今生

在讲解高精度地图之前&#xff0c;我们先把定位这个事情弄清楚&#xff0c;想明白&#xff0c;后面的事情就会清晰很多&#xff0c;自古哲学里面讨论的人生终极问题&#xff0c;无非就三个&#xff0c;我是谁&#xff0c;我从哪里来&#xff0c;我要去哪里&#xff0c;这里的位…

构建智慧设施管理平台:数字化引领未来建筑行业发展

随着城市化进程的不断推进和建筑行业的持续发展&#xff0c;智慧设施管理平台的重要性日益凸显。在这一背景下&#xff0c;构建智慧设施管理平台成为推动建筑行业数字化转型的关键举措。本文将深入探讨智慧设施管理平台的构建与优势&#xff0c;助力建筑企业把握数字化转型的主…

头歌实践教学平台——Python程序设计之语言基础

1.1 四则运算(project) 整数四则运算 """ 试编程实现分两行输入两个非零整数&#xff0c;并在4 行中按顺序输出两个数的加、减、乘、除的计算结果。 要求输出与如下示例格式相同&#xff0c;符号前后各有一个空格。 """#输入整数变量a和b&#x…

7-139 有趣的括号

括号()的组合千奇百怪,Drizzle 想知道各种组合的括号可以是否合法 合法要求:每个同类型的左括号必须有与之对应的同类的右括号以正确的顺序闭合 要求: 输入:输入一个括号字符串 输出:输出是否合法,是则True,否则False 示例: 输入: (){}[]输出: True范围: 对于 1…

html+js光标操作

光标设置id为username的字段 window.addEventListener("load", function() {document.getElementById("username").focus(); }); 光标在username的时候点击enter回车键的时候光标移动到id为password的input里面 document.getElementById("username…

【从零开始学架构 架构基础】二 架构设计的复杂度来源:高性能复杂度来源

架构设计的复杂度来源其实就是架构设计要解决的问题&#xff0c;主要有如下几个&#xff1a;高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键&#xff0c;就是新旧技术之间不是完全的替代关系&#xff0c;有交叉&#xff0c;有各自的特点&#xff0c;所以才需要具体…

always on 可用行分析及部署操作

一、Always On 可用性分析 Always On 是 SQL Server 提供的一种高可用性和灾难恢复解决方案。它通过允许数据库在多个 SQL Server 实例之间自动故障转移,从而确保业务的连续性。以下是对 Always On 的可用性分析: 高可用性:Always On 通过创建可用性组,将数据库同步到多个…

openEuler 22.03安装单机版oracle 19c(附录所有patch包)

客户要在OpenEuler 22.0.3 LTS上安装的19.3.0.0 ,在安装到11%的时候报错all_no_orcl错误,我们知道欧拉底层是rhel9,这些错误其实经常接触都知道肯定是各种软件包的版本不对导致的,但是各种依赖太多了也不好解决,最后在官网有所发现: Requirements for Installing Oracle Datab…

Python数据类型转换:打通进销存数据处理的通道!

在Python中&#xff0c;数据类型转换是将一个数据类型的值转换为另一种数据类型的过程。Python提供了丰富的内置函数来实现不同数据类型之间的转换&#xff0c;包括整数、浮点数、字符串、列表、元组、集合和字典等。 基本语法 数据类型转换的基本语法是使用内置函数进行转换…

第八十二章 将 Web 应用程序与远程 Web 服务器结合使用 - 访问多个 IRIS 服务器上的 CSP

文章目录 第八十二章 将 Web 应用程序与远程 Web 服务器结合使用 - 访问多个 IRIS 服务器上的 CSP为应用程序路径配置 IRIS 服务器更改 URL 中的 IRIS 服务器名称 第八十二章 将 Web 应用程序与远程 Web 服务器结合使用 - 访问多个 IRIS 服务器上的 CSP 如果需要配置一台 Web …

【文末附gpt升级方案】探讨当前时机是否适合进入AIGC行业(一)

随着科技的飞速发展&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;作为新兴的技术领域&#xff0c;正逐步走进公众的视野&#xff0c;并在多个行业展现出巨大的应用潜力。然而&#xff0c;对于创业者、投资者以及希望进入这一领域的专业人士来说&#xff0c;当前时…

2024新零售行业多元化用工报告

来源&#xff1a;君润人力 近期历史回顾&#xff1a;

小米15曝光?可能会要稍微涨价

也许是感受到了智能机市场的逐渐复苏&#xff0c;最近各大手机品牌发售新品的速度明显加快了。从4月份的Redmi、一加&#xff0c;再到5月份一大堆vivo、OPPO新机型的发布。而近日&#xff0c;有关小米14即将发售的消息也是悄咪咪的放了出来。 去年发售的小米14可以说是狠狠地让…

202012青少年软件编程(Python)等级考试试卷(三级)

第 1 题 【单选题】 在Python正则表达式中&#xff0c;用来匹配任意空白字符的是&#xff08; &#xff09;。 A &#x1f612; B :S C :d D &#x1f604; 正确答案:A 试题解析: 第 2 题 【单选题】 在Python正则表达式中&#xff0c;用来匹配任意非数字字符的是&…

双向RNN和双向LSTM

双向RNN和双向LSTM 一、双向循环神经网络BiRNN 1、为什么要用BiRNN 双向RNN&#xff0c;即可以从过去的时间点获取记忆&#xff0c;又可以从未来的时间点获取信息,也就是说具有以下两个特点&#xff1a; 捕捉前后文信息&#xff1a;传统的单向 RNN 只能利用先前的上下文信息…

vue3+ts(<script setup lang=“ts“>)刷新页面后保持下拉框选中效果

效果图&#xff1a; 代码&#xff1a; <template><div class"app-layout"><div class"app-box"><div class"header"><div class"header-left"></div><div class"title">室外智…