51. UE5 RPG 自定义FGameplayEffectContext

我们期望能够通过FGameplayEffectContext将此次技能造成的伤害是否触发格挡和暴击的参数传递到AttributeSet中,所以需要实现自定义一个FGameplayEffectContext类,来增加对应的配置。

创建自定义类文件

首先在Public目录上右键,选择添加一个文件
在这里插入图片描述
创建一个.h文件
在这里插入图片描述
然后在Private目录添加一个cpp文件
在这里插入图片描述
然后,我们在蓝图中定义结构体,并继承FGameplayEffectContext

#pragma once //预处理指令 确保这个头文件只被包含(include)一次,防止重复定义。#include "GameplayEffectTypes.h"
#include "RPGAbilityTypes.generated.h"USTRUCT(BlueprintType) //在蓝图中可作为类型使用
struct FRPGGameplayEffectContext : public FGameplayEffectContext
{GENERATED_BODY() //宏 自动生成构造函数、析构函数、拷贝构造函数等public:protected:};

我们要增加两个参数,用于设置或者获取当前技能是否暴击或者格挡

protected:UPROPERTY()bool bIsBlockedHit = false; //格挡UPROPERTY()bool BIsCriticalHit = false; //暴击

然后增加他的获取和设置函数

public:bool IsBlockedHit() const { return bIsBlockedHit; }bool IsCriticalHit() const { return BIsCriticalHit; }void SetIsBlockedHit(const bool bInIsBlockedHit) { bIsBlockedHit = bInIsBlockedHit; }void SetIsCriticalHit(const bool bInIsCriticalHit) { BIsCriticalHit = bInIsCriticalHit; }

要实现子类,我们有一些需要覆写父类的一些函数,这些函数主要用于序列化,序列化的目的是为了将数据转换为二进制数据用于网络通信,因为最终我们需要将数据提交到服务器端,客户端向服务器端提交数据无法直接传递,所以需要序列化为二进制数据,然后将二进制数据提交给服务器端进行反序列化后使用。

首先覆写用于用于返回用于序列化的实际结构体函数。序列化是将对象的状态转换为可以存储或传输的形式的过程,反序列化则是相反的过程。在游戏开发和网络通信中,序列化和反序列化是非常重要的。这个函数在父类里面注释必须要在子类里面覆写。
在这里插入图片描述

/** 返回用于序列化的实际结构体 */
virtual UScriptStruct* GetScriptStruct() const override
{return FGameplayEffectContext::StaticStruct();
}

接下来,我们还需要覆写用于序列化配置的函数,将对应的参数保存,这样,在服务器端,可以完整的复原这个实例。
在这里插入图片描述
FArchive& Ar 是Unreal Engine中用于序列化和反序列化对象的类,它支持以二进制的形式进行加载保存和垃圾回收。
class UPackageMap* Map 用于查找或记录对象间的引用关系
bool& bOutSuccess 输出是否序列化成功

/** 用于序列化类的参数 */
virtual bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) override;

接下来,我们将覆写这个函数的实现,因为内部很多实现需要我们手动去实现。

二进制运算

首先我们看一下源码里的实现,它教给我们了如何去实现序列化,里面有一些二进制的运算

bool FGameplayEffectContext::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{uint8 RepBits = 0;if (Ar.IsSaving()) //判断当前是否在保存数据{//保存数据时,如果有对应的配置项数据,那么将对应位的值设置为1if (bReplicateInstigator && Instigator.IsValid()){RepBits |= 1 << 0;}if (bReplicateEffectCauser && EffectCauser.IsValid() ){RepBits |= 1 << 1;}if (AbilityCDO.IsValid()){RepBits |= 1 << 2;}if (bReplicateSourceObject && SourceObject.IsValid()){RepBits |= 1 << 3;}if (Actors.Num() > 0){RepBits |= 1 << 4;}if (HitResult.IsValid()){RepBits |= 1 << 5;}if (bHasWorldOrigin){RepBits |= 1 << 6;}}//序列化RepBitsAr.SerializeBits(&RepBits, 7);//如果对应位的值为1,那么将数据存入Arif (RepBits & (1 << 0)){Ar << Instigator;}if (RepBits & (1 << 1)){Ar << EffectCauser;}if (RepBits & (1 << 2)){Ar << AbilityCDO;}if (RepBits & (1 << 3)){Ar << SourceObject;}if (RepBits & (1 << 4)){SafeNetSerializeTArray_Default<31>(Ar, Actors);}if (RepBits & (1 << 5)){if (Ar.IsLoading()){if (!HitResult.IsValid()){HitResult = TSharedPtr<FHitResult>(new FHitResult());}}HitResult->NetSerialize(Ar, Map, bOutSuccess);}if (RepBits & (1 << 6)){Ar << WorldOrigin;bHasWorldOrigin = true;}else{bHasWorldOrigin = false;}//如果是加载数据(即反序列化)时,需要调用对ASC进行初始化if (Ar.IsLoading()){AddInstigator(Instigator.Get(), EffectCauser.Get()); // Just to initialize InstigatorAbilitySystemComponent}	bOutSuccess = true;return true;
}

刚开始看的时候确实有点懵,比如这个RepBits |= 1 << 0; RepBits & (1 << 0)还有Ar << Instigator;,接下来,我们就分析一下这些内容是什么。
RepBits |= 1 << 0; RepBits & (1 << 0)属于二进制运算,首先我们先将它们分开理解。
|=在正常运算中见过很多,*= /=这种,它的意思也一样
RepBits |= 1 << 0; 可以转换成 RepBits = RepBits | 1 << 0;
|&是二进制的运算符
unit8 相当于8位二进制无符号8位整数类型 0000 0000
unit32 相当于32位二进制无符号8位整数类型 0000 0000 0000 0000 0000 0000
为了方便分析,我们后面使用八位的分析
1 << 0中间使用了左移操作符,相当于1向左位移0位 结果为 0000 0001
如果是1 << 3相当于1向左位移3位 结果为0000 1000

那么再说|&二进制的运算符,它们都是位的运算符,对应的位置的运算
| 是两个二进制,如果同一个位置有一个值为1,那么返回的值就是1 举例:
1000 1000 | 1000 0001 的结果为1000 1001
&则是同一个位置,两个值都为1,返回的结果对应的位置的值才为1,举例:
1000 1000 & 1000 0001 的结果为1000 0000

所以,现在再看上面的代码,RepBits |= 1 << 0;这些意思就是如果条件成为,将把参数对应位的值设置为1,而RepBits & (1 << 0)则是判断条件,就是为了判断对应位置的值是否为1,如果返回的结果不是0000 0000,条件则会成立。

先对而言,Ar << Instigator;就简单了很多,它是一个简写,它用于序列化或反序列化对象。这里的 <<不是一个标准的C++流插入操作符,而是在Unreal Engine的序列化系统中有特殊含义。
当执行 Ar << Instigator; 时,FArchive& Ar类会调用Instigator对象的序列化方法。Instigator的当前状态会被写入到Ar所代表的存储介质中,根据右侧的类型<<也会切换对应类型的方法去处理,并且它还可以通过判断Ar.IsSaving()去序列化或者反序列化。

实现自定义的NetSerialize

首先,在函数中,我们将RepBits 设置成32位整型,因为之前的8位已经不足以存下我们的内容。

bool FRPGGameplayEffectContext::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
{uint32 RepBits = 0;

然后,在if (Ar.IsSaving())判断后,在底部加上我们的新增的属性

	if (Ar.IsSaving()){if (bReplicateInstigator && Instigator.IsValid()){RepBits |= 1 << 0;}if (bReplicateEffectCauser && EffectCauser.IsValid() ){RepBits |= 1 << 1;}if (AbilityCDO.IsValid()){RepBits |= 1 << 2;}if (bReplicateSourceObject && SourceObject.IsValid()){RepBits |= 1 << 3;}if (Actors.Num() > 0){RepBits |= 1 << 4;}if (HitResult.IsValid()){RepBits |= 1 << 5;}if (bHasWorldOrigin){RepBits |= 1 << 6;}//自定义内容,增加暴击和格挡触发存储if(bIsBlockedHit){RepBits |= 1 << 7;}if(BIsCriticalHit){RepBits |= 1 << 8;}}

接着在序列长度这里,由于增加了两项,所以将7修改为9

//使用了多少长度,就将长度设置为多少
Ar.SerializeBits(&RepBits, 9);

然后接着将源码中序列或反序列的逻辑代码复制过来

if (RepBits & (1 << 0)){Ar << Instigator;}if (RepBits & (1 << 1)){Ar << EffectCauser;}if (RepBits & (1 << 2)){Ar << AbilityCDO;}if (RepBits & (1 << 3)){Ar << SourceObject;}if (RepBits & (1 << 4)){SafeNetSerializeTArray_Default<31>(Ar, Actors);}if (RepBits & (1 << 5)){if (Ar.IsLoading()){if (!HitResult.IsValid()){HitResult = TSharedPtr<FHitResult>(new FHitResult());}}HitResult->NetSerialize(Ar, Map, bOutSuccess);}if (RepBits & (1 << 6)){Ar << WorldOrigin;bHasWorldOrigin = true;}else{bHasWorldOrigin = false;}

然后在后面添加我们新增的两项

	//新增对暴击格挡的序列化或反序列化处理if (RepBits & (1 << 7)){Ar << bIsBlockedHit;}if (RepBits & (1 << 8)){Ar << BIsCriticalHit;}

将加载时初始化ASC的逻辑复制过来

	if (Ar.IsLoading()){AddInstigator(Instigator.Get(), EffectCauser.Get()); // Just to initialize InstigatorAbilitySystemComponent}

最后,将Success设置为true,完成设置

	bOutSuccess = true;return true;

以下为完整代码,我们实现了对应的网络序列化工作
RPGAbilityTypes.h

#pragma once //预处理指令 确保这个头文件只被包含(include)一次,防止重复定义。#include "GameplayEffectTypes.h"
#include "RPGAbilityTypes.generated.h"USTRUCT(BlueprintType) //在蓝图中可作为类型使用
struct FRPGGameplayEffectContext : public FGameplayEffectContext
{GENERATED_BODY() //宏 自动生成构造函数、析构函数、拷贝构造函数等public:bool IsBlockedHit() const { return bIsBlockedHit; }bool IsCriticalHit() const { return BIsCriticalHit; }void SetIsBlockedHit(const bool bInIsBlockedHit) { bIsBlockedHit = bInIsBlockedHit; }void SetIsCriticalHit(const bool bInIsCriticalHit) { BIsCriticalHit = bInIsCriticalHit; }/** 返回用于序列化的实际结构体 */virtual UScriptStruct* GetScriptStruct() const override{return FGameplayEffectContext::StaticStruct();}/** 用于序列化类的参数 */virtual bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) override;
protected:UPROPERTY()bool bIsBlockedHit = false; //格挡UPROPERTY()bool BIsCriticalHit = false; //暴击
};

RPGAbilityTypes.cpp

#include "RPGAbilityTypes.h"bool FRPGGameplayEffectContext::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
{uint32 RepBits = 0;if (Ar.IsSaving()){if (bReplicateInstigator && Instigator.IsValid()){RepBits |= 1 << 0;}if (bReplicateEffectCauser && EffectCauser.IsValid() ){RepBits |= 1 << 1;}if (AbilityCDO.IsValid()){RepBits |= 1 << 2;}if (bReplicateSourceObject && SourceObject.IsValid()){RepBits |= 1 << 3;}if (Actors.Num() > 0){RepBits |= 1 << 4;}if (HitResult.IsValid()){RepBits |= 1 << 5;}if (bHasWorldOrigin){RepBits |= 1 << 6;}//自定义内容,增加暴击和格挡触发存储if(bIsBlockedHit){RepBits |= 1 << 7;}if(BIsCriticalHit){RepBits |= 1 << 8;}}//使用了多少长度,就将长度设置为多少Ar.SerializeBits(&RepBits, 9);if (RepBits & (1 << 0)){Ar << Instigator;}if (RepBits & (1 << 1)){Ar << EffectCauser;}if (RepBits & (1 << 2)){Ar << AbilityCDO;}if (RepBits & (1 << 3)){Ar << SourceObject;}if (RepBits & (1 << 4)){SafeNetSerializeTArray_Default<31>(Ar, Actors);}if (RepBits & (1 << 5)){if (Ar.IsLoading()){if (!HitResult.IsValid()){HitResult = TSharedPtr<FHitResult>(new FHitResult());}}HitResult->NetSerialize(Ar, Map, bOutSuccess);}if (RepBits & (1 << 6)){Ar << WorldOrigin;bHasWorldOrigin = true;}else{bHasWorldOrigin = false;}//新增对暴击格挡的序列化或反序列化处理if (RepBits & (1 << 7)){Ar << bIsBlockedHit;}if (RepBits & (1 << 8)){Ar << BIsCriticalHit;}if (Ar.IsLoading()){AddInstigator(Instigator.Get(), EffectCauser.Get()); // Just to initialize InstigatorAbilitySystemComponent}	bOutSuccess = true;return true;
}

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

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

相关文章

【Flutter】极光推送配置流程(VIVO/OPPO/荣耀厂商通道) 章三

相关文章 推送配置共三篇(如下链接) 【Flutter】极光推送配置流程(极光通道/华为厂商/IOS) 章一 【Flutter】极光推送配置流程(小米厂商通道) 章二 【Flutter】极光推送配置流程(VIVO/OPPO/荣耀厂商通道) 章三 前言 很高兴大家来看小编写的文章&#xff5e;&#xff5e; 继【…

NSSCTF中的web学习(md5())

目录 MD5的学习 [BJDCTF 2020]easy_md5 [LitCTF 2023]Follow me and hack me [LitCTF 2023]Ping [SWPUCTF 2021 新生赛]easyupload3.0 [NSSCTF 2022 Spring Recruit]babyphp MD5的学习 md5()函数&#xff1a; md5($a)&#xff1a;返回a字符串的散列值 md5($a,TRUE)&…

使用XxlCrawler抓取全球航空公司ICAO三字码

目录 前言 一、数据源介绍 1、目标网站 2、页面渲染结构 二、XxlCrawler信息获取 1、创建XxlCrawler对象 2、定义PageVo对象 3、直接PageVO解析 4、自定义解析 总结 前言 长距离旅行或者出差&#xff0c;飞机一定是出行的必备方式。对于旅行达人或者出差人员而言&…

MySQL、JDBC复盘及规划

数据库仍有习题尚未做完&#xff0c;策略从一天做完改为每天5到10题&#xff0c;以此达到掌握和复习的效果&#xff0c;JDBC的六部仍需每天练习&#xff0c;从明天开始正式进行JavaWeb的学习&#xff0c;预计持续到七月中旬&#xff0c;还会完成一个书城项目&#xff0c;六月底…

API低代码平台介绍3-异构数据源的数据查询功能

异构数据源的数据查询功能 在上一篇文章中我们通过API平台定义了一个最基本的数据查询接口&#xff0c;本篇文章我们将上升难度&#xff0c;在原有接口的基础上&#xff0c;实现在MySQL数据库和Oracle数据库同时进行数据查询。   什么场景会需要同时对异构数据源进行查询&…

【C++】string类的使用③(修改器Modifiers || 非成员函数重载Non-member function overloads)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f525;修改器&#xff08;Modifiers&#xff09;**operator**appendpush_back和pop_backassigninserterasereplaceswap &#x1f525;非成员函数重载&#xff…

二叉树专题(有关二叉树的相关学习)

二叉树 1.数概念及结构 1.1树的结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结…

springcloud简单了解及上手

springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…

【simulink】Scrambling 加扰

https://ww2.mathworks.cn/help/comm/ug/additive-scrambling-of-input-data-in-simulink.html 草图 simulink 代码图

web入门练手案例(二)

下面是一下web入门案例和实现的代码&#xff0c;带有部分注释&#xff0c;倘若代码中有任何问题或疑问&#xff0c;欢迎留言交流~ 数字变色Logo 案例描述 “Logo”是“商标”的英文说法&#xff0c;是企业最基本的视觉识别形象&#xff0c;通过商标的推广可以让消费者了解企…

号外!IP SSL证书申请只需十分钟!

IP SSL证书是一种专为IP地址设计的SSL证书&#xff0c;它使得基于IP地址的网站或服务能够实现HTTPS加密&#xff0c;确保数据在传输过程中的安全性和完整性。以下是关于IP SSL证书的一些技术性要点和申请流程概述&#xff1a; 一、IP SSL证书技术要点 1、适用场景&#xff1a…

ASP.NET银行大厅自助信息系统的开发与实现

摘 要 本毕业设计在基于银行业务大厅现有业务的基础上&#xff0c;针对自助银行的概念和其独有特点&#xff0c;通过.NETSQL技术&#xff0c;开发一个简单的银行大厅自助信息系统&#xff0c;完成一些自助银行的业务需求如帐户信息查询、帐户挂失、自助交费、留言、新闻查询…

【HDFS】关于HDFS-17497:在commit block时更新quota

链接:https://github.com/apache/hadoop/pull/6765 Ticket标题:The number of bytes of the last committed block should be calculated into the file length。 HDFS里,一个在写入的文件可能包含多个commited状态的块。 但是计算文件大小的时候,最后一个commited block并…

菲律宾签证照片尺寸要求,用手机生成

菲律宾签证照片尺寸要求如下图所示&#xff0c;可以用手机在微信搜索随时照小程序&#xff0c;快速生成哦。

C语言:指针(3)

1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* ; 本质是把字符串 hello bit. ⾸字符的地址放到了pstr中。上⾯代码的意思是把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。 2. 数组指针变量 2.1 数组指针变量是什么&#xff1f; 答案…

Shuffle Cards (STL rope平衡树库)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例1&#xff1a; 输入 5 1 2 3 输出 2 3 4 1 5 样例2&#xff1a; 输入 5 2 2 3 2 3 输出 3 4 1 2 5 样例3&#xff1a; 输入 5 3 2 3 1 4 2 4输出 3 4 1 5 2 思路&#xff1a; 这道题&…

Element-UI 快速入门指南

文章目录 一、安装 Element-UI1.1 使用 npm 安装1.2 使用 yarn 安装 二、引入 Element-UI三、使用 Element-UI 组件3.1 按钮组件3.2 输入框组件3.3 表单组件3.4 表格组件3.5 弹框组件 四、自定义主题4.1 安装主题工具4.2 初始化变量文件4.3 编译主题 五、总结 &#x1f389;欢迎…

网络编程套接字(一) 【简单的Udp网络程序】

网络编程套接字<一> 理解源端口号和目的端口号PORT VS PID认识TCP协议和UDP协议网络字节序socket编程接口sockaddr结构简单的UDP网络程序服务端创建套接字服务端绑定运行服务器客户端创建套接字关于客户端的绑定问题启动客户端启动客户端本地测试INADDR_ANY 理解源端口号…

Nginx解决跨域问题

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 W3C标准&#xff1…

Flutter实战记录-协作开发遇到的问题

一.前言 Android项目使用了混合架构&#xff0c;部分模块使用Flutter进行开发。在电脑A上开发的项目提交到git仓库&#xff0c;电脑B拉取后进行操作&#xff0c;遇到两个问题&#xff0c;特此做一下记录&#xff1b; 二.问题A Settings file ‘D:\xxx\settings.gradle’ line…