2020-11-19 12:03:52 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "BlueprintEditorLibrary.h"
# include "BlueprintEditorLibraryModule.h"
# include "Kismet2/BlueprintEditorUtils.h"
# include "EdGraph/EdGraph.h"
# include "EdGraph/EdGraphNode.h"
# include "EdGraphSchema_K2.h"
# include "EdGraphNode_Comment.h"
# include "EdGraphUtilities.h"
# include "BlueprintTypePromotion.h"
# include "K2Node_PromotableOperator.h"
# include "K2Node_CommutativeAssociativeBinaryOperator.h"
# include "Kismet2/KismetEditorUtilities.h"
# include "BlueprintNodeSpawner.h"
# include "EdGraphUtilities.h"
2020-12-18 14:24:31 -04:00
# include "AnimGraphNode_Base.h"
2021-01-05 11:17:06 -04:00
# include "Components/TimelineComponent.h"
2021-01-25 18:41:59 -04:00
# include "Kismet2/Kismet2NameValidators.h"
2021-02-05 15:06:57 -04:00
# include "Subsystems/AssetEditorSubsystem.h"
2021-01-05 11:17:06 -04:00
# define LOCTEXT_NAMESPACE "BlueprintEditorLibrary"
2020-11-19 12:03:52 -04:00
DEFINE_LOG_CATEGORY ( LogBlueprintEditorLib ) ;
///////////////////////////////////////////////////////////
// InternalBlueprintEditorLibrary
namespace InternalBlueprintEditorLibrary
{
/**
* Replace the OldNode with the NewNode and reconnect it ' s pins . If the pins don ' t
* exist on the NewNode , then orphan the connections .
*
* @ param OldNode The old node to replace
* @ param NewNode The new node to put in the old node ' s place
*/
2021-04-14 15:04:17 -04:00
static bool ReplaceOldNodeWithNew ( UEdGraphNode * OldNode , UEdGraphNode * NewNode )
2020-11-19 12:03:52 -04:00
{
2020-12-01 16:26:40 -04:00
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
2021-04-14 15:04:17 -04:00
bool bSuccess = false ;
2020-11-19 12:03:52 -04:00
if ( Schema & & OldNode & & NewNode )
{
TMap < FName , FName > OldToNewPinMap ;
for ( UEdGraphPin * Pin : OldNode - > Pins )
{
if ( Pin - > ParentPin ! = nullptr )
{
// ReplaceOldNodeWithNew() will take care of mapping split pins (as long as the parents are properly mapped)
continue ;
}
else if ( Pin - > PinName = = UEdGraphSchema_K2 : : PN_Self )
{
// there's no analogous pin, signal that we're expecting this
OldToNewPinMap . Add ( Pin - > PinName , NAME_None ) ;
}
else
{
// The input pins follow the same naming scheme
OldToNewPinMap . Add ( Pin - > PinName , Pin - > PinName ) ;
}
}
2021-04-14 15:04:17 -04:00
bSuccess = Schema - > ReplaceOldNodeWithNew ( OldNode , NewNode , OldToNewPinMap ) ;
2020-11-20 15:36:26 -04:00
// reconstructing the node will clean up any
// incorrect default values that may have been copied over
2021-04-14 15:04:17 -04:00
NewNode - > ReconstructNode ( ) ;
2020-11-19 12:03:52 -04:00
}
2021-04-14 15:04:17 -04:00
return bSuccess ;
2020-11-19 12:03:52 -04:00
}
2020-12-18 14:24:31 -04:00
/**
* Returns true if any of these nodes pins have any links . Does not check for
* a default value on pins
*
* @ param Node The node to check
*
* @ return bool True if the node has any links , false otherwise .
*/
static bool NodeHasAnyConnections ( const UEdGraphNode * Node )
{
if ( Node )
{
for ( const UEdGraphPin * Pin : Node - > Pins )
{
if ( Pin & & Pin - > LinkedTo . Num ( ) > 0 )
{
return true ;
}
}
}
return false ;
}
2021-02-05 15:06:57 -04:00
/**
* Attempt to close any open editors that may be relevant to this blueprint . This will prevent any
* problems where the user could see a previously deleted node / graph .
*
* @ param Blueprint The blueprint that is being edited
*/
static void CloseOpenEditors ( UBlueprint * Blueprint )
{
UAssetEditorSubsystem * AssetSubsystem = GEditor ? GEditor - > GetEditorSubsystem < UAssetEditorSubsystem > ( ) : nullptr ;
if ( AssetSubsystem & & Blueprint )
{
AssetSubsystem - > CloseAllEditorsForAsset ( Blueprint ) ;
}
}
2020-11-19 12:03:52 -04:00
} ;
///////////////////////////////////////////////////////////
// UBlueprintEditorLibrary
UBlueprintEditorLibrary : : UBlueprintEditorLibrary ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
}
2021-01-07 16:41:11 -04:00
void UBlueprintEditorLibrary : : ReplaceVariableReferences ( UBlueprint * Blueprint , const FName OldVarName , const FName NewVarName )
2020-11-19 12:03:52 -04:00
{
2021-01-07 16:41:11 -04:00
if ( ! Blueprint | | OldVarName . IsNone ( ) | | NewVarName . IsNone ( ) )
2020-11-19 12:03:52 -04:00
{
return ;
}
FBlueprintEditorUtils : : RenameVariableReferences ( Blueprint , Blueprint - > GeneratedClass , OldVarName , NewVarName ) ;
}
UEdGraph * UBlueprintEditorLibrary : : FindEventGraph ( UBlueprint * Blueprint )
{
return Blueprint ? FBlueprintEditorUtils : : FindEventGraph ( Blueprint ) : nullptr ;
}
UEdGraph * UBlueprintEditorLibrary : : FindGraph ( UBlueprint * Blueprint , FName GraphName )
{
2021-01-07 16:27:41 -04:00
if ( Blueprint & & ! GraphName . IsNone ( ) )
2020-11-19 12:03:52 -04:00
{
2021-01-07 16:27:41 -04:00
TArray < UEdGraph * > AllGraphs ;
Blueprint - > GetAllGraphs ( AllGraphs ) ;
for ( UEdGraph * CurrentGraph : AllGraphs )
2020-11-19 12:03:52 -04:00
{
if ( CurrentGraph - > GetFName ( ) = = GraphName )
{
return CurrentGraph ;
}
}
}
return nullptr ;
}
UK2Node_PromotableOperator * CreateOpNode ( const FName OpName , UEdGraph * Graph , const int32 AdditionalPins )
{
2020-11-20 15:36:26 -04:00
if ( ! Graph )
{
return nullptr ;
}
2020-11-19 12:03:52 -04:00
// The spawner will be null if type promo isn't enabled
if ( UBlueprintFunctionNodeSpawner * Spawner = FTypePromotion : : GetOperatorSpawner ( OpName ) )
{
// Spawn a new node!
IBlueprintNodeBinder : : FBindingSet Bindings ;
FVector2D SpawnLoc { } ;
UK2Node_PromotableOperator * NewOpNode = Cast < UK2Node_PromotableOperator > ( Spawner - > Invoke ( Graph , Bindings , SpawnLoc ) ) ;
2020-11-20 15:36:26 -04:00
check ( NewOpNode ) ;
2020-11-19 12:03:52 -04:00
// Add the necessary number of additional pins
for ( int32 i = 0 ; i < AdditionalPins ; + + i )
{
NewOpNode - > AddInputPin ( ) ;
}
return NewOpNode ;
}
return nullptr ;
}
void UBlueprintEditorLibrary : : UpgradeOperatorNodes ( UBlueprint * Blueprint )
{
if ( ! Blueprint )
{
return ;
}
2021-04-14 15:04:17 -04:00
if ( ! TypePromoDebug : : IsTypePromoEnabled ( ) )
2020-11-19 12:03:52 -04:00
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Type Promotion is not enabled! Cannot upgrade operator nodes. Set 'BP.TypePromo.IsEnabled' to true and try again. " ) ) ;
return ;
}
TArray < UEdGraph * > AllGraphs ;
Blueprint - > GetAllGraphs ( AllGraphs ) ;
Blueprint - > Modify ( ) ;
2021-04-15 18:15:07 -04:00
/**
* Used to help us restore the default values of any pins that may have changed their types
* during replacement .
*/
struct FRestoreDefaultsHelper
{
FEdGraphPinType PinType { } ;
FString DefaultValue = TEXT ( " " ) ;
TObjectPtr < UObject > DefaultObject = nullptr ;
FText DefaultTextValue = FText : : GetEmpty ( ) ;
} ;
TMap < FName , FRestoreDefaultsHelper > PinTypeMap ;
2020-11-19 12:03:52 -04:00
for ( UEdGraph * Graph : AllGraphs )
{
2020-11-20 15:36:26 -04:00
check ( Graph ) ;
2020-11-19 12:03:52 -04:00
Graph - > Modify ( ) ;
for ( int32 i = Graph - > Nodes . Num ( ) - 1 ; i > = 0 ; - - i )
{
2021-04-15 18:15:07 -04:00
PinTypeMap . Reset ( ) ;
2020-11-19 12:03:52 -04:00
// Not every function that we want to upgrade is a CommunicativeBinaryOpNode
2020-11-20 15:36:26 -04:00
// Some are just regular CallFunction nodes; Vector + Float is an example of this
2020-11-19 12:03:52 -04:00
if ( UK2Node_CallFunction * OldOpNode = Cast < UK2Node_CallFunction > ( Graph - > Nodes [ i ] ) )
{
UFunction * Func = OldOpNode - > GetTargetFunction ( ) ;
2020-11-20 15:36:26 -04:00
UEdGraph * OwningGraph = OldOpNode - > GetGraph ( ) ;
2021-04-14 15:04:17 -04:00
const bool bHadAnyConnections = InternalBlueprintEditorLibrary : : NodeHasAnyConnections ( OldOpNode ) ;
2020-11-19 12:03:52 -04:00
2020-11-20 15:36:26 -04:00
// We should only be modifying nodes within the graph that we want
ensure ( OwningGraph = = Graph ) ;
2020-11-19 12:03:52 -04:00
// Don't bother with non-promotable functions or things that are already promotable operators
2020-12-16 20:30:27 -04:00
if ( ! FTypePromotion : : IsFunctionPromotionReady ( Func ) | | OldOpNode - > IsA < UK2Node_PromotableOperator > ( ) )
2020-11-19 12:03:52 -04:00
{
continue ;
}
2021-04-15 18:15:07 -04:00
// Keep track of the types of anything with a default value so they can be restored
for ( UEdGraphPin * Pin : OldOpNode - > Pins )
{
if ( Pin - > Direction = = EGPD_Input & & Pin - > LinkedTo . IsEmpty ( ) )
{
FRestoreDefaultsHelper RestoreData ;
RestoreData . PinType = Pin - > PinType ;
RestoreData . DefaultValue = Pin - > DefaultValue ;
RestoreData . DefaultObject = Pin - > DefaultObject ;
RestoreData . DefaultTextValue = Pin - > DefaultTextValue ;
PinTypeMap . Add ( Pin - > GetFName ( ) , RestoreData ) ;
}
}
2020-11-19 12:03:52 -04:00
FName OpName = FTypePromotion : : GetOpNameFromFunction ( Func ) ;
UK2Node_CommutativeAssociativeBinaryOperator * BinaryOpNode = Cast < UK2Node_CommutativeAssociativeBinaryOperator > ( OldOpNode ) ;
// Spawn a new node!
UK2Node_PromotableOperator * NewOpNode = CreateOpNode (
OpName ,
2020-11-20 15:36:26 -04:00
OwningGraph ,
2020-11-19 12:03:52 -04:00
BinaryOpNode ? BinaryOpNode - > GetNumberOfAdditionalInputs ( ) : 0
) ;
// If there is a node that is a communicative op node but is not promotable
// then the node will be null
if ( ! NewOpNode )
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Failed to spawn new operator node! " ) ) ;
continue ;
}
NewOpNode - > NodePosX = OldOpNode - > NodePosX ;
NewOpNode - > NodePosY = OldOpNode - > NodePosY ;
InternalBlueprintEditorLibrary : : ReplaceOldNodeWithNew ( OldOpNode , NewOpNode ) ;
2021-04-14 15:04:17 -04:00
2021-04-15 18:15:07 -04:00
for ( const TPair < FName , FRestoreDefaultsHelper > & Pair : PinTypeMap )
{
const FRestoreDefaultsHelper & OldPinData = Pair . Value ;
if ( UEdGraphPin * Pin = NewOpNode - > FindPin ( Pair . Key ) )
{
if ( NewOpNode - > CanConvertPinType ( Pin ) )
{
NewOpNode - > ConvertPinType ( Pin , OldPinData . PinType ) ;
Pin - > DefaultValue = OldPinData . DefaultValue ;
Pin - > DefaultObject = OldPinData . DefaultObject ;
Pin - > DefaultTextValue = OldPinData . DefaultTextValue ;
}
}
}
2021-04-14 15:04:17 -04:00
// Reset the new node to be wild card if there were no connections to the original node.
// This is necessary because replacing the old node will attempt to reconcile any
// default values on the node, which can result in incorrect pin types and a default
// value that doesn't match.
if ( ! bHadAnyConnections )
{
NewOpNode - > ResetNodeToWildcard ( ) ;
}
2020-11-19 12:03:52 -04:00
}
}
}
}
void UBlueprintEditorLibrary : : CompileBlueprint ( UBlueprint * Blueprint )
{
if ( Blueprint )
{
// Skip saving this to avoid possible tautologies when saving and allow the user to manually save
EBlueprintCompileOptions Flags = EBlueprintCompileOptions : : SkipSave ;
FKismetEditorUtilities : : CompileBlueprint ( Blueprint , Flags ) ;
}
}
UEdGraph * UBlueprintEditorLibrary : : AddFunctionGraph ( UBlueprint * Blueprint , const FString & FuncName )
{
if ( ! Blueprint )
{
2021-01-25 21:59:02 -04:00
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Failed to add function graph, ensure that blueprint is not null! " ) ) ;
2020-11-19 12:03:52 -04:00
return nullptr ;
}
2021-01-25 18:41:59 -04:00
// Validate that the given name is appropriate for a new function graph
FName GraphName ;
if ( FKismetNameValidator ( Blueprint ) . IsValid ( FuncName ) = = EValidatorResult : : Ok )
{
GraphName = FName ( * FuncName ) ;
}
else
{
2021-01-25 21:59:02 -04:00
static const FString NewFunctionString = TEXT ( " NewFunction " ) ;
GraphName = FBlueprintEditorUtils : : FindUniqueKismetName ( Blueprint , ! FuncName . IsEmpty ( ) ? FuncName : NewFunctionString ) ;
2021-01-25 18:41:59 -04:00
}
2020-11-19 12:03:52 -04:00
Blueprint - > Modify ( ) ;
UEdGraph * NewGraph = FBlueprintEditorUtils : : CreateNewGraph (
Blueprint ,
2021-01-25 18:41:59 -04:00
GraphName ,
2020-11-19 12:03:52 -04:00
UEdGraph : : StaticClass ( ) ,
UEdGraphSchema_K2 : : StaticClass ( )
) ;
FBlueprintEditorUtils : : AddFunctionGraph < UFunction > ( Blueprint , NewGraph , /* bIsUserCreated = */ true , /* SignatureFromObject = */ nullptr ) ;
return NewGraph ;
}
void UBlueprintEditorLibrary : : RemoveFunctionGraph ( UBlueprint * Blueprint , FName FuncName )
{
if ( ! Blueprint )
{
return ;
}
// Find the function graph of this name
UEdGraph * FunctionGraph = nullptr ;
for ( UEdGraph * Graph : Blueprint - > FunctionGraphs )
{
if ( Graph - > GetFName ( ) = = FuncName )
{
FunctionGraph = Graph ;
break ;
}
}
// Remove the function graph if we can
if ( FunctionGraph & & FunctionGraph - > bAllowDeletion )
{
Blueprint - > Modify ( ) ;
2021-02-05 15:06:57 -04:00
InternalBlueprintEditorLibrary : : CloseOpenEditors ( Blueprint ) ;
2020-11-19 12:03:52 -04:00
FBlueprintEditorUtils : : RemoveGraph ( Blueprint , FunctionGraph , EGraphRemoveFlags : : MarkTransient ) ;
}
else
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Failed to remove function '%s' on blueprint '%s'! " ) , * FuncName . ToString ( ) , * Blueprint - > GetFriendlyName ( ) ) ;
}
}
2020-12-18 14:24:31 -04:00
void UBlueprintEditorLibrary : : RemoveUnusedNodes ( UBlueprint * Blueprint )
{
if ( ! Blueprint )
{
return ;
}
TArray < UEdGraph * > AllGraphs ;
Blueprint - > GetAllGraphs ( AllGraphs ) ;
Blueprint - > Modify ( ) ;
for ( UEdGraph * Graph : AllGraphs )
{
// Skip non-editable graphs
if ( ! Graph | | FBlueprintEditorUtils : : IsGraphReadOnly ( Graph ) )
{
continue ;
}
Graph - > Modify ( ) ;
int32 NumNodesRemoved = 0 ;
for ( int32 i = Graph - > Nodes . Num ( ) - 1 ; i > = 0 ; - - i )
{
UEdGraphNode * Node = Graph - > Nodes [ i ] ;
// We only want to delete user facing nodes because this is meant
// to be a BP refactoring/cleanup tool. Anim graph nodes can still
// be valid with no pin connections made to them
if ( Node - > CanUserDeleteNode ( ) & &
! Node - > IsA < UAnimGraphNode_Base > ( ) & &
2021-02-08 15:57:24 -04:00
! Node - > IsA < UEdGraphNode_Comment > ( ) & &
2020-12-18 14:24:31 -04:00
! InternalBlueprintEditorLibrary : : NodeHasAnyConnections ( Node ) )
{
Node - > BreakAllNodeLinks ( ) ;
Graph - > RemoveNode ( Node ) ;
+ + NumNodesRemoved ;
}
}
// Notify a change to the graph if nodes have been removed
if ( NumNodesRemoved > 0 )
{
Graph - > NotifyGraphChanged ( ) ;
}
}
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( Blueprint ) ;
}
2020-11-19 12:03:52 -04:00
void UBlueprintEditorLibrary : : RemoveGraph ( UBlueprint * Blueprint , UEdGraph * Graph )
{
if ( ! Blueprint | | ! Graph )
{
return ;
}
2021-02-05 15:06:57 -04:00
InternalBlueprintEditorLibrary : : CloseOpenEditors ( Blueprint ) ;
2020-11-19 12:03:52 -04:00
FBlueprintEditorUtils : : RemoveGraph ( Blueprint , Graph , EGraphRemoveFlags : : MarkTransient ) ;
}
void UBlueprintEditorLibrary : : RenameGraph ( UEdGraph * Graph , const FString & NewNameStr )
{
if ( ! Graph )
{
2021-02-08 15:57:03 -04:00
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Invalid graph given, failed to rename! " ) ) ;
2020-11-19 12:03:52 -04:00
return ;
}
2021-02-08 15:57:03 -04:00
// Validate that the given name is appropriate for a new function graph
UBlueprint * BP = FBlueprintEditorUtils : : FindBlueprintForGraph ( Graph ) ;
if ( ! BP )
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Failed to find blueprint for graph! " ) ) ;
return ;
}
FString ValidatedNewName ;
if ( FKismetNameValidator ( BP ) . IsValid ( NewNameStr ) = = EValidatorResult : : Ok )
{
ValidatedNewName = NewNameStr ;
}
else
{
static const FString RenamedGraphString = TEXT ( " NewGraph " ) ;
ValidatedNewName = FBlueprintEditorUtils : : FindUniqueKismetName ( BP , ! NewNameStr . IsEmpty ( ) ? NewNameStr : RenamedGraphString ) . ToString ( ) ;
}
FBlueprintEditorUtils : : RenameGraph ( Graph , ValidatedNewName ) ;
2020-11-19 12:03:52 -04:00
}
UBlueprint * UBlueprintEditorLibrary : : GetBlueprintAsset ( UObject * Object )
{
return Cast < UBlueprint > ( Object ) ;
}
void UBlueprintEditorLibrary : : ReparentBlueprint ( UBlueprint * Blueprint , UClass * NewParentClass )
{
if ( ! Blueprint | | ! NewParentClass )
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " Failed to reparent blueprint! " ) ) ;
return ;
}
if ( NewParentClass = = Blueprint - > ParentClass )
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " '%s' is already parented to class '%s'! " ) , * Blueprint - > GetFriendlyName ( ) , * NewParentClass - > GetName ( ) ) ;
return ;
}
// There could be possible data loss if reparenting outside the current class hierarchy
if ( ! Blueprint - > ParentClass | | ! NewParentClass - > GetDefaultObject ( ) - > IsA ( Blueprint - > ParentClass ) )
{
UE_LOG ( LogBlueprintEditorLib , Warning , TEXT ( " '%s' class heirarcy is changing, there could be possible data loss! " ) , * Blueprint - > GetFriendlyName ( ) ) ;
}
UClass * OriginalParentClass = Blueprint - > ParentClass ;
Blueprint - > ParentClass = NewParentClass ;
FBlueprintEditorUtils : : RefreshAllNodes ( Blueprint ) ;
FBlueprintEditorUtils : : MarkBlueprintAsModified ( Blueprint ) ;
CompileBlueprint ( Blueprint ) ;
2021-01-05 11:17:06 -04:00
}
bool UBlueprintEditorLibrary : : GatherUnusedVariables ( const UBlueprint * Blueprint , TArray < FProperty * > & OutProperties )
{
if ( ! Blueprint )
{
return false ;
}
bool bHasAtLeastOneVariableToCheck = false ;
for ( TFieldIterator < FProperty > PropertyIt ( Blueprint - > SkeletonGeneratedClass , EFieldIteratorFlags : : ExcludeSuper ) ; PropertyIt ; + + PropertyIt )
{
FProperty * Property = * PropertyIt ;
// Don't show delegate properties, there is special handling for these
const bool bDelegateProp = Property - > IsA ( FDelegateProperty : : StaticClass ( ) ) | | Property - > IsA ( FMulticastDelegateProperty : : StaticClass ( ) ) ;
const bool bShouldShowProp = ( ! Property - > HasAnyPropertyFlags ( CPF_Parm ) & & Property - > HasAllPropertyFlags ( CPF_BlueprintVisible ) & & ! bDelegateProp ) ;
if ( bShouldShowProp )
{
bHasAtLeastOneVariableToCheck = true ;
FName VarName = Property - > GetFName ( ) ;
const int32 VarInfoIndex = FBlueprintEditorUtils : : FindNewVariableIndex ( Blueprint , VarName ) ;
const bool bHasVarInfo = ( VarInfoIndex ! = INDEX_NONE ) ;
const FObjectPropertyBase * ObjectProperty = CastField < const FObjectPropertyBase > ( Property ) ;
bool bIsTimeline = ObjectProperty & &
ObjectProperty - > PropertyClass & &
ObjectProperty - > PropertyClass - > IsChildOf ( UTimelineComponent : : StaticClass ( ) ) ;
if ( ! bIsTimeline & & bHasVarInfo & & ! FBlueprintEditorUtils : : IsVariableUsed ( Blueprint , VarName ) )
{
OutProperties . Add ( Property ) ;
}
}
}
return bHasAtLeastOneVariableToCheck ;
}
int32 UBlueprintEditorLibrary : : RemoveUnusedVariables ( UBlueprint * Blueprint )
{
if ( ! Blueprint )
{
return 0 ;
}
// Gather FProperties from this BP and see if we can remove any
TArray < FProperty * > VariableProperties ;
UBlueprintEditorLibrary : : GatherUnusedVariables ( Blueprint , VariableProperties ) ;
// No variables can be removed from this blueprint
if ( VariableProperties . Num ( ) = = 0 )
{
return 0 ;
}
// Get the variables by name so that we can bulk remove them and print them out to the log
TArray < FName > VariableNames ;
FString PropertyList ;
VariableNames . Reserve ( VariableProperties . Num ( ) ) ;
for ( int32 Index = 0 ; Index < VariableProperties . Num ( ) ; + + Index )
{
VariableNames . Add ( VariableProperties [ Index ] - > GetFName ( ) ) ;
if ( PropertyList . IsEmpty ( ) )
{
PropertyList = UEditorEngine : : GetFriendlyName ( VariableProperties [ Index ] ) ;
}
else
{
PropertyList + = FString : : Printf ( TEXT ( " , %s " ) , * UEditorEngine : : GetFriendlyName ( VariableProperties [ Index ] ) ) ;
}
}
const int32 NumRemovedVars = VariableNames . Num ( ) ;
// Remove the variables by name
FBlueprintEditorUtils : : BulkRemoveMemberVariables ( Blueprint , VariableNames ) ;
UE_LOG ( LogBlueprintEditorLib , Log , TEXT ( " The following variable(s) were deleted successfully: %s. " ) , * PropertyList ) ;
return NumRemovedVars ;
}
2021-11-18 14:37:34 -05:00
UClass * UBlueprintEditorLibrary : : GeneratedClass ( UBlueprint * BlueprintObj )
{
if ( BlueprintObj )
{
return BlueprintObj - > GeneratedClass - > GetAuthoritativeClass ( ) ;
}
return nullptr ;
}
2021-01-05 11:17:06 -04:00
# undef LOCTEXT_NAMESPACE // "BlueprintEditorLibrary"