UE学习日志#14 GAS--ASC源码简要分析10 GC相关
注:1.这个分类是按照源码里的注释分类的
2.本篇是通读并给出一些注释形式的,并不涉及结构性的分析
3.看之前要对UE的GAS系统的定义有初步了解
4.因为都是接口函数,有些没细看的研究那一部分的时候会细看
1 一些接口函数,但是注释说不要直接调用要通过GameplayCueManager调用
// Do not call these functions directly, call the wrappers on GameplayCueManager insteadUFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted_FromSpec(const FGameplayEffectSpecForRPC Spec, FPredictionKey PredictionKey) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesExecuted(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesExecuted_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAdded(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAdded_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters Parameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAddedAndWhileActive_FromSpec(const FGameplayEffectSpecForRPC& Spec, FPredictionKey PredictionKey) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAddedAndWhileActive_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesAddedAndWhileActive_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;
2 ExecuteGameplayCue相关
注释翻译:GameplayCues也可以独立出现,这些函数接受一个可选的效果上下文,用于传递命中结果等信息
一个是传入GEContextHandle版本的,一个是传入FGameplayCueParameters版本的
声明如下:
/** GameplayCues can also come on their own. These take an optional effect context to pass through hit result, etc */void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
实现就是调用GameplayCueManager里的函数:
void UAbilitySystemComponent::ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext)
{// Send to the wrapper on the cue managerUAbilitySystemGlobals::Get().GetGameplayCueManager()->InvokeGameplayCueExecuted(this, GameplayCueTag, ScopedPredictionKey, EffectContext);
}void UAbilitySystemComponent::ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters)
{// Send to the wrapper on the cue managerUAbilitySystemGlobals::Get().GetGameplayCueManager()->InvokeGameplayCueExecuted_WithParams(this, GameplayCueTag, ScopedPredictionKey, GameplayCueParameters);
}
先大致看下实现可以发现逻辑都一样,都是先检查有效性,再构造FGameplayCuePendingExecute PendingCue,再调用AddPendingCueExecuteInternal,唯一不一样的地方就是初始化PendingCue.CueParameters这个参数的方式不一样,WithParams版本的很简单,就是直接赋值,下面去找下InitGameplayCueParameters这个函数,里边重点就这一行:
CueParameters.EffectContext = EffectContext;
这里贴的实现:
void UGameplayCueManager::InvokeGameplayCueExecuted(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext)
{if (EnableSuppressCuesOnGameplayCueManager && OwningComponent && OwningComponent->bSuppressGameplayCues){return;}if (OwningComponent){FGameplayCuePendingExecute PendingCue;PendingCue.PayloadType = EGameplayCuePayloadType::CueParameters;PendingCue.GameplayCueTags.Add(GameplayCueTag);PendingCue.OwningComponent = OwningComponent;UAbilitySystemGlobals::Get().InitGameplayCueParameters(PendingCue.CueParameters, EffectContext);PendingCue.PredictionKey = PredictionKey;AddPendingCueExecuteInternal(PendingCue);}
}void UGameplayCueManager::InvokeGameplayCueExecuted_WithParams(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters)
{if (EnableSuppressCuesOnGameplayCueManager && OwningComponent && OwningComponent->bSuppressGameplayCues){return;}if (OwningComponent){FGameplayCuePendingExecute PendingCue;PendingCue.PayloadType = EGameplayCuePayloadType::CueParameters;PendingCue.GameplayCueTags.Add(GameplayCueTag);PendingCue.OwningComponent = OwningComponent;PendingCue.CueParameters = GameplayCueParameters;PendingCue.PredictionKey = PredictionKey;AddPendingCueExecuteInternal(PendingCue);}
}
顺着思路看AddPendingCueExecuteInternal:
发现就是把他加入了执行队列,但是没有真正执行
void UGameplayCueManager::AddPendingCueExecuteInternal(FGameplayCuePendingExecute& PendingCue)
{if (ProcessPendingCueExecute(PendingCue)){PendingExecuteCues.Add(PendingCue);}if (GameplayCueSendContextCount == 0){// Not in a context, flush nowFlushPendingCues();}
}
再去看FlushPendingCues这个函数:
函数体太长了,核心就是调用这两个函数:
诶惊奇的发现就是前面第一部分里不让你直接调用的接口函数,找了一圈RepInterface也是调用接口函数,所以最后除了各个函数的条件判断不太一样,都是调用的ASC中的InvokeGameplayCueEvent
RepInterface->Call_InvokeGameplayCueExecuted_WithParams
PendingCue.OwningComponent->InvokeGameplayCueEvent
这里的RepInterface:
IAbilitySystemReplicationProxyInterface* RepInterface = PendingCue.OwningComponent->GetReplicationInterface();
再顺着看Invoke这个函数,发现都调用GameplayCueManager的HandleGameplayCue
UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue
GC中的HandleGameplayCue会先将标签翻译,然后路由,路由中的具体处理:
CueSet和Interface调用HandleGameplayCue,其中的GameplayCueInterface:
Cast<IGameplayCueInterface>(TargetActor)
// Give the global set a chanceif (bAcceptsCue && !(Options & EGameplayCueExecutionOptions::IgnoreNotifies)){RuntimeGameplayCueObjectLibrary.CueSet->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);}// Use the interface even if it's not in the mapif (GameplayCueInterface && bAcceptsCue){GameplayCueInterface->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);}
Interface中的Handle会处理函数列表,最后转到默认处理函数,子类可以实现
而CueSet中调用这个函数:
UGameplayCueSet::HandleGameplayCueNotify_Internal
会分UGameplayCueNotify_Static和AGameplayCueNotify_Actor处理具体逻辑,到这就各种信息检索完了,进入具体的处理逻辑,之后就要进入这两个类里看了,这里就不继续看了
3 AddGameplayCue
/** Add a persistent gameplay cue */void AddGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());void AddGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
套娃到这里:
这个函数大体逻辑:
-
检查是否是服务器
-
检查是否已经存在该提示:避免重复添加
-
强制网络同步:确保客户端能够接收到最新的游戏玩法提示
-
添加GC到容器
-
处理混合复制模式:根据复制模式调整预测Key
-
调用RPC播放激活事件:通过RPC将游戏玩法提示同步到客户端
-
触发服务器端事件:在服务器端触发
WhileActive
事件 -
客户端预测逻辑:在客户端预测性地添加GC,并触发事件
void UAbilitySystemComponent::AddGameplayCue_Internal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters, FActiveGameplayCueContainer& GameplayCueContainer)
{if (IsOwnerActorAuthoritative()){const bool bWasInList = GameplayCueContainer.HasCue(GameplayCueTag);ForceReplication();GameplayCueContainer.AddCue(GameplayCueTag, ScopedPredictionKey, GameplayCueParameters);// For mixed minimal replication mode, we do NOT want the owning client to play the OnActive event through this RPC, since it will get the full replicated // GE in its AGE array. Generate a server-side prediction key for it, which it will look for on the _Implementation function and ignore. (<--- Original Hack){FPredictionKey PredictionKeyForRPC = ScopedPredictionKey; // Key we send for RPC. Start with the regular old ScopedPredictionKey// Special stuff for mixed replication modeif (ReplicationMode == EGameplayEffectReplicationMode::Mixed){if (GameplayCueContainer.bMinimalReplication){// For *replicated to sim proxies only* container, Create a Server Initiated PK to avoid double playing on the auto proxy in mixed replication mode (Original Hack)PredictionKeyForRPC = FPredictionKey::CreateNewServerInitiatedKey(this);}else{// For "replicated to everyone" cue container, we need to clear server replicated prediction keys, or else they will trip the same absorption code that we added for the first hack above.// Its ok to just throw out a server replicated prediction key because (outside of mixed replication mode) it will not affect what the client does in NetMulticast_InvokeGameplayCueAdded_WithParams_Implementation// (E.g, the client only skips the InvokeCall if the key is locally generated, not for server generated ones anways)if (ScopedPredictionKey.IsServerInitiatedKey()){PredictionKeyForRPC = FPredictionKey();}}}// Finally, call the RPC to play the OnActive eventif (IAbilitySystemReplicationProxyInterface* ReplicationInterface = GetReplicationInterface()){ReplicationInterface->Call_InvokeGameplayCueAdded_WithParams(GameplayCueTag, PredictionKeyForRPC, GameplayCueParameters);}}if (!bWasInList){// Call on server here, clients get it from repnotifyInvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::WhileActive, GameplayCueParameters);}}else if (ScopedPredictionKey.IsLocalClientKey()){GameplayCueContainer.PredictiveAdd(GameplayCueTag, ScopedPredictionKey);// Allow for predictive gameplaycue events? Needs more thoughtInvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::OnActive, GameplayCueParameters);InvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::WhileActive, GameplayCueParameters);}
}
4 RemoveGameplayCue
/** Remove a persistent gameplay cue */void RemoveGameplayCue(const FGameplayTag GameplayCueTag);
去到了Container里处理Remove,这里就不深入了,具体研究GC会继续看
void UAbilitySystemComponent::RemoveGameplayCue_Internal(const FGameplayTag GameplayCueTag, FActiveGameplayCueContainer& GameplayCueContainer)
{if (IsOwnerActorAuthoritative()){GameplayCueContainer.RemoveCue(GameplayCueTag);}else if (ScopedPredictionKey.IsLocalClientKey()){GameplayCueContainer.PredictiveRemove(GameplayCueTag);}
}
总结:1.以上就是几个关键的函数,但都是偏向的都是调用的整体逻辑,没有深入具体的实现,留坑()
2.按着我看的顺序来的,并非实际调用顺序
相关文章:
UE学习日志#14 GAS--ASC源码简要分析10 GC相关
注:1.这个分类是按照源码里的注释分类的 2.本篇是通读并给出一些注释形式的,并不涉及结构性的分析 3.看之前要对UE的GAS系统的定义有初步了解 4.因为都是接口函数,有些没细看的研究那一部分的时候会细看 1 一些接口函数,但是…...

使用Python和Qt6创建GUI应用程序--关于Qt的一点介绍
关于Qt的一点介绍 Qt是一个免费的开源部件工具包,用于创建跨平台GUI应用程序,允许应用程序从Windows瞄准多个平台,macOS, Linux和Android的单一代码库。但是Qt不仅仅是一个Widget工具箱和功能内置支持多媒体,数据库&am…...
C#@符号在string.Format方法中作用
本文详解@符号在string.Format方法中作用。...
Next.js 14 TS 中使用jwt 和 App Router 进行管理
jwt是一个很基础的工作。但是因为架构不一样,就算是相同的架构,版本不一样,加jwt都会有一定的差别。现在我们的项目是Next.js 14 TS 的 App Router项目(就是没有pages那种),添加jwt的步骤: 1、…...
【贪心算法】洛谷P1090 合并果子 / [USACO06NOV] Fence Repair G
2025 - 01 - 21 - 第 45 篇 【洛谷】贪心算法题单 -【 贪心算法】 - 【学习笔记】 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G 【贪心算法】 文章目录 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06…...

Windows11无法打开Windows安全中心主界面
# 问题描述 安全中心无法打卡主界面,并弹出“需要使用新应用以打开此windowsdefender连接”. 解决方法 以管理员权限打开PowerShell,推荐使用快捷键win x打开快捷界面,选择Windows终端(管理员),并在终…...
下载arm架构的deb包的方法
在ARM板上操作 如果你是在arm板上使用apt安装和下载包,那么安装过的包会在以下路径里: /var/cache/apt/archives只需要复制出来就可以 如果只下载不安装,可以使用命令 sudo apt-get -d install package_name:arm64 # 如果是32位࿰…...
【Day29 LeetCode】动态规划DP
一、动态规划DP 1、不同路径 62 首先是dp数组,dp[i][j]表示从起点(0, 0)到达当前位置(i, j)的路径数,转移方程从只能向下和向右移动可知,初始化边界可直观推出第一行和第一列上的位置只有一条路径。 class Solution { public:int uniquePa…...

5分钟带你获取deepseek api并搭建简易问答应用
目录 1、获取api 2、获取base_url和chat_model 3、配置模型参数 方法一:终端中临时将加入 方法二:创建.env文件 4、 配置client 5、利用deepseek大模型实现简易问答 deepseek-v3是截止博文撰写之日,无论是国内还是国际上发布的大模型中…...
LeetCode题练习与总结:最短无序连续子数组--581
一、题目描述 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。 请你找出符合题意的 最短 子数组,并输出它的长度。 示例 1: 输入:num…...
探秘 TCP TLP:从背景到实现
回家的路上还讨论了个关于 TCP TLP 的问题,闲着无事缕一缕。本文内容参考自 Tail Loss Probe (TLP): An Algorithm for Fast Recovery of Tail Losses 以及 Linux 内核源码。 TLP,先说缘由。自 TCP 引入 Fast retrans 机制就是为了尽力避免 RTO…...

linux学习之网络编程
一、两个模型及其对应关系 OSI七层模型 TCP/IP 四层模型 -------------------------------------------------------------------------- 应用层 表示层 ----> …...
scrol家族 offset家族 client家族学习
Scroll 系列属性 scrollTop & scrollLeft scrollTop: 返回元素的内容已向上滚动的部分的高度。scrollLeft: 返回元素的内容已向左滚动的部分的宽度。 scrollHeight & scrollWidth scrollHeight: 返回元素的实际高度,包括由于溢出而在屏幕上不可见的内容…...
css-background-color(transparent)
1.前言 在 CSS 中,background-color 属性用于设置元素的背景颜色。除了基本的颜色值(如 red、blue 等)和十六进制颜色值(如 #FF0000、#0000FF 等),还有一些特殊的属性值可以用来设置背景颜色。 2.backgrou…...

如何将xps文件转换为txt文件?xps转为pdf,pdf转为txt,提取pdf表格并转为txt
文章目录 xps转txt方法一方法二 pdf转txt整页转txt提取pdf表格,并转为txt 总结另外参考XPS文件转换为TXT文件XPS文件转换为PDF文件PDF文件转换为TXT文件提取PDF表格并转为TXT示例代码(部分) 本文测试代码已上传,路径如下ÿ…...

【Samba】Ubuntu20.04 Windows 共享文件夹
【Samba】Ubuntu20.04 Windows 共享文件夹 前言整体思路检查 Ubuntu 端 和 Windows 网络通信是否正常创建共享文件夹安装并配置 Samba 服务器安装 Samba 服务器创建 Samba 用户编辑 Samba 配置文件重启 Samba 服务器 在 Windows 端 访问 Ubuntu 的共享文件夹 前言 本文基于 Ub…...
gradle和maven的区别以及怎么选择使用它们
目录 区别 1. 配置方式 2. 依赖管理 3. 构建性能 4. 灵活性和扩展性 5. 多项目构建 如何选择使用 选择 Maven 的场景 选择 Gradle 的场景 区别 1. 配置方式 Maven: 使用基于 XML 的 pom.xml 文件进行配置。所有的项目信息、依赖管理、构建插件等都在这个文…...
360大数据面试题及参考答案
数据清理有哪些方法? 数据清理是指发现并纠正数据文件中可识别的错误,包括检查数据一致性,处理无效值和缺失值等。常见的数据清理方法有以下几种: 去重处理:数据中可能存在重复的记录,这不仅会占用存储空间,还可能影响分析结果。通过对比每条记录的关键属性,若所有关键…...
Myeclipse最新版本 C1 2019.4.0
Myeclipse C1 2019.4.0下载地址:链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发,使用这个工具即可。有提示功能,有自动纠错功能。 ★集成开发环境可以让软件开…...
MySQL 9.2.0 的功能
MySQL 9.2.0 的功能 MySQL 9.2.0 的功能新增、弃用和删除内容如下: 新增功能 权限新增12:引入了CREATE_SPATIAL_REFERENCE_SYSTEM权限,拥有该权限的用户可执行CREATE SPATIAL REFERENCE SYSTEM、CREATE OR REPLACE SPATIAL REFERENCE SYSTEM…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究
摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...
P10909 [蓝桥杯 2024 国 B] 立定跳远
# P10909 [蓝桥杯 2024 国 B] 立定跳远 ## 题目描述 在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 $n$ 个检查点 $a_1, a_2, \cdots , a_n$ 且 $a_i \ge a_{i−1} > 0$。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时࿰…...

华硕电脑,全新的超频方式,无需进入BIOS
想要追求更佳性能释放 或探索更多可玩性的小伙伴, 可能会需要为你的电脑超频。 但我们常用的不论是BIOS里的超频, 还是Armoury Crate奥创智控中心超频, 每次调节都要重启,有点麻烦。 TurboV Core 全新的超频方案来了 4不规…...