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

系列文章目录

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 blockFEdGraphPinType 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 listTArray<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 entriesif (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 stepsconst bool bNotFirstIndex = i > 0;if (bNotFirstIndex){// Emit a debug siteFBlueprintCompiledStatement& DebugSiteAndJumpTarget = Context.AppendStatementForNode(Node);DebugSiteAndJumpTarget.Type = Context.GetBreakpointType();DebugSiteAndJumpTarget.Comment = NodeComment;DebugSiteAndJumpTarget.bIsJumpTarget = true;// Patch up the previous push jump targetcheck(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 buildconst 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 stateFBlueprintCompiledStatement& GotoSequenceLinkedState = Context.AppendStatementForNode(Node);GotoSequenceLinkedState.Type = KCST_UnconditionalGoto;Context.GotoFixupRequestMap.Add(&GotoSequenceLinkedState, OutputPins[i]);}check(LastPushStatement);}else{// Directly emit pushes to execute the remaining branchesfor (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 pinUEdGraphNode* 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 pinsCreatePin(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 compactint32 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 pinCreatePin(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 sidesint32 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 matchTestPin->PinName = NewPinName;// Create the new output pin to matchCreatePin(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.mzph.cn/news/702116.shtml

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

相关文章

Vue3中的事件监听与处理机制深度解析

随着Vue3的发布&#xff0c;其在性能、灵活性和易用性上都实现了显著提升。其中&#xff0c;事件监听和处理机制作为Vue框架中的重要组成部分&#xff0c;也进行了相应的优化与升级。本文将深入探讨Vue3中如何进行事件监听与处理。 一、Vue3事件绑定 在Vue3中&#xff0c;我们…

springboot215基于springboot技术的美食烹饪互动平台的设计与实现

美食烹饪互动平台的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统美食信息管理难度大&…

MAC地址学习和老化

MAC地址学习过程 一般情况下&#xff0c;MAC地址表是设备根据收到的数据帧里的源MAC地址自动学习而建立的。 图1 MAC地址学习示意图 如图1&#xff0c;HostA向SwitchA发送数据时&#xff0c;SwitchA从数据帧中解析出源MAC地址&#xff08;即HostA的MAC地址&#xff09;和VLAN…

做接口测试的流程一般是怎么样的?UI功能6大流程、接口测试8大流程这些你真的全会了吗?

在讲接口流程测试之前&#xff0c;首先需要给大家申明下&#xff1a;接口测试对于测试人员而言&#xff0c;非常非常重要&#xff0c;懂功能测试接口测试&#xff0c;就能在企业中拿到一份非常不错的薪资。 这么重要的接口测试&#xff0c;一般也是面试笔试必问。为方便大家更…

C++ //练习 8.13 重写本节的电话号码程序,从一个命名文件而非cin读取数据。

C Primer&#xff08;第5版&#xff09; 练习 8.13 练习 8.13 重写本节的电话号码程序&#xff0c;从一个命名文件而非cin读取数据。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /***************************************…

Unity(第四部)新手组件

暴力解释就是官方给你的功能&#xff1b;作用的对象上面如&#xff1a; 创建一个球体&#xff0c;给这个球体加上重力 所有物体都是一个空物体&#xff0c;加上一些组件才形成了所需要的GameObject。 这是一个空物体&#xff0c;在Scene场景中没有任何外在表现&#xff0c;因为…

tinyxml2开源库使用

源码下载&#xff1a;GitHub - leethomason/tinyxml2: TinyXML2 is a simple, small, efficient, C XML parser that can be easily integrated into other programs. 1.加载tinyxml2库 解压上面现在的压缩包&#xff0c;将tinyxml2.h/tinyxml2.cpp添加到项目工程当中&#x…

javascript中的垃圾回收机制

一、什么是JavaScript垃圾回收机制 JavaScript中的垃圾回收机制是自动管理内存的一种机制。它负责在程序运行时识别和清除不再使用的内存&#xff0c;以便释放资源并提高性能。 JavaScript中的垃圾回收器会定期扫描内存中的对象&#xff0c;标记那些可达对象和不可达对象。 可达…

公厕智慧化_智慧化的公厕

公厕智慧化是现代城市建设中的重要一环。通过信息化、数字化和智慧化技术手段&#xff0c;实现对公共厕所的高效管理和服务&#xff0c;不仅提升了城市环境质量&#xff0c;还改善了居民生活品质。智慧公厕的智慧化包括监测、管理、服务和设备的智慧化&#xff0c;利用先进科技…

unity-unity2d基础操作笔记(二)0.5.0

unity2d基础操作笔记 五十一、canvas中的必须熟悉的属性五十二、如何调整canvas与游戏人物大小近似大小五十三、canvas中的canvas scaler介绍【概念】五十四、ui scale mode介绍【概念】五十五、为什么创建image后,canvas的范围要要远远大于游戏世界?五十六、图片常用操作【技…

1分钟带你学会Python的pass关键字和range函数

1.pass 关键字 pass关键字在 python 中没有任何实际意义&#xff0c;主要是用来完成占位的操作&#xff0c;保证语句的完整性 age int(input(请输入您的年龄&#xff1a;))if age > 18: pass # pass 在此处没有任何意义&#xff0c;只是占位 print(欢迎光临。。。…

美易官方:巴菲特发布2024年致股东公开信,重磅!

近日&#xff0c;全球投资界的巨擘巴菲特发布了2024年致股东的公开信&#xff0c;引起了广泛关注。作为伯克希尔哈撒韦公司的董事长和首席执行官&#xff0c;巴菲特的信函一直备受投资者们的期待。在这封公开信中&#xff0c;巴菲特分享了关于投资、公司治理和全球经济等方面的…

常见的排序算法整理

1.冒泡排序 1.1 冒泡排序普通版 每次冒泡过程都是从数列的第一个元素开始&#xff0c;然后依次和剩余的元素进行比较&#xff0c;若小于相邻元素&#xff0c;则交换两者位置&#xff0c;同时将较大元素作为下一个比较的基准元素&#xff0c;继续将该元素与其相邻的元素进行比…

Spring 中 ApplicationContext 和 BeanFactory 的区别有哪些

先看一张类图&#xff1a; 区别&#xff1a; 1&#xff1a;包目录不同&#xff1a; spring-beans.jar 中 org.springframework.beans.factory.BeanFactory spring-context.jar 中 org.springframework.context.ApplicationContext 2&#xff1a;国际化&#xff1a; BeanFacto…

数组详解-格式定义-遍历-求最值-遍历求和-打乱数组中的数据

数组指的是一种容器,可以用来存储同种数据类型的多个值 定义数组格式 : 完整格式-静态初始化 数据类型[]数组名new 数据类型[]{元素1,元素2,元素3...} int[] array new int[]{11,22,33}; 简化格式 int[] array {11,22,33}; 完整格式-动态初始化 初始化时只指定数组长…

mysql的日志文件在哪?

阅读本文之前请参阅----MySQL 数据库安装教程详解&#xff08;linux系统和windows系统&#xff09; MySQL的日志文件通常包括错误日志、查询日志、慢查询日志和二进制日志等。这些日志文件的位置取决于MySQL的安装和配置。以下是一些常见的日志文件位置和如何找到它们&#xff…

C++11 Thead线程库的基本使用

文章目录 创建线程传递参数等待线程完成分离线程joinable() 创建线程 要创建线程&#xff0c;我们需要一个可调用的函数或函数对象&#xff0c;作为线程的入口点。在C11中&#xff0c;我们可以使用函数指针、函数对象或lambda表达式来实现。 创建线程的基本语法如下&#xff…

PHP中的飞碟运算符、取反运算符、对比非ASCII字符串、对比浮点数操作

对比浮点数 在电脑里存储的浮点数可能会和输入的值有些许差异&#xff0c;比如输入的是10.0&#xff0c;但存储的是10.00001. 在比较两个浮点数是否相等时可以计算下两个数的差值&#xff0c;然后查看下两数之差是否小于可以接受的阈值&#xff0c;如果要求精度在小数点后5位的…

算法训练day34 Leetcode1005.K次取反后最大化的数组和● 134. 加油站 135. 分发糖果

1005 k次取反后最大化的数组和 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回…

ubuntu 22.04LTS的一些使用心得

前言 笔者一直在折腾ubuntu作为开发的主力系统&#xff0c;尤其是最近微信和各种软件陆续支持Debian系列&#xff0c;很多软件都可以用了&#xff0c;当然开源的软件大部分是跨平台的&#xff0c;尤其是idea系列。 X11 OR Wayland 关于X11和wayland&#xff0c;笔者还是使用…