概要

UE Get节点在Unreal Engine的蓝图系统中用于获取变量的值。这个节点通常用于从变量中读取数据,以便在游戏的逻辑流程中使用。

要使用Get节点,你首先需要有一个已经定义的变量。然后,在蓝图编辑器中,你可以通过右键点击空白处,选择“添加节点” -> “变量” -> “Get”来创建一个Get节点。在弹出的窗口中,你可以选择需要获取的变量。一旦创建了Get节点,你可以将其拖拽到蓝图的适当位置,并连接它到需要使用该变量值的其他节点。
UE Get节点和源码-LMLPHP

需要注意的是,Get节点只是获取变量的值,而不会修改它。如果你想修改变量的值,你应该使用Set节点。

Get节点的输出可以用于驱动蓝图中的其他逻辑或操作。例如,你可以使用Get节点获取一个游戏对象的位置,然后根据这个位置来执行某些动作或决策。

总的来说,UE Get节点是Unreal Engine蓝图系统中用于读取变量值的重要工具,它使得开发者能够灵活地获取和使用游戏中的数据。

UE Get节点有哪些常见的应用场景

UE Get节点在Unreal Engine的蓝图系统中主要用于获取变量的值,这些变量可以是游戏对象的状态、属性、配置参数等。由于Get节点能够动态地获取变量的值,因此它在多种场景中都有广泛的应用。以下是一些UE Get节点常见的应用场景:

动态调整游戏对象的行为:通过Get节点获取游戏对象的某些属性(如位置、速度、旋转等),然后根据这些属性的值动态调整游戏对象的行为。例如,根据玩家的位置调整敌人的攻击策略,或者根据物体的速度调整其移动方式。
实现条件判断和分支逻辑:结合Branch节点或其他控制流节点,使用Get节点获取变量的值来进行条件判断。根据判断结果,执行不同的逻辑分支。这在构建复杂的游戏逻辑和交互中非常常见。
同步多个游戏对象的状态:在某些情况下,你可能需要确保多个游戏对象保持同步的状态。通过Get节点获取一个游戏对象的状态,并使用Set节点将其应用到其他游戏对象上,可以实现状态的同步和一致性。
响应用户输入和事件:在事件驱动的游戏逻辑中,Get节点可以用于获取用户输入或事件触发的变量值。例如,当用户按下某个按钮时,通过Get节点获取按钮状态的值,并据此执行相应的操作或响应。
配置管理和动态加载:在游戏开发过程中,可能需要根据不同的配置或参数来调整游戏的行为。通过Get节点获取配置文件或参数表中的变量值,可以实现游戏行为的动态加载和调整。
需要注意的是,虽然Get节点在多种场景中都有应用,但在使用时也需要注意数据的时效性和准确性。确保你获取的变量值是最新且正确的,以避免逻辑错误或不一致的行为。

总的来说,UE Get节点是Unreal Engine蓝图系统中非常重要的一个节点类型,它使得开发者能够灵活地获取游戏中的数据,并根据这些数据实现各种复杂的逻辑和交互。

相关源码

源码文件:
K2Node_VariableGet.h
K2Node_VariableGet.cpp
相关类:
FKCHandler_VariableGet
K2Node_VariableGetImpl

#include "K2Node_VariableGet.h"
#include "UObject/UObjectHash.h"
#include "UObject/PropertyPortFlags.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Framework/Commands/UIAction.h"
#include "ToolMenus.h"
#include "EdGraph/EdGraph.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_CallFunction.h"
#include "K2Node_IfThenElse.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompilerMisc.h"
#include "KismetCompiler.h"
#include "ScopedTransaction.h"

//
// FKCHandler_VariableGet

#define LOCTEXT_NAMESPACE "K2Node"

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

	virtual void RegisterNet(FKismetFunctionContext& Context, UEdGraphPin* Net) override
	{
		// This net is a variable read
		ResolveAndRegisterScopedTerm(Context, Net, Context.VariableReferences);
	}

	virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
	{
		UK2Node_Variable* VarNode = Cast<UK2Node_Variable>(Node);
		if (VarNode)
		{
			VarNode->CheckForErrors(CompilerContext.GetSchema(), Context.MessageLog);

			// Report an error that the local variable could not be found
			if(VarNode->VariableReference.IsLocalScope() && VarNode->GetPropertyForVariable() == NULL)
			{
				FFormatNamedArguments Args;
				Args.Add(TEXT("VariableName"), FText::FromName(VarNode->VariableReference.GetMemberName()));

				if(VarNode->VariableReference.GetMemberScopeName() != Context.Function->GetName())
				{
					Args.Add(TEXT("ScopeName"), FText::FromString(VarNode->VariableReference.GetMemberScopeName()));
					CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("LocalVariableNotFoundInScope_Error", "Unable to find local variable with name '{VariableName}' for @@, scope expected: @@, scope found: {ScopeName}"), Args).ToString(), Node, Node->GetGraph());
				}
				else
				{
					CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("LocalVariableNotFound_Error", "Unable to find local variable with name '{VariableName}' for @@"), Args).ToString(), Node);
				}
			}
		}

		FNodeHandlingFunctor::RegisterNets(Context, Node);
	}
};

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

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

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

}

UK2Node_VariableGet::UK2Node_VariableGet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bIsPureGet(true)
{
}

void UK2Node_VariableGet::CreateNonPurePins(TArray<UEdGraphPin*>* InOldPinsPtr)
{
	const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema());
	check(K2Schema != nullptr);
	if (!K2Schema->DoesGraphSupportImpureFunctions(GetGraph()))
	{
		bIsPureGet = true;
	}

	if (!bIsPureGet)
	{
		FEdGraphPinType PinType;
		FProperty* VariableProperty = GetPropertyForVariable();

		// We need the pin's type, to both see if it's an array and if it is of the correct types to remain an impure node
		if (VariableProperty)
		{
			K2Schema->ConvertPropertyToPinType(GetPropertyForVariable(), PinType);
		}
		// If there is no property and we are given some old pins to look at, find the old value pin and use the type there
		// This allows nodes to be pasted into other BPs without access to the property
		else if (InOldPinsPtr)
		{
			// find old variable pin and use the type.
			const FName PinName = GetVarName();
			for (const UEdGraphPin* Pin : *InOldPinsPtr)
			{
				if (Pin && PinName == Pin->PinName)
				{
					PinType = Pin->PinType;
					break;
				}
			}

		}

		if (IsValidTypeForNonPure(PinType))
		{
			// Input - Execution Pin
			CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);

			// Output - Execution Pins
			UEdGraphPin* ValidPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
			ValidPin->PinFriendlyName = LOCTEXT("Valid", "Is Valid");

			UEdGraphPin* InvalidPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Else);
			InvalidPin->PinFriendlyName = LOCTEXT("Invalid", "Is Not Valid");
		}
		else
		{
			bIsPureGet = true;
		}
	}
}

void UK2Node_VariableGet::AllocateDefaultPins()
{
	if(GetVarName() != NAME_None)
	{
		CreateNonPurePins(nullptr);

		if(CreatePinForVariable(EGPD_Output))
		{
			CreatePinForSelf();
		}
	}

	Super::AllocateDefaultPins();
}

void UK2Node_VariableGet::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
	if(GetVarName() != NAME_None)
	{
		CreateNonPurePins(&OldPins);

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

		RestoreSplitPins(OldPins);
	}
}

FText UK2Node_VariableGet::GetPropertyTooltip(FProperty const* VariableProperty)
{
	FName VarName = NAME_None;
	if (VariableProperty != nullptr)
	{
		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())
		{
			FFormatNamedArguments Args;
			Args.Add(TEXT("VarName"), FText::FromName(VarName));
			Args.Add(TEXT("PropertyTooltip"), SubTooltip);

			return FText::Format(LOCTEXT("GetVariableProperty_Tooltip", "Read the value of variable {VarName}\n{PropertyTooltip}"), Args);
		}
	}
	return K2Node_VariableGetImpl::GetBaseTooltip(VarName);
}

FText UK2Node_VariableGet::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("GetBlueprintVariable_Tooltip", "Read the value of variable {VarName}\n{UserTooltip}"), Args);
	}
	return K2Node_VariableGetImpl::GetBaseTooltip(VarDesc.VarName);
}

FText UK2Node_VariableGet::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_VariableGetImpl::GetBaseTooltip(GetVarName()), this);
		}
	}
	return CachedTooltip;
}

FText UK2Node_VariableGet::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	// If there is only one variable being read, the title can be made the variable name
	FName OutputPinName;
	int32 NumOutputsFound = 0;

	for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
	{
		UEdGraphPin* Pin = Pins[PinIndex];

		// The following code is to attempt to log info related to UE-19729
		if (TitleType == ENodeTitleType::ListView)
		{
			if (UEdGraph* Graph = Cast<UEdGraph>(GetOuter()))
			{
				FString VariableName = GetVarNameString();
				FString BlueprintPath = FBlueprintEditorUtils::FindBlueprintForGraph(Graph)->GetPathName();
				FString SetupStyle = bIsPureGet? TEXT("pure") : TEXT("validated");
				FString VariableResolves = (VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()) != nullptr)? TEXT("resolves") : TEXT("does not resolve");
				checkf(Pin, TEXT("Get node for variable '%s' in Blueprint '%s' which is setup as %s and has %d pins. Variable %s"), *VariableName, *BlueprintPath, *SetupStyle, Pins.Num(), *VariableResolves);
			}
		}

		if (Pin->Direction == EGPD_Output)
		{
			++NumOutputsFound;
			OutputPinName = Pin->PinName;
		}
	}

	if (NumOutputsFound != 1)
	{
		return LOCTEXT("Get", "Get");
	}
	else if (CachedNodeTitle.IsOutOfDate(this))
	{
		FFormatNamedArguments Args;
		Args.Add(TEXT("PinName"), FText::FromName(OutputPinName));
		// FText::Format() is slow, so we cache this to save on performance
		CachedNodeTitle.SetCachedText(FText::Format(LOCTEXT("GetPinName", "Get {PinName}"), Args), this);
	}
	return CachedNodeTitle;
}

FNodeHandlingFunctor* UK2Node_VariableGet::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_VariableGet(CompilerContext);
}

bool UK2Node_VariableGet::IsValidTypeForNonPure(const FEdGraphPinType& InPinType)
{
	return !InPinType.IsContainer() && (InPinType.PinCategory == UEdGraphSchema_K2::PC_Object || InPinType.PinCategory == UEdGraphSchema_K2::PC_Class || InPinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject || InPinType.PinCategory == UEdGraphSchema_K2::PC_SoftClass);
}

void UK2Node_VariableGet::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
{
	Super::GetNodeContextMenuActions(Menu, Context);

	const UEdGraphPin* ValuePin = GetValuePin();
	if (ValuePin && IsValidTypeForNonPure(ValuePin->PinType))
	{
		{
			FText MenuEntryTitle;
			FText MenuEntryTooltip;

			bool bCanTogglePurity = true;
			auto CanExecutePurityToggle = [](bool const bInCanTogglePurity)->bool
			{
				return bInCanTogglePurity;
			};

			if (bIsPureGet)
			{
				MenuEntryTitle   = LOCTEXT("ConvertToImpureGetTitle",   "Convert to Validated Get");
				MenuEntryTooltip = LOCTEXT("ConvertToImpureGetTooltip", "Adds in branching execution pins so that you can separately handle when the returned value is valid/invalid.");

				const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema());
				check(K2Schema != nullptr);

				bCanTogglePurity = K2Schema->DoesGraphSupportImpureFunctions(GetGraph());
				if (!bCanTogglePurity)
				{
					MenuEntryTooltip = LOCTEXT("CannotMakeImpureGetTooltip", "This graph does not support impure calls!");
				}
			}
			else
			{
				MenuEntryTitle   = LOCTEXT("ConvertToPureGetTitle",   "Convert to pure Get");
				MenuEntryTooltip = LOCTEXT("ConvertToPureGetTooltip", "Removes the execution pins to make the node more versatile.");
			}

			FToolMenuSection& Section = Menu->AddSection("K2NodeVariableGet", LOCTEXT("VariableGetHeader", "Variable Get"));
			Section.AddMenuEntry(
				"TogglePurity",
				MenuEntryTitle,
				MenuEntryTooltip,
				FSlateIcon(),
				FUIAction(
				FExecuteAction::CreateUObject(const_cast<UK2Node_VariableGet*>(this), &UK2Node_VariableGet::TogglePurity),
				FCanExecuteAction::CreateStatic(CanExecutePurityToggle, bCanTogglePurity && !Context->bIsDebugging),
				FIsActionChecked()
				)
				);
		}
	}
}

void UK2Node_VariableGet::TogglePurity()
{
	FText TransactionTitle;
	if(!bIsPureGet)
	{
		TransactionTitle = LOCTEXT("TogglePureGet", "Convert to Pure Get");
	}
	else
	{
		TransactionTitle = LOCTEXT("ToggleImpureGet", "Convert to Impure Get");
	}
	const FScopedTransaction Transaction( TransactionTitle );
	Modify();

	SetPurity(!bIsPureGet);
}

void UK2Node_VariableGet::SetPurity(bool bNewPurity)
{
	if (bNewPurity != bIsPureGet)
	{
		bIsPureGet = bNewPurity;

		bool const bHasBeenConstructed = (Pins.Num() > 0);
		if (bHasBeenConstructed)
		{
			ReconstructNode();
		}
	}
}

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

	// Some expansions, such as timelines, will create gets for non-blueprint visible properties, and we don't want to validate against that
	if (!IsIntermediateNode())
	{
		if (FProperty* Property = GetPropertyForVariable())
		{
			const FBlueprintEditorUtils::EPropertyReadableState PropertyReadableState = FBlueprintEditorUtils::IsPropertyReadableInBlueprint(GetBlueprint(), Property);

			if (PropertyReadableState != FBlueprintEditorUtils::EPropertyReadableState::Readable)
			{
				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 (PropertyReadableState == FBlueprintEditorUtils::EPropertyReadableState::NotBlueprintVisible)
				{
					// UE_DEPRECATED(4.17) ... make this an error
					MessageLog.Warning(*FText::Format(LOCTEXT("UnableToGet_NotVisible", "{VariableName} is not blueprint visible (BlueprintReadOnly or BlueprintReadWrite). Please fix mark up or cease accessing as this will be made an error in a future release. @@"), Args).ToString(), this);
				}
				else if (PropertyReadableState == FBlueprintEditorUtils::EPropertyReadableState::Private)
				{
					// UE_DEPRECATED(4.17) ... make this an error
					MessageLog.Warning(*FText::Format(LOCTEXT("UnableToGet_ReadOnly", "{VariableName} is private and not accessible in this context. Please fix mark up or cease accessing as this will be an error in a future release. @@"), Args).ToString(), this);
				}
				else
				{
					check(false);
				}
			}
		}
	}
}

void UK2Node_VariableGet::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	Super::ExpandNode(CompilerContext, SourceGraph);

	FProperty* VariableProperty = GetPropertyForVariable();

	UK2Node_VariableGet* VariableGetNode = this;

	// Do not attempt to expand the node when not a pure get nor when there is no property. Normal compilation error detection will detect the missing property.
	if (!bIsPureGet && VariableProperty)
	{
		UEdGraphPin* ValuePin = GetValuePin();

		// Impure Get nodes convert into three nodes:
		// 1. A pure Get node
		// 2. An IsValid node
		// 3. A Branch node (only impure part)
		
		// Create the pure Get node
		VariableGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_VariableGet>(this, SourceGraph);
		VariableGetNode->VariableReference = VariableReference;
		VariableGetNode->AllocateDefaultPins();
		CompilerContext.MessageLog.NotifyIntermediateObjectCreation(VariableGetNode, this);

		// Move pin links from Get node we are expanding, to the new pure one we've created
		CompilerContext.MovePinLinksToIntermediate(*ValuePin, *VariableGetNode->GetValuePin());
		if (!VariableReference.IsLocalScope())
		{
			CompilerContext.MovePinLinksToIntermediate(*FindPin(UEdGraphSchema_K2::PN_Self), *VariableGetNode->FindPin(UEdGraphSchema_K2::PN_Self));
		}

		// Create the IsValid node
		UK2Node_CallFunction* IsValidFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);

		// Based on if the type is an "Object" or a "Class" changes which function to use
		if (ValuePin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
		{
			IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValid)));
		}
		else if (ValuePin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class)
		{
			IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValidClass)));
		}
		else if (ValuePin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject)
		{
			IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValidSoftObjectReference)));
		}
		else if (ValuePin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftClass)
		{
			IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValidSoftClassReference)));
		}
		IsValidFunction->AllocateDefaultPins();
		CompilerContext.MessageLog.NotifyIntermediateObjectCreation(IsValidFunction, this);

		// Connect the value pin from the new Get node to the IsValid
		UEdGraphPin* ObjectPin = IsValidFunction->Pins[1];
		check(ObjectPin->Direction == EGPD_Input);
		ObjectPin->MakeLinkTo(VariableGetNode->GetValuePin());

		// Create the Branch node
		UK2Node_IfThenElse* BranchNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);
		BranchNode->AllocateDefaultPins();
		CompilerContext.MessageLog.NotifyIntermediateObjectCreation(BranchNode, this);

		// Connect the bool output pin from IsValid node to the Branch node
		UEdGraphPin* BoolPin = IsValidFunction->Pins[2];
		check(BoolPin->Direction == EGPD_Output);
		BoolPin->MakeLinkTo(BranchNode->GetConditionPin());

		// Connect the Branch node to the input of the impure Get node
		CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BranchNode->GetExecPin());

		// Move the two Branch pins to the Branch node
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UEdGraphSchema_K2::PN_Then), *BranchNode->FindPin(UEdGraphSchema_K2::PN_Then));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UEdGraphSchema_K2::PN_Else), *BranchNode->FindPin(UEdGraphSchema_K2::PN_Else));

		BreakAllNodeLinks();
	}

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

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

			const UEdGraphSchema_K2* K2Schema = CompilerContext.GetSchema();

			// Move Self pin connections
			CompilerContext.MovePinLinksToIntermediate(*K2Schema->FindSelfPin(*this, EGPD_Input), *K2Schema->FindSelfPin(*CallFuncNode, EGPD_Input));

			// Move Value pin connections
			CompilerContext.MovePinLinksToIntermediate(*GetValuePin(), *CallFuncNode->GetReturnValuePin());
		}
	}
}

void UK2Node_VariableGet::Serialize(FArchive& Ar)
{
	// The following code is to attempt to log info related to UE-19729
	if (Ar.IsSaving() && Ar.IsPersistent())
	{
		uint32 PortFlagsToSkip = PPF_Duplicate | PPF_DuplicateForPIE;
		if (!(Ar.GetPortFlags() & PortFlagsToSkip))
		{
			if (UEdGraph* Graph = Cast<UEdGraph>(GetOuter()))
			{
				if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph))
				{
					if (!Blueprint->bBeingCompiled)
					{
						// The following line may spur the crash noted in UE-19729 and will confirm that the crash happens before the FiB gather.
						GetNodeTitle(ENodeTitleType::ListView);
					}
				}
			}
		}
	}
	Super::Serialize(Ar);
}


#undef LOCTEXT_NAMESPACE

02-17 23:00