UE蓝图 Set节点和源码

系列文章目录

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


Set节点说明

在这里插入图片描述

UE蓝图中的Set节点是用于对变量进行赋值操作的重要组件。它具有多种功能和作用,具体如下:

  1. 变量赋值:Set节点可以用于设置不同类型的变量值,包括整数、浮点数、布尔值、字符串等。在游戏开发中,开发者经常需要修改或设置各种变量的值,如角色的生命值、位置、游戏的状态等。Set节点提供了一个直观且简单的方式来完成这些赋值操作。
  2. 简化开发流程:通过使用Set节点,开发者无需编写复杂的代码逻辑,便可以在蓝图中直接进行变量赋值操作,从而简化了游戏开发的过程,提高了开发效率。
  3. 可视化操作:Set节点具有直观的可视化界面,开发者可以通过拖拽连接线来连接Set节点与其他节点,这样就能够建立变量之间的关联关系。这种可视化的操作方式使得逻辑流程更加清晰明了,便于理解和调试。
  4. 自动类型转换:Set节点还支持自动类型转换功能,可以在不同类型之间进行转换,并自动处理类型兼容性问题。这减少了开发者在处理类型转换问题时的烦恼。
  5. 动态修改变量:使用Set节点,开发者可以实现变量的动态修改。例如,在游戏过程中,可以通过Set节点实时地改变角色的生命值、位置或其他属性,从而更新游戏状态。

总的来说,UE蓝图中的Set节点提供了一种直观、简单且高效的方式来对游戏开发中的变量进行赋值操作,同时支持多种数据类型和动态修改,极大地简化了游戏开发过程。

实现方式

  • 继承UK2Node_Variable类,并根据需要实现赋值相关逻辑。Handler_VariableSet:创建Set相关BlueprintCompiledStatement,Statement类型是KCST_Assignment。

总的来说,UE蓝图中的Set节点是游戏开发中不可或缺的一部分,它简化了变量操作过程,提高了开发效率,并使得逻辑流程更加清晰明了。通过了解Set节点的功能和实现方式,开发者可以更好地利用它进行游戏开发,提高游戏的质量和用户体验。

相关源码

在这里插入图片描述

源码相关文件
UK2Node_VariableSet.h
UK2Node_VariableSet.cpp
Handler_VariableSet.h
Handler_VariableSet.cpp
相关实现类
UK2Node_VariableSet
Handler_VariableSet:创建Set相关BlueprintCompiledStatement,Statement类型是KCST_Assignment

// Copyright Epic Games, Inc. All Rights Reserved.


#include "K2Node_VariableSet.h"
#include "GameFramework/Actor.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_VariableGet.h"
#include "K2Node_CallFunction.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiler.h"
#include "VariableSetHandler.h"

#define LOCTEXT_NAMESPACE "K2Node_VariableSet"

namespace K2Node_VariableSetImpl
{
	/**
	 * Shared utility method for retrieving a UK2Node_VariableSet's bare tooltip.
	 * 
	 * @param  VarName	The name of the variable that the node represents.
	 * @return A formatted text string, describing what the VariableSet node does.
	 */
	static FText GetBaseTooltip(FName VarName);

	/**
	 * Returns true if the specified variable RepNotify AND is defined in a 
	 * blueprint. Most (all?) native rep notifies are intended to be client 
	 * only. We are moving away from this paradigm in blueprints. So for now 
	 * this is somewhat of a hold over to avoid nasty bugs where a K2 set node 
	 * is calling a native function that the designer has no idea what it is 
	 * doing.
	 * 
	 * @param  VariableProperty	The variable property you wish to check.
	 * @return True if the specified variable RepNotify AND is defined in a blueprint.
	 */
	static bool PropertyHasLocalRepNotify(FProperty const* VariableProperty);
}

static FText K2Node_VariableSetImpl::GetBaseTooltip(FName VarName)
{
	FFormatNamedArguments Args;
	Args.Add(TEXT("VarName"), FText::FromName(VarName));

	return FText::Format(LOCTEXT("SetVariableTooltip", "Set the value of variable {VarName}"), Args);

}

static bool K2Node_VariableSetImpl::PropertyHasLocalRepNotify(FProperty const* VariableProperty)
{
	if (VariableProperty != nullptr)
	{
		// We check that the variable is 'defined in a blueprint' so as to avoid 
		// natively defined RepNotifies being called unintentionally. Most(all?) 
		// native rep notifies are intended to be client only. We are moving 
		// away from this paradigm in blueprints. So for now this is somewhat of 
		// a hold over to avoid nasty bugs where a K2 set node is calling a 
		// native function that the designer has no idea what it is doing.
		UBlueprintGeneratedClass* VariableSourceClass = Cast<UBlueprintGeneratedClass>(VariableProperty->GetOwnerClass());
		bool const bIsBlueprintProperty = (VariableSourceClass != nullptr);

		if (bIsBlueprintProperty && (VariableProperty->RepNotifyFunc != NAME_None))
		{
			// Find function (ok if its defined in native class)
			UFunction* Function = VariableSourceClass->FindFunctionByName(VariableProperty->RepNotifyFunc);

			// If valid repnotify func
			if ((Function != nullptr) && (Function->NumParms == 0) && (Function->GetReturnProperty() == nullptr))
			{
				return true;
			}
		}
	}
	return false;
}

UK2Node_VariableSet::UK2Node_VariableSet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}
//创建默认的引脚
void UK2Node_VariableSet::AllocateDefaultPins()
{
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);

	if (GetVarName() != NAME_None)
	{
		if(CreatePinForVariable(EGPD_Input))
		{
			CreatePinForSelf();
		}

		if(CreatePinForVariable(EGPD_Output, GetVariableOutputPinName()))
		{
			CreateOutputPinTooltip();
		}
	}

	Super::AllocateDefaultPins();
}

void UK2Node_VariableSet::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);

	if (GetVarName() != NAME_None)
	{
		if(!CreatePinForVariable(EGPD_Input))
		{
			if(!RecreatePinForVariable(EGPD_Input, OldPins))
			{
				return;
			}
		}

		if(!CreatePinForVariable(EGPD_Output, GetVariableOutputPinName()))
		{
			if(!RecreatePinForVariable(EGPD_Output, OldPins, GetVariableOutputPinName()))
			{
				return;
			}
		}
		CreateOutputPinTooltip();
		CreatePinForSelf();
	}

	RestoreSplitPins(OldPins);
}



FText UK2Node_VariableSet::GetPropertyTooltip(FProperty const* VariableProperty)
{
	FText TextFormat;
	FFormatNamedArguments Args;

	bool const bHasLocalRepNotify = K2Node_VariableSetImpl::PropertyHasLocalRepNotify(VariableProperty);

	FName VarName = NAME_None;
	if (VariableProperty != nullptr)
	{
		if (bHasLocalRepNotify)
		{
			Args.Add(TEXT("ReplicationNotifyName"), FText::FromName(VariableProperty->RepNotifyFunc));
			TextFormat = LOCTEXT("SetVariableWithRepNotify_Tooltip", "Set the value of variable {VarName} and call {ReplicationNotifyName}");
		}

		VarName = VariableProperty->GetFName();

		UClass* SourceClass = VariableProperty->GetOwnerClass();
		// discover if the variable property is a non blueprint user variable
		bool const bIsNativeVariable = (SourceClass != nullptr) && (SourceClass->ClassGeneratedBy == nullptr);

		FText SubTooltip;
		if (bIsNativeVariable)
		{
			FText const PropertyTooltip = VariableProperty->GetToolTipText();
			if (!PropertyTooltip.IsEmpty())
			{
				// See if the native property has a tooltip
				SubTooltip = PropertyTooltip;
				FString TooltipName = FString::Printf(TEXT("%s.%s"), *VarName.ToString(), *FBlueprintMetadata::MD_Tooltip.ToString());
				FText::FindText(*VariableProperty->GetFullGroupName(true), *TooltipName, SubTooltip);
			}
		}
		else if (SourceClass)
		{
			if (UBlueprint* VarBlueprint = Cast<UBlueprint>(SourceClass->ClassGeneratedBy))
			{
				FString UserTooltipData;
				if (FBlueprintEditorUtils::GetBlueprintVariableMetaData(VarBlueprint, VarName, VariableProperty->GetOwnerStruct(), FBlueprintMetadata::MD_Tooltip, UserTooltipData))
				{
					SubTooltip = FText::FromString(UserTooltipData);
				}
			}
		}

		if (!SubTooltip.IsEmpty())
		{
			Args.Add(TEXT("PropertyTooltip"), SubTooltip);
			if (bHasLocalRepNotify)
			{
				TextFormat = LOCTEXT("SetVariablePropertyWithRepNotify_Tooltip", "Set the value of variable {VarName} and call {ReplicationNotifyName}\n{PropertyTooltip}");
			}
			else
			{
				TextFormat = LOCTEXT("SetVariableProperty_Tooltip", "Set the value of variable {VarName}\n{PropertyTooltip}");
			}
		}
	}

	if (TextFormat.IsEmpty())
	{
		return K2Node_VariableSetImpl::GetBaseTooltip(VarName);
	}
	else
	{
		Args.Add(TEXT("VarName"), FText::FromName(VarName));
		return FText::Format(TextFormat, Args);
	}
}

FText UK2Node_VariableSet::GetBlueprintVarTooltip(FBPVariableDescription const& VarDesc)
{
	int32 const MetaIndex = VarDesc.FindMetaDataEntryIndexForKey(FBlueprintMetadata::MD_Tooltip);
	bool const bHasTooltipData = (MetaIndex != INDEX_NONE);

	if (bHasTooltipData)
	{
		FString UserTooltipData = VarDesc.GetMetaData(FBlueprintMetadata::MD_Tooltip);

		FFormatNamedArguments Args;
		Args.Add(TEXT("VarName"), FText::FromName(VarDesc.VarName));
		Args.Add(TEXT("UserTooltip"), FText::FromString(UserTooltipData));

		return FText::Format(LOCTEXT("SetBlueprintVariable_Tooltip", "Set the value of variable {VarName}\n{UserTooltip}"), Args);
	}
	return K2Node_VariableSetImpl::GetBaseTooltip(VarDesc.VarName);
}

FText UK2Node_VariableSet::GetTooltipText() const
{
	if (CachedTooltip.IsOutOfDate(this))
	{
		if (FProperty* Property = GetPropertyForVariable())
		{
			CachedTooltip.SetCachedText(GetPropertyTooltip(Property), this);
		}
		else if (FBPVariableDescription const* VarDesc = GetBlueprintVarDescription())
		{
			CachedTooltip.SetCachedText(GetBlueprintVarTooltip(*VarDesc), this);
		}
		else
		{
			CachedTooltip.SetCachedText(K2Node_VariableSetImpl::GetBaseTooltip(GetVarName()), this);
		}
	}
	return CachedTooltip;
}

FText UK2Node_VariableSet::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	// If there is only one variable being written (one non-meta input pin), the title can be made the variable name
	FName InputPinName;
	int32 NumInputsFound = 0;

	for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
	{
		UEdGraphPin* Pin = Pins[PinIndex];
		if ((Pin->Direction == EGPD_Input) && (!K2Schema->IsMetaPin(*Pin)))
		{
			++NumInputsFound;
			InputPinName = Pin->PinName;
		}
	}

	if (NumInputsFound != 1)
	{
		return HasLocalRepNotify() ? NSLOCTEXT("K2Node", "SetWithNotify", "Set with Notify") : NSLOCTEXT("K2Node", "Set", "Set");
	}
	// @TODO: The variable name mutates as the user makes changes to the 
	//        underlying property, so until we can catch all those cases, we're
	//        going to leave this optimization off
	else if (CachedNodeTitle.IsOutOfDate(this))
	{
		FFormatNamedArguments Args;
		Args.Add(TEXT("PinName"), FText::FromName(InputPinName));

		// FText::Format() is slow, so we cache this to save on performance
		if (HasLocalRepNotify())
		{
			CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "SetWithNotifyPinName", "Set with Notify {PinName}"), Args), this);
		}
		else
		{
			CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "SetPinName", "Set {PinName}"), Args), this);
		}
	}
	return CachedNodeTitle;
}

/** Returns true if the variable we are setting has a RepNotify AND was defined in a blueprint
 *		The 'defined in a blueprint' is to avoid natively defined RepNotifies being called unintentionally.
 *		Most (all?) native rep notifies are intended to be client only. We are moving away from this paradigm in blueprints
 *		So for now this is somewhat of a hold over to avoid nasty bugs where a K2 set node is calling a native function that the
 *		designer has no idea what it is doing.
 */
bool UK2Node_VariableSet::HasLocalRepNotify() const
{
	return K2Node_VariableSetImpl::PropertyHasLocalRepNotify(GetPropertyForVariable());
}

bool UK2Node_VariableSet::ShouldFlushDormancyOnSet() const
{
	if (!GetVariableSourceClass()->IsChildOf(AActor::StaticClass()))
	{
		return false;
	}

	// Flush net dormancy before setting a replicated property
	FProperty *Property = FindFProperty<FProperty>(GetVariableSourceClass(), GetVarName());
	return (Property != NULL && (Property->PropertyFlags & CPF_Net));
}

bool UK2Node_VariableSet::IsNetProperty() const
{
	FProperty* Property = GetPropertyForVariable();
	return Property && (Property->PropertyFlags & CPF_Net);
}

FName UK2Node_VariableSet::GetRepNotifyName() const
{
	FProperty * Property = GetPropertyForVariable();
	if (Property)
	{
		return Property->RepNotifyFunc;
	}
	return NAME_None;
}


FNodeHandlingFunctor* UK2Node_VariableSet::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_VariableSet(CompilerContext);
}

FName UK2Node_VariableSet::GetVariableOutputPinName() const
{
	return TEXT("Output_Get");
}

void UK2Node_VariableSet::CreateOutputPinTooltip()
{
	UEdGraphPin* Pin = FindPin(GetVariableOutputPinName());
	check(Pin);
	Pin->PinToolTip = NSLOCTEXT("K2Node", "SetPinOutputTooltip", "Retrieves the value of the variable, can use instead of a separate Get node").ToString();
}

FText UK2Node_VariableSet::GetPinNameOverride(const UEdGraphPin& Pin) const
{
	// Stop the output pin for the variable, effectively the "get" pin, from displaying a name.
	if(Pin.ParentPin == nullptr && (Pin.Direction == EGPD_Output || Pin.PinType.PinCategory == UEdGraphSchema_K2::PC_Exec))
	{
		return FText::GetEmpty();
	}

	return !Pin.PinFriendlyName.IsEmpty() ? Pin.PinFriendlyName : FText::FromName(Pin.PinName);
}

void UK2Node_VariableSet::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const
{
	Super::ValidateNodeDuringCompilation(MessageLog);

	// Some expansions will create sets for non-blueprint visible properties, and we don't want to validate against that
	if (!IsIntermediateNode())
	{
		if (FProperty* Property = GetPropertyForVariable())
		{
			const FBlueprintEditorUtils::EPropertyWritableState PropertyWritableState = FBlueprintEditorUtils::IsPropertyWritableInBlueprint(GetBlueprint(), Property);

			if (PropertyWritableState != FBlueprintEditorUtils::EPropertyWritableState::Writable)
			{
				FFormatNamedArguments Args;
				if (UObject* Class = Property->GetOwner<UObject>())
				{
					Args.Add(TEXT("VariableName"), FText::AsCultureInvariant(FString::Printf(TEXT("%s.%s"), *Class->GetName(), *Property->GetName())));
				}
				else
				{
					Args.Add(TEXT("VariableName"), FText::AsCultureInvariant(Property->GetName()));
				}

				if (PropertyWritableState == FBlueprintEditorUtils::EPropertyWritableState::BlueprintReadOnly || PropertyWritableState == FBlueprintEditorUtils::EPropertyWritableState::NotBlueprintVisible)
				{
					MessageLog.Error(*FText::Format(LOCTEXT("UnableToSet_NotWritable", "{VariableName} is not blueprint writable. @@"), Args).ToString(), this);
				}
				else if (PropertyWritableState == FBlueprintEditorUtils::EPropertyWritableState::Private)
				{
					MessageLog.Error(*LOCTEXT("UnableToSet_ReadOnly", "{VariableName} is private and not accessible in this context. @@").ToString(), this);
				}
				else
				{
					check(false);
				}
			}
		}
	}
}
//节点展开,包含UK2Node_VariableGet节点
void UK2Node_VariableSet::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	Super::ExpandNode(CompilerContext, SourceGraph);

	if (CompilerContext.bIsFullCompile)
	{
		FProperty* VariableProperty = GetPropertyForVariable();

		const UEdGraphSchema_K2* K2Schema = CompilerContext.GetSchema();

		if (UEdGraphPin* VariableGetPin = FindPin(GetVariableOutputPinName()))
		{
			// If the output pin is linked, we need to spawn a separate "Get" node and hook it up.
			if (VariableGetPin->LinkedTo.Num())
			{
				if (VariableProperty)
				{
					UK2Node_VariableGet* VariableGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_VariableGet>(this, SourceGraph);
					VariableGetNode->VariableReference = VariableReference;
					VariableGetNode->AllocateDefaultPins();
					CompilerContext.MessageLog.NotifyIntermediateObjectCreation(VariableGetNode, this);
					CompilerContext.MovePinLinksToIntermediate(*VariableGetPin, *VariableGetNode->FindPin(GetVarName()));

					// Duplicate the connection to the self pin.
					UEdGraphPin* SetSelfPin = K2Schema->FindSelfPin(*this, EGPD_Input);
					UEdGraphPin* GetSelfPin = K2Schema->FindSelfPin(*VariableGetNode, EGPD_Input);
					if (SetSelfPin && GetSelfPin)
					{
						CompilerContext.CopyPinLinksToIntermediate(*SetSelfPin, *GetSelfPin);
					}
				}
			}
			Pins.Remove(VariableGetPin);
			VariableGetPin->MarkPendingKill();
		}

		// If property has a BlueprintSetter accessor, then replace the variable get node with a call function
		if (VariableProperty)
		{
			const FString& SetFunctionName = VariableProperty->GetMetaData(FBlueprintMetadata::MD_PropertySetFunction);
			if (!SetFunctionName.IsEmpty())
			{
				UClass* OwnerClass = VariableProperty->GetOwnerClass();
				UFunction* SetFunction = OwnerClass->FindFunctionByName(*SetFunctionName);
				check(SetFunction);

				UK2Node_CallFunction* CallFuncNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
				CallFuncNode->SetFromFunction(SetFunction);
				CallFuncNode->AllocateDefaultPins();

				// Move Exec pin connections
				CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFuncNode->GetExecPin());

				// Move Then pin connections
				CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PN_Then, EGPD_Output), *CallFuncNode->GetThenPin());
				
				// Move Self pin connections
				if (UEdGraphPin* SetSelfPin = K2Schema->FindSelfPin(*this, EGPD_Input))
				{
					CompilerContext.MovePinLinksToIntermediate(*SetSelfPin, *K2Schema->FindSelfPin(*CallFuncNode, EGPD_Input));
				}

				// Move Value pin connections
				UEdGraphPin* SetFunctionValuePin = nullptr;
				for (UEdGraphPin* CallFuncPin : CallFuncNode->Pins)
				{
					if (!K2Schema->IsMetaPin(*CallFuncPin))
					{
						check(CallFuncPin->Direction == EGPD_Input);
						SetFunctionValuePin = CallFuncPin;
						break;
					}
				}
				check(SetFunctionValuePin);

				CompilerContext.MovePinLinksToIntermediate(*FindPin(GetVarName(), EGPD_Input), *SetFunctionValuePin);
			}
		}
	}

}

#undef LOCTEXT_NAMESPACE

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shandongwill

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值