文章目录
- Set节点说明
- 相关源码
Set节点说明
UE蓝图中的Set节点是用于对变量进行赋值操作的重要组件。它具有多种功能和作用,具体如下:
- 变量赋值:Set节点可以用于设置不同类型的变量值,包括整数、浮点数、布尔值、字符串等。在游戏开发中,开发者经常需要修改或设置各种变量的值,如角色的生命值、位置、游戏的状态等。Set节点提供了一个直观且简单的方式来完成这些赋值操作。
- 简化开发流程:通过使用Set节点,开发者无需编写复杂的代码逻辑,便可以在蓝图中直接进行变量赋值操作,从而简化了游戏开发的过程,提高了开发效率。
- 可视化操作:Set节点具有直观的可视化界面,开发者可以通过拖拽连接线来连接Set节点与其他节点,这样就能够建立变量之间的关联关系。这种可视化的操作方式使得逻辑流程更加清晰明了,便于理解和调试。
- 自动类型转换:Set节点还支持自动类型转换功能,可以在不同类型之间进行转换,并自动处理类型兼容性问题。这减少了开发者在处理类型转换问题时的烦恼。
- 动态修改变量:使用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 funcif ((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 variablebool 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 tooltipSubTooltip = 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 nameFName 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 offelse if (CachedNodeTitle.IsOutOfDate(this)){FFormatNamedArguments Args;Args.Add(TEXT("PinName"), FText::FromName(InputPinName));// FText::Format() is slow, so we cache this to save on performanceif (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 propertyFProperty *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 thatif (!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 functionif (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 connectionsCompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFuncNode->GetExecPin());// Move Then pin connectionsCompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PN_Then, EGPD_Output), *CallFuncNode->GetThenPin());// Move Self pin connectionsif (UEdGraphPin* SetSelfPin = K2Schema->FindSelfPin(*this, EGPD_Input)){CompilerContext.MovePinLinksToIntermediate(*SetSelfPin, *K2Schema->FindSelfPin(*CallFuncNode, EGPD_Input));}// Move Value pin connectionsUEdGraphPin* 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