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;用于调度成百上千的服…

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

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

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

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

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

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

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…

【文末附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">室外智…

scratch列表排序 2024年3月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析

目录 scratch列表排序 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、p…

Python函数之旅专栏(导航)

Python内置函数(参考版本:3.11.8)AELRabs( )enumerate( )len( )range( )aiter( )eval( )list( )repr( )all( )exec( )locals( )reversed( )anext( )round( )any( ) ascii( )FM  filter( )map( )S float( )max( )set( )Bformat( )memoryview( )setattr( )bin( )frozenset( )…

ArcGI基本技巧-科研常用OLS, GWR, GTWR模型实现

ArcGI基本技巧-科研常用OLS, GWR, GTWR模型实现 OLS,GWR,GTWR回归模型均可以揭示解释变量对被解释变量的影响且可以进行预测。Ordinary Least Squares (OLS)是最小二乘法&#xff0c;Geographically Weighted Regression (GWR)是地理加权回归&#xff0c;Geographically and T…

Unity射击游戏开发教程:(18)添加弹药计数+补充弹药

添加简单的弹药计数 我将讨论如何向游戏中添加简单的弹药计数。这将包括在 HUD 中添加弹药计数器,当弹药达到 0 时,文本会将颜色更改为红色以提醒玩家。另外,当弹药数为0时,玩家将无法再射击。让我们深入了解吧! 在播放器脚本中我们需要添加一些变量。我们将创建两个公共整…

详细分析Python中的win32com(附Demo)

目录 前言1. 基本知识2. Excel3. Word 前言 对于自动化RPA比较火热&#xff0c;相应的库也比较多&#xff0c;此文分析win32com这个库&#xff0c;用于操作office 1. 基本知识 Win32com 是一个 Python 模块&#xff0c;是 pywin32 扩展的一部分&#xff0c;允许 Python 代码…

C语言如何删除表中指定位置的结点?

一、问题 如何删除链表中指定位置的结点&#xff1f; 二、解答 删除链表中指定的结点&#xff0c;就像是排好队的⼩朋友⼿牵着⼿&#xff0c;将其中⼀个⼩朋友从队伍中分出来&#xff0c;只需将这个⼩朋友的双⼿从两边松开。 删除结点有两种情况&#xff1a; &#xff08;1&am…

HTML静态网页成品作业(HTML+CSS)——我的家乡江永网页设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…