UE蓝图 序列(Sequence)节点和源码

news2025/1/18 4:31:41

系列文章目录

UE蓝图 Get节点和源码
UE蓝图 Set节点和源码
UE蓝图 Cast节点和源码
UE蓝图 分支(Branch)节点和源码
UE蓝图 入口(FunctionEntry)节点和源码
UE蓝图 返回结果(FunctionResult)节点和源码
UE蓝图 函数调用(CallFunction)节点和源码
UE蓝图 函数调用(CallFunction)节点和源码
UE蓝图 序列(Sequence)节点和源码


文章目录

  • 系列文章目录
  • 一、序列节点功能
  • 二、ExecutionSequence节点用法
  • 三、序列使用场景
  • 四、实现原理
  • 五、相关源码


一、序列节点功能

在这里插入图片描述

UE(Unreal Engine)蓝图中的ExecutionSequence节点是一个非常重要的节点,用于控制执行序列中的不同分支。它主要用于生成两种类型的语句:KCST_PushState和KCST_UnconditionGoto。

KCST_PushState语句通过EmitPushExecState函数处理,它首先输出指令EX_PushExecutionFlow,然后写入执行序列节点的下一个分支的字节码偏移。这样,当这个执行流程结束时,会执行EX_PopExecutionFlow指令,从而取出这个偏移并执行接下来的字节码。

二、ExecutionSequence节点用法

UE蓝图中的ExecutionSequence节点主要用于控制执行序列中的不同分支。以下是ExecutionSequence节点的基本用法:

  1. 创建ExecutionSequence节点:在UE蓝图中,可以通过右键点击空白处弹出选择列表窗口,然后选择“Sequence”来创建一个ExecutionSequence节点。
  2. 连接其他节点:将需要执行的节点连接到ExecutionSequence节点上。可以根据需要添加多个节点,并按照执行的顺序将它们连接到ExecutionSequence节点上。
  3. 设置执行条件:ExecutionSequence节点可以根据条件选择执行不同的分支。可以通过添加Condition节点或其他条件判断节点来设置执行条件。
  4. 控制执行流程:ExecutionSequence节点会按照连接的节点的顺序执行它们。当遇到条件判断节点时,会根据条件的结果选择执行不同的分支。
  5. 使用KCST_PushState和KCST_UnconditionGoto:ExecutionSequence节点主要会生成KCST_PushState和KCST_UnconditionGoto两个Statement。这些Statement用于控制执行流程的跳转和状态管理。

三、序列使用场景

在Unreal Engine(UE)的蓝图中,序列节点(如ExecutionSequence节点)的使用场景非常广泛。这些节点主要用于控制游戏逻辑的流程,确保各个事件和动作按照预定的顺序执行。以下是一些常见的UE蓝图序列节点的使用场景:

  1. 初始化流程:在游戏对象的初始化过程中,可以使用序列节点来组织和管理初始化流程。例如,在角色创建时,可以使用序列节点来确保先加载角色的模型,然后设置角色的初始状态,最后为角色添加技能和装备。

  2. 任务与事件触发:在游戏任务或事件系统中,序列节点可以用于定义任务或事件的执行流程。例如,在任务执行过程中,可以使用序列节点来控制任务目标的完成顺序,或者在任务完成后触发特定的奖励或事件。

  3. 状态管理:序列节点可以用于实现游戏对象的状态管理。通过创建不同的状态转换逻辑,可以在不同状态之间进行切换,以满足游戏的各种需求。例如,在角色控制系统中,可以使用序列节点来管理角色的不同战斗状态、移动状态等。

  4. 动画与音效同步:在游戏动画和音效处理方面,序列节点可以用于控制动画和音效的播放顺序和同步。通过精心设计的序列节点,可以实现动画、音效与游戏逻辑的完美融合,提升游戏体验。

  5. 资源加载与释放:在游戏资源管理方面,序列节点可以用于控制资源的加载、释放和更新流程。通过合理地组织资源加载和释放流程,可以提高游戏的性能和响应速度。

四、实现原理

  • 创建输入引脚
void UK2Node_ExecutionSequence::AllocateDefaultPins()
{
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetPinNameGivenIndex(0));
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetPinNameGivenIndex(1));
}
  • 调用FKCHandler_ExecutionSequence.RegisterNets注册函数引脚
  • 调用Compile编译创建Statement
for (int32 i = OutputPins.Num() - 1; i > 0; i--)
{
	FBlueprintCompiledStatement& PushExecutionState = Context.AppendStatementForNode(Node);
	PushExecutionState.Type = KCST_PushState;
	Context.GotoFixupRequestMap.Add(&PushExecutionState, OutputPins[i]);
}
// Immediately jump to the first pin
UEdGraphNode* NextNode = OutputPins[0]->LinkedTo[0]->GetOwningNode();
FBlueprintCompiledStatement& NextExecutionState = Context.AppendStatementForNode(Node);
NextExecutionState.Type = KCST_UnconditionalGoto;
Context.GotoFixupRequestMap.Add(&NextExecutionState, OutputPins[0]);

五、相关源码

源码文件:
K2Node_ExecutionSequence.h
K2Node_ExecutionSequence.cpp
相关类:
FKCHandler_ExecutionSequence
K2Node_ExecutionSequence


class FKCHandler_ExecutionSequence : public FNodeHandlingFunctor
{
public:
	FKCHandler_ExecutionSequence(FKismetCompilerContext& InCompilerContext)
		: FNodeHandlingFunctor(InCompilerContext)
	{
	}

	virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
	{
		// Make sure that the input pin is connected and valid for this block
		FEdGraphPinType ExpectedPinType;
		ExpectedPinType.PinCategory = UEdGraphSchema_K2::PC_Exec;

		UEdGraphPin* ExecTriggeringPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_Execute, EGPD_Input);
		if ((ExecTriggeringPin == nullptr) || !Context.ValidatePinType(ExecTriggeringPin, ExpectedPinType))
		{
			CompilerContext.MessageLog.Error(*LOCTEXT("NoValidExecutionPinForExecSeq_Error", "@@ must have a valid execution pin @@").ToString(), Node, ExecTriggeringPin);
			return;
		}
		else if (ExecTriggeringPin->LinkedTo.Num() == 0)
		{
			CompilerContext.MessageLog.Warning(*LOCTEXT("NodeNeverExecuted_Warning", "@@ will never be executed").ToString(), Node);
			return;
		}

		// Find the valid, connected output pins, and add them to the processing list
		TArray<UEdGraphPin*> OutputPins;
		for (UEdGraphPin* CurrentPin : Node->Pins)
		{
			if ((CurrentPin->Direction == EGPD_Output) && (CurrentPin->LinkedTo.Num() > 0) && (CurrentPin->PinName.ToString().StartsWith(UEdGraphSchema_K2::PN_Then.ToString())))
			{
				OutputPins.Add(CurrentPin);
			}
		}

		//@TODO: Sort the pins by the number appended to the pin!

		// Process the pins, if there are any valid entries
		if (OutputPins.Num() > 0)
		{
			if (Context.IsDebuggingOrInstrumentationRequired() && (OutputPins.Num() > 1))
			{
				const FString NodeComment = Node->NodeComment.IsEmpty() ? Node->GetName() : Node->NodeComment;

				// Assuming sequence X goes to A, B, C, we want to emit:
				//   X: push X1
				//      goto A
				//  X1: debug site
				//      push X2
				//      goto B
				//  X2: debug site
				//      goto C

				// A push statement we need to patch up on the next pass (e.g., push X1 before we know where X1 is)
				FBlueprintCompiledStatement* LastPushStatement = NULL;

				for (int32 i = 0; i < OutputPins.Num(); ++i)
				{
					// Emit the debug site and patch up the previous jump if we're on subsequent steps
					const bool bNotFirstIndex = i > 0;
					if (bNotFirstIndex)
					{
						// Emit a debug site
						FBlueprintCompiledStatement& DebugSiteAndJumpTarget = Context.AppendStatementForNode(Node);
						DebugSiteAndJumpTarget.Type = Context.GetBreakpointType();
						DebugSiteAndJumpTarget.Comment = NodeComment;
						DebugSiteAndJumpTarget.bIsJumpTarget = true;

						// Patch up the previous push jump target
						check(LastPushStatement);
						LastPushStatement->TargetLabel = &DebugSiteAndJumpTarget;
					}

					// Emit a push to get to the next step in the sequence, unless we're the last one or this is an instrumented build
					const bool bNotLastIndex = ((i + 1) < OutputPins.Num());
					if (bNotLastIndex)
					{
						FBlueprintCompiledStatement& PushExecutionState = Context.AppendStatementForNode(Node);
						PushExecutionState.Type = KCST_PushState;
						LastPushStatement = &PushExecutionState;
					}

					// Emit the goto to the actual state
					FBlueprintCompiledStatement& GotoSequenceLinkedState = Context.AppendStatementForNode(Node);
					GotoSequenceLinkedState.Type = KCST_UnconditionalGoto;
					Context.GotoFixupRequestMap.Add(&GotoSequenceLinkedState, OutputPins[i]);
				}

				check(LastPushStatement);
			}
			else
			{
				// Directly emit pushes to execute the remaining branches
				for (int32 i = OutputPins.Num() - 1; i > 0; i--)
				{
					FBlueprintCompiledStatement& PushExecutionState = Context.AppendStatementForNode(Node);
					PushExecutionState.Type = KCST_PushState;
					Context.GotoFixupRequestMap.Add(&PushExecutionState, OutputPins[i]);
				}

				// Immediately jump to the first pin
				UEdGraphNode* NextNode = OutputPins[0]->LinkedTo[0]->GetOwningNode();
				FBlueprintCompiledStatement& NextExecutionState = Context.AppendStatementForNode(Node);
				NextExecutionState.Type = KCST_UnconditionalGoto;
				Context.GotoFixupRequestMap.Add(&NextExecutionState, OutputPins[0]);
			}
		}
		else
		{
			FBlueprintCompiledStatement& NextExecutionState = Context.AppendStatementForNode(Node);
			NextExecutionState.Type = KCST_EndOfThread;
		}
	}
};

UK2Node_ExecutionSequence::UK2Node_ExecutionSequence(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UK2Node_ExecutionSequence::AllocateDefaultPins()
{
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);

	// Add two default pins
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetPinNameGivenIndex(0));
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetPinNameGivenIndex(1));

	Super::AllocateDefaultPins();
}

FText UK2Node_ExecutionSequence::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	return NSLOCTEXT("K2Node", "Sequence", "Sequence");
}

FSlateIcon UK2Node_ExecutionSequence::GetIconAndTint(FLinearColor& OutColor) const
{
	static FSlateIcon Icon("EditorStyle", "GraphEditor.Sequence_16x");
	return Icon;
}

FLinearColor UK2Node_ExecutionSequence::GetNodeTitleColor() const
{
	return FLinearColor::White;
}

FText UK2Node_ExecutionSequence::GetTooltipText() const
{
	return NSLOCTEXT("K2Node", "ExecutePinInOrder_Tooltip", "Executes a series of pins in order");
}

FName UK2Node_ExecutionSequence::GetUniquePinName()
{
	FName NewPinName;
	int32 i = 0;
	while (true)
	{
		NewPinName = GetPinNameGivenIndex(i++);
		if (!FindPin(NewPinName))
		{
			break;
		}
	}

	return NewPinName;
}

void UK2Node_ExecutionSequence::AddInputPin()
{
	Modify();
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetUniquePinName());
}

void UK2Node_ExecutionSequence::InsertPinIntoExecutionNode(UEdGraphPin* PinToInsertBefore, EPinInsertPosition Position)
{
	Modify();

	int32 DesiredPinIndex = Pins.Find(PinToInsertBefore);
	if (DesiredPinIndex != INDEX_NONE)
	{
		if (Position == EPinInsertPosition::After)
		{
			DesiredPinIndex = DesiredPinIndex + 1;
		}

		FCreatePinParams Params;
		Params.Index = DesiredPinIndex;
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GetUniquePinName(), Params);

		// refresh names on the pin list:
		int32 ThenIndex = 0;
		for (int32 Idx = 0; Idx < Pins.Num(); ++Idx)
		{
			UEdGraphPin* PotentialPin = Pins[Idx];
			if (UEdGraphSchema_K2::IsExecPin(*PotentialPin) && (PotentialPin->Direction == EGPD_Output))
			{
				PotentialPin->PinName = GetPinNameGivenIndex(ThenIndex);
				++ThenIndex;
			}
		}
	}
}

void UK2Node_ExecutionSequence::RemovePinFromExecutionNode(UEdGraphPin* TargetPin) 
{
	UK2Node_ExecutionSequence* OwningSeq = Cast<UK2Node_ExecutionSequence>( TargetPin->GetOwningNode() );
	if (OwningSeq)
	{
		OwningSeq->Pins.Remove(TargetPin);
		TargetPin->MarkPendingKill();

		// Renumber the pins so the numbering is compact
		int32 ThenIndex = 0;
		for (int32 i = 0; i < OwningSeq->Pins.Num(); ++i)
		{
			UEdGraphPin* PotentialPin = OwningSeq->Pins[i];
			if (UEdGraphSchema_K2::IsExecPin(*PotentialPin) && (PotentialPin->Direction == EGPD_Output))
			{
				PotentialPin->PinName = GetPinNameGivenIndex(ThenIndex);
				++ThenIndex;
			}
		}
	}
}

bool UK2Node_ExecutionSequence::CanRemoveExecutionPin() const
{
	int32 NumOutPins = 0;

	for (int32 i = 0; i < Pins.Num(); ++i)
	{
		UEdGraphPin* PotentialPin = Pins[i];
		if (UEdGraphSchema_K2::IsExecPin(*PotentialPin) && (PotentialPin->Direction == EGPD_Output))
		{
			NumOutPins++;
		}
	}

	return (NumOutPins > 2);
}

FName UK2Node_ExecutionSequence::GetPinNameGivenIndex(int32 Index) const
{
	return *FString::Printf(TEXT("%s_%d"), *UEdGraphSchema_K2::PN_Then.ToString(), Index);
}

void UK2Node_ExecutionSequence::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
	Super::AllocateDefaultPins();

	// Create the execution input pin
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);

	// Create a new pin for each old execution output pin, and coerce the names to match on both sides
	int32 ExecOutPinCount = 0;
	for (int32 i = 0; i < OldPins.Num(); ++i)
	{
		UEdGraphPin* TestPin = OldPins[i];
		if (UEdGraphSchema_K2::IsExecPin(*TestPin) && (TestPin->Direction == EGPD_Output))
		{
			const FName NewPinName(GetPinNameGivenIndex(ExecOutPinCount));
			ExecOutPinCount++;

			// Make sure the old pin and new pin names match
			TestPin->PinName = NewPinName;

			// Create the new output pin to match
			CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, NewPinName);
		}
	}
}

UEdGraphPin* UK2Node_ExecutionSequence::GetThenPinGivenIndex(const int32 Index) 
{
	return FindPin(GetPinNameGivenIndex(Index));
}

FNodeHandlingFunctor* UK2Node_ExecutionSequence::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_ExecutionSequence(CompilerContext);
}
}

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

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

相关文章

three.js第一个3D案例

在正式学习Three.js之前&#xff0c;先做一些必要的准备工作&#xff0c;具体说就是下载threejs官方文件包&#xff0c;threejs官方文件包提供了很多有用的学习资源。 threejs官方文件包所有版本&#xff1a;https://github.com/mrdoob/three.js/releases threejs文件资源目录…

Go 利用上下文进行并发计算

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等&#xff0c;您的关注将是我的更新动力&#xff01; 在Go编程中&#xff0c;上下文&#xff08;context&#xff09;是一个非常重要的概念&#xff0c;它包含了与请求相关的信息&…

【嵌入式移植】7、U-Boot源码分析4—链接脚本分析

U-Boot源码分析4—链接脚本分析 1 u-boot-spl.lds1.1 链接脚本的生成1.2 u-boot-spl.lds内容分析1.3 text - 程序代码段1.4 sram其它段定义1.4.1 .rodata只读数据段1.4.2 .data数据段1.4.3 .u_boot_list段 1.5 BSS段1.6 /DISCARD/ 从上一篇文章【嵌入式移植】6、U-Boot源码分析…

130 如何通过vs2017开发linux c++程序

使用VS2017开发linux下的应用程序&#xff08;C/C&#xff09;_vc_linux.exe vs2017-CSDN博客 参考上面这哥们的&#xff0c;写的很详细 前言 本文章记录如何使用VS2017进行linux应用程序的开发&#xff08;针对新手小白&#xff09;&#xff0c;VS2017能较为方便的通过SSH编辑…

电脑蓝牙在哪里打开?1分钟轻松打开蓝牙!

“我在操作电脑的时候想将电脑的蓝牙打开来连接音响和键盘&#xff0c;但是不知道电脑蓝牙应该如何打开&#xff0c;有什么比较简单的方法吗&#xff1f;” 随着无线技术的日益发展&#xff0c;蓝牙已成为连接各种设备的重要桥梁。无论是传输文件、音频还是与外部设备进行通信&…

YOLO学习中的琐碎知识点

目录 一、导入的库 二、名词介绍 &#xff08;1&#xff09;pytorch张量 &#xff08;2&#xff09;边界框&#xff08;bounding box&#xff09; 三、pycharm操作 &#xff08;1&#xff09;参数设置 四、文件认识 五、YOLO如何训练自己的模型 一、导入的库 import to…

笔记本Win 10系统查看电池健康状况

博主最近换了个笔记本电池&#xff0c;之前的电池容量明显变小了很多&#xff0c;而且出现了轻微鼓包的情况。所以用gpt问了一下怎么用系统的方法查看电池情况。 在Windows 10系统中&#xff0c;您可以通过以下步骤来查看笔记本电脑电池的健康状况&#xff1a; 打开命令提示符&…

神经网络系列---归一化

文章目录 归一化批量归一化预测阶段 测试阶段γ和β&#xff08;注意&#xff09;举例 层归一化前向传播反向传播 归一化 批量归一化 &#xff08;Batch Normalization&#xff09;在训练过程中的数学公式可以概括如下&#xff1a; 给定一个小批量数据 B { x 1 , x 2 , … …

线程普通任务执行流程

&#xff08;1&#xff09;先判断是否存在空闲线程&#xff0c;存在直接分配&#xff0c;不存在执行&#xff08;2&#xff09;&#xff1b; &#xff08;2&#xff09;判断工作线程数量小于核心数量&#xff0c;未超出创建核心线程执行线程任务&#xff0c;超出执行&#xff…

为什么会员模式是一种明智的扩张方式

会员模式看起来是一种有趣、令人兴奋且很酷的业务发展方式&#xff0c;但当您真正深入研究时&#xff0c;您可能会惊讶地发现它远不止于此。 会员资格为我们提供了一条道德扩展的途径。我们可以就地为客户提供服务。 这就是为什么会员模式可能成为您企业的下一步&#xff0c;…

LeetCode 热题 100 | 二叉树(一)

目录 1 基础知识 1.1 先序遍历 1.2 中序遍历 1.3 后序遍历 2 94. 二叉树的中序遍历 3 104. 二叉树的最大深度 4 226. 翻转二叉树 5 101. 对称二叉树 菜鸟做题&#xff0c;语言是 C 1 基础知识 二叉树常见的遍历方式有&#xff1a; 先序遍历中序遍历后序遍历…

LeetCode | 寻找两个正序数组的中位数 Python C语言

Problem: 4. 寻找两个正序数组的中位数 文章目录 思路解题方法Code结果结果一些思考 思路 先合并&#xff0c;后排序&#xff0c;最后找中间轴。 解题方法 由解题思路可知 Code 这是python3的代码。 class Solution:def findMedianSortedArrays(self, nums1: List[int], …

微服务知识02

1、九大高并发解决方案 2、系统架构图​​​​​​​ 3、分布式事务 本地事务、分布式事务 操作不同服务器的数据库&#xff08;垂直分库&#xff09; 4、分布式事务解决方案&#xff08;没有seata之前&#xff09; &#xff08;1&#xff09;XA协议&#xff08;强一致性&a…

STM32单片机基本原理与应用(八)

温度传感器实验 实验内容&#xff1a; 单片机通过代码模拟1-Wire总线并对DS18B20进行读写&#xff0c;并在TFTLCD屏幕上显示当前实时温度。 电路原理图&#xff1a; 1-Wire总线 1-Wire总线&#xff1a;即单总线协议&#xff0c;采用单根信号线&#xff0c;既传输时钟&#…

深度学习基础(一)神经网络基本原理

之前的章节我们初步介绍了机器学习相关基础知识&#xff0c;目录如下&#xff1a; 机器学习基础&#xff08;一&#xff09;理解机器学习的本质-CSDN博客 机器学习基础&#xff08;二&#xff09;监督与非监督学习-CSDN博客 机器学习基础&#xff08;四&#xff09;非监督学…

python jupyter notebook打开页面方便使用

如果没安装jupyter, 请安装&#xff1a; pip install jupyter notebook 运行jupyter notebook jupyter-notebook

【SpringBoot】Spring常用注解总结

目录 ⭐spring springmvc和springboot的区别 Autowired 和Resource的区别和联系 1. SpringBootApplication 2. Spring Bean 相关 2.1. Autowired 2.2. Component,Repository,Service, Controller 2.3. RestController 2.4. Scope 2.5. Configuration 3. 处理常见的 HT…

利用R语言进行聚类分析实战(数据+代码+可视化+详细分析)

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

红队攻防之powershell上线基础免杀(一)

不努力&#xff0c;你背井离乡干嘛&#xff1f;当卧底啊 环境为win10&#xff0c;在哥斯拉生成的webshell下&#xff0c;执行powershell命令。 测试杀毒软件为&#xff1a;火绒&#xff0c;腾讯电脑管家 哥斯拉生成php文件的webshell 如图 哥斯拉进行连接 把要执行命令的文件…

[树形DP] 树的最大独立集

题目 这个挺简单的&#xff0c;注意状态转移时&#xff0c;如果选这个点&#xff0c;那么它的子结点状态应该为不选&#xff0c;如果这个点的状态是不选&#xff0c;那么可以在它的子结点里选择&#xff1a;选/不选两个状态&#xff0c;所以最后结果是max挑选。 #include<b…