UE学习日志#14 GAS--ASC源码简要分析10 GC相关

news2025/1/30 13:17:38

注:1.这个分类是按照源码里的注释分类的

2.本篇是通读并给出一些注释形式的,并不涉及结构性的分析

3.看之前要对UE的GAS系统的定义有初步了解

4.因为都是接口函数,有些没细看的研究那一部分的时候会细看

1  一些接口函数,但是注释说不要直接调用要通过GameplayCueManager调用

// Do not call these functions directly, call the wrappers on GameplayCueManager instead
	UFUNCTION(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 manager
	UAbilitySystemGlobals::Get().GetGameplayCueManager()->InvokeGameplayCueExecuted(this, GameplayCueTag, ScopedPredictionKey, EffectContext);
}

void UAbilitySystemComponent::ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters)
{
	// Send to the wrapper on the cue manager
	UAbilitySystemGlobals::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 now
		FlushPendingCues();
	}
}

 再去看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 chance
	if (bAcceptsCue && !(Options & EGameplayCueExecutionOptions::IgnoreNotifies))
	{
		RuntimeGameplayCueObjectLibrary.CueSet->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);
	}

	// Use the interface even if it's not in the map
	if (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);

套娃到这里:

这个函数大体逻辑:

  1. 检查是否是服务器

  2. 检查是否已经存在该提示:避免重复添加

  3. 强制网络同步:确保客户端能够接收到最新的游戏玩法提示

  4. 添加GC到容器

  5. 处理混合复制模式:根据复制模式调整预测Key

  6. 调用RPC播放激活事件:通过RPC将游戏玩法提示同步到客户端

  7. 触发服务器端事件:在服务器端触发 WhileActive 事件

  8. 客户端预测逻辑:在客户端预测性地添加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 mode
			if (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 event
			if (IAbilitySystemReplicationProxyInterface* ReplicationInterface = GetReplicationInterface())
			{
				ReplicationInterface->Call_InvokeGameplayCueAdded_WithParams(GameplayCueTag, PredictionKeyForRPC, GameplayCueParameters);
			}
		}

		if (!bWasInList)
		{
			// Call on server here, clients get it from repnotify
			InvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::WhileActive, GameplayCueParameters);
		}
	}
	else if (ScopedPredictionKey.IsLocalClientKey())
	{
		GameplayCueContainer.PredictiveAdd(GameplayCueTag, ScopedPredictionKey);

		// Allow for predictive gameplaycue events? Needs more thought
		InvokeGameplayCueEvent(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.按着我看的顺序来的,并非实际调用顺序

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

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

相关文章

游戏引擎介绍:Game Engine

简介 定义&#xff1a;软件框架&#xff0c;一系列为开发游戏的工具的集合 可协作创意生产工具&#xff0c;复杂性艺术&#xff0c;注重realtime实时 目的 为艺术家&#xff0c;设计师&#xff0c;程序员设计工具链 游戏引擎开发参考书 推荐&#xff1a;Game Engine Archite…

[A-29]ARMv8/v9-GIC-中断子系统的安全架构设计(Security/FIQ/IRQ)

ver0.1 前言 打开这篇文章的时候,我们已经为每一个中断信号规划一条路径,在外设和PE-Core之间建立了消息通道,外设有紧急的情况下可以给SOC中的大哥打报告了。下面就把接力棒就交到了CPU手里了,但是PE-Core要交给那个Exception Level以及Security下运行的软件处理呢?本文…

能够对设备的历史数据进行学习与分析,通过与设备当前状态的比对,识别潜在故障并做出预判的名厨亮灶开源了。

明厨亮灶视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。AI技术可以24小时…

Linux进程调度与等待:背后的机制与实现

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 当一个进程发起某种操作&#xff08;如I/O请求、信号、锁的获取等&#xff09;&#xff0c;但该操作需要的资源暂时不可用时&#xff0c;进程会被操作系统挂起&#xff0c;进入“等待队列”或“阻塞状态”。…

寒假1.25

题解 web:[极客大挑战 2019]Upload 打开环境 上传一个一句话木马试试 只能上传图片那就再上传一次&#xff0c;bp抓包修改type-content为image/jpeg试试 不行 看来是文件后缀被绕过了&#xff0c;上传一个.html然后抓包改类型试试 上传成功了&#xff0c;但是提示‘<&…

C++/stack_queue

目录 1.stack 1.1stack的介绍 1.2stack的使用 练习题&#xff1a; 1.3stack的模拟实现 2.queue的介绍和使用 2.1queue的介绍 2.2queue的使用 2.3queue的模拟实现 3.priority_queue的介绍和使用 3.1priority_queue的介绍 3.2priority_queue的使用 欢迎 1.stack 1.1stack…

【Java】微服务找不到问题记录can not find user-service

一、问题描述 运行网关微服务与用户微服务后&#xff0c;nacos服务成功注册 但是测试接口的时候网关没有找到相关服务 二、解决方案 我先检查了pom文件确定没问题后查看配置文件 最后发现是配置里spring.application.namexxx-user里面服务的名字后面多了一个空格 三、总结…

QT:图像上绘制图形

需求描述 1、展示一张图像 2、在图像上可以使用数据绘制图像&#xff1a;矩形、不规则图形、线条 3、有按键可以选择 概要设计 规划布局如下 1、左边是Qlabel 用于展示图片 2、右边是三个按钮 具体实现 1、 首先设计 UI 界面&#xff0c;对控件进行布局 在 mainwindow.u…

基于java线程池和EasyExcel实现数据异步导入

基于java线程池和EasyExcel实现数据异步导入 2.代码实现 2.1 controller层 PostMapping("import")public void importExcel(MultipartFile file) throws IOException {importService.importExcelAsync(file);}2.2 service层 Resource private SalariesListener sa…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…

群晖docker获取私有化镜像http: server gave HTTP response to HTTPS client].

群晖docker获取私有化镜像提示http: server gave HTTP response to HTTPS clien 问题描述 层级时间用户事件Information2023/07/08 12:47:45cxlogeAdd image from xx.xx.31.240:1923/go-gitea/gitea:1.19.3Error2023/07/08 12:47:48cxlogeFailed to pull image [Get "http…

ResNeSt: Split-Attention Networks论文学习笔记

这张图展示了一个名为“Split-Attention”的神经网络结构&#xff0c;该结构在一个基数组&#xff08;cardinal group&#xff09;内进行操作。基数组通常指的是在神经网络中处理的一组特征或通道。图中展示了如何通过一系列操作来实现对输入特征的注意力机制。 以下是图中各部…

【matlab】绘图 离散数据--->连续函数

matlab绘图练习 离散数据及离散函数对离散区间进行细划分 达到连续效果画plot(y)图 与 复数的应用 离散数据及离散函数 例1 x1[1 2 4 6 7 8 10 11 12 14 16 17 18 20] y1[1 2 4 6 7 8 10 10 8 7 6 4 2 1] figure(1); plot(x1,y1,o,MarkerSize,15); x21:20; y2log(x2); figure…

Qt中QVariant的使用

1.使用QVariant实现不同类型数据的相加 方法&#xff1a;通过type函数返回数值的类型&#xff0c;然后通过setValue来构造一个QVariant类型的返回值。 函数&#xff1a; QVariant mainPage::dataPlus(QVariant a, QVariant b) {QVariant ret;if ((a.type() QVariant::Int) &a…

基础项目实战——3D赛车(c++)

目录 前言一、渲染引擎二、关闭事件三、梯形绘制四、轨道绘制五、边缘绘制六、草坪绘制七、前后移动八、左右移动​九、曲线轨道​十、课山坡轨道​十一、循环轨道​十二、背景展示​十三、引入速度​十四、物品绘制​十五、课数字路障​十六、分数展示​十七、重新生成​十八、…

【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念&#xff0c;今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码…

一文讲解Java中Object类常用的方法

在Java中&#xff0c;经常提到一个词“万物皆对象”&#xff0c;其中的“万物”指的是Java中的所有类&#xff0c;而这些类都是Object类的子类&#xff1b; Object主要提供了11个方法&#xff0c;大致可以分为六类&#xff1a; 对象比较&#xff1a; public native int has…

操作系统之输入输出

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

【Convex Optimization Stanford】Lec3 Function

【Convex Optimization Stanford】Lec3 Function 前言凸函数的定义对凸函数在一条线上的限制增值扩充&#xff1f; 一阶条件二阶条件一些一阶/二阶条件的例子象集和sublevel set关于函数凸性的扩展&#xff08;Jesen Inequality)保持函数凸性的操作非负加权和 & 仿射函数的…

【Linux探索学习】第二十七弹——信号(一):Linux 信号基础详解

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 前面我们已经将进程通信部分讲完了&#xff0c;现在我们来讲一个进程部分也非常重要的知识点——信号&#xff0c;信号也是进程间通信的一…