构建蓝图中包含多输入多输出引脚的节点

0 概述

虚幻引擎在蓝图中提供了各种各样的实用函数、事件、宏等,极大的方便了蓝图的可视化编程,对于快速测试和原型实现十分方便。这其中有一类节点具有多个输入或者多个输出,而在C++或者蓝图中常规的蓝图可调用函数通常是单个输入输出引脚。这种多输入多输出引脚的节点在蓝图和C++中都可以实现,在蓝图中通常是使用宏或者合并的图表(折叠的合成节点)实现这样的节点,在C++中通过UFUNCTION元数据符实现这样的节点。当然C++中和蓝图中实现的多输入输出引脚的节点有很大区别,这里不展开具体内容。

1 多输出节点

1.1 蓝图中构建

在蓝图的宏(Marcos)和合并的图表(Collapsed Graph)中,有一种特殊的数据类型:执行引脚(Exec),如下图所示:

image-20220828174322282

这种数据类型,可以在参数(Inputs)和返回值(Outputs)添加多个执行引脚(Exec)以构建具有多输入多输出引脚的节点,以下将通过宏和折叠节点构建。

在蓝图中新建一个宏,这里将该宏命名为MarcoMultiOut,默认在这个宏中会有一个输入和输出,没有任何参数,点击输入(Inputs)和输出(Outputs)在详细面板中依次参数,并添加相应的逻辑,如图所示:

image-20220828180156614

这个宏有一个输入引脚以及两个输出引脚,Result参数将决定从哪个输出引脚执行出来,在合并图表中与宏中完全相同,值得注意的是合并图表可以在任何地方通过选中相应的节点在右键菜单中选择折叠节点(Collapse Nodes)进行创建。

1.2 C++中构建

通过设置UFUNCTION中的元数据说明符ExpandEnumAsExecs,同也可以在C++中的实现蓝图可调用的多输入多输出引脚的节点。ExpandEnumAsExecs用于蓝图可调用函数(BlueprintCallable),通过自己设定的枚举表示可用的输入或输出节点,该枚举类型必须是UENUM,因为它是作为函数的参数使用的。以下是一个简单的多输出节点的关键代码:

/**
 * 该枚举用于描述蓝图的引脚
 */
UENUM()
enum class EExecNodePin : uint8
{
	Success,
	Fail
};

该枚举用于描述蓝图的引脚,有成功和失败两种引脚。

/**
 * 执行结果取决于输入的值
 */
UFUNCTION(BlueprintCallable, Category="MutliExecNode", meta=(ExpandEnumAsExecs="Result"))
static void MultiOutExecNodeDependInput(EExecNodePin& Result, bool Success);

在函数声明中,元数据符ExpandEnumAsExecs的值Result必须和参数中枚举变量的名称相同。这里因为是多个输出引脚节点,可以将多个输出引脚也理解为一种返回值,所以这个枚举变量就必须是一个引用,这样可以在函数实现中去设置Result的值,反过来说如果是多个输入引脚,那么这个枚举就不需要设置为引用,因为它会将我们在蓝图中连接的引脚的枚举传入到这个变量。值得注意的是如果枚举使用的不是enum class,在函数的参数中不能直接使用EExecNodePin& Result,这将无法通过编译,而将这个参数类型应该改为TEnumAsByte<EExecNodePin>& Result,所以比较推荐使用enum class这种风格的枚举。

void UMultiExecNode::MultiOutExecNodeDependInput(EExecNodePin& Result, bool Success)
{
	Result = Success ? EExecNodePin::Success : EExecNodePin::Fail;
}

该函数节点实现的逻辑显而易见,Success变量将决定最终执行出的引脚。以下是蓝图中的运行测试,由于Success为false最终将从Fail引脚执行出来。

image-20220828214615531

2 多输入节点

2.1 蓝图中构建

在蓝图中构建多输入节点与多输出节点类似,不同之处在于需要在宏或者折叠节点的输入(Inputs)添加多个Exec变量,以下是一个简单的多输入引脚节点:

image-20220828220924393

2.2 C++中构建

在C++中构建多输出引脚与多输入引脚类似,前文中有详细说明,不同之处在于枚举变量是否为引用类型,以下是一个简单的多输入引脚的节点的关键代码:

/**
 * 该枚举用于描述蓝图的节点名称
 */
UENUM()
enum class EMultiInPin : uint8
{
	BranchA,
	BranchB,
	BranchC
};
/**
* 最简易的多输入单输出节点,无论从哪个节点进入都会从右边出来
*/
UFUNCTION(BlueprintCallable, Category="MutliExecNode", meta=(ExpandEnumAsExecs="Result"))
static FString MultiInExecNode(EMultiInPin Result);
FString UMultiExecNode::MultiInExecNode(EMultiInPin Result)
{
	FString Name;
	if (const UEnum* PinName = StaticEnum<EMultiInPin>())
	{
		Name = PinName->GetNameStringByValue(static_cast<int64>(Result));
	}
	return Name;
}

该函数的逻辑比较简单,将返回输入引脚的名称,以下是在蓝图中的测试用例,最终将输出I am from BranchC字符串。

image-20220828221622078

3 多输入输出节点

3.1 蓝图中构建

从第2节中的多输入与多输出引脚节点的构建,很容易知道二者可以同时存在,具体原理不在赘述,以下是蓝图中实现的同时具备多输入输出引脚的节点:

image-20220828222056073

3.2 C++中构建

在C++中构建多输出引脚与多输入引脚的节点只需要同时具备引用和非引用的枚举变量,以下是一个简单同时具备多输出引脚个多输入引脚的函数节点:

/**
 * 该枚举用于描述蓝图的节点名称
 */
UENUM()
enum class EMultiPin : uint8
{
	Pin0,
	Pin1,
	Pin2,
	Pin3
};
/**
 * 出去的节点取决于进入的节点
 */
UFUNCTION(BlueprintCallable, Category="MutliExecNode", meta=(ExpandEnumAsExecs="In,Out"))
static FString MultiInAndOutExecNode(EMultiPin In, EMultiPin& Out);
FString UMultiExecNode::MultiInAndOutExecNode(EMultiPin In, EMultiPin& Out)
{
	FString Name;
	Out = In;
	if (const UEnum* PinName = StaticEnum<EMultiPin>())
	{
		Name = PinName->GetNameStringByValue(static_cast<int64>(In));
	}
	return Name;
}

该函数将会依据进入的引脚值,从相同的输出引脚执行出来,并且返回进入的引脚名称,以下是一个蓝图测试用例,最终输出Pin2 -> Pin 2字符串。

image-20220828224940502

4 总结

多输入多输出引脚的节点在蓝图中十分常见,且使用起来极为方便,大多数情况下能够替代复杂的节点逻辑,让蓝图更加干净整洁。这类节点在蓝图中比较特殊,当然,蓝图中还有跟多特殊的节点类型,后续再见分晓。

posted @ 2022-08-28 23:07  那不几多  阅读(2491)  评论(0)    收藏  举报