UEdGraphNode* FEdGraphSchemaAction_K2AddCallOnActor::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); // Snap the node placement location to the grid, ensures calculations later match up better FVector2D LocalLocation; LocalLocation.X = FMath::GridSnap( Location.X, SNAP_GRID ); LocalLocation.Y = FMath::GridSnap( Location.Y, SNAP_GRID ); // First use the base functionality to spawn the 'call function' node UEdGraphNode* CallNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, LocalLocation); const float FunctionNodeHeightUnsnapped = UEdGraphSchema_K2::EstimateNodeHeight( CallNode ); // this is the guesstimate of the function node's height, snapped to grid units const float FunctionNodeHeight = FMath::GridSnap( FunctionNodeHeightUnsnapped, SNAP_GRID ); // this is roughly the middle of the function node height const float FunctionNodeMidY = LocalLocation.Y + FunctionNodeHeight * 0.5f; // this is the offset up from the mid point at which we start placing nodes const float StartYOffset = (float((LevelActors.Num() > 0) ? LevelActors.Num()-1 : 0) * -NodeLiteralHeight) * 0.5f; // The Y location we start placing nodes from const float ReferencedNodesPlacementYLocation = FunctionNodeMidY + StartYOffset; // Now we need to create the actor literal to wire up for ( int32 ActorIndex = 0; ActorIndex < LevelActors.Num(); ActorIndex++ ) { AActor* LevelActor = LevelActors[ActorIndex]; if(LevelActor != NULL) { UK2Node_Literal* LiteralNode = NewObject<UK2Node_Literal>(ParentGraph); ParentGraph->AddNode(LiteralNode, false, bSelectNewNode); LiteralNode->SetFlags(RF_Transactional); LiteralNode->SetObjectRef(LevelActor); LiteralNode->AllocateDefaultPins(); LiteralNode->NodePosX = LocalLocation.X - FunctionNodeLiteralReferencesXOffset; // this is the current offset down from the Y start location to place the next node at float CurrentNodeOffset = NodeLiteralHeight * float(ActorIndex); LiteralNode->NodePosY = ReferencedNodesPlacementYLocation + CurrentNodeOffset; LiteralNode->SnapToGrid(SNAP_GRID); // Connect the literal out to the self of the call UEdGraphPin* LiteralOutput = LiteralNode->GetValuePin(); UEdGraphPin* CallSelfInput = CallNode->FindPin(K2Schema->PN_Self); if(LiteralOutput != NULL && CallSelfInput != NULL) { LiteralOutput->MakeLinkTo(CallSelfInput); } } } return CallNode; }
void UK2Node_PlayMovieScene::CreatePinForBoundObject( FMovieSceneBoundObject& BoundObject ) { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); // We use the GUID as the pin name as this uniquely identifies it const FString PinName = BoundObject.GetPossessableGuid().ToString( EGuidFormats::DigitsWithHyphens ); // For the friendly name, we use the possessable name from the MovieScene asset that is associated with this node FText PinFriendlyName = FText::FromString(PinName); { UMovieScene* MovieScene = GetMovieScene(); if( MovieScene != NULL ) // @todo sequencer: Need to refresh the PinFriendlyName if the MovieScene asset changes, or if the possessable slot is renamed within Sequencer { for( auto PossessableIndex = 0; PossessableIndex < MovieScene->GetPossessableCount(); ++PossessableIndex ) { auto& Possessable = MovieScene->GetPossessable( PossessableIndex ); if( Possessable.GetGuid() == BoundObject.GetPossessableGuid() ) { // Found a name for this possessable PinFriendlyName = FText::FromString(Possessable.GetName()); break; } } } } const FString PinSubCategory(TEXT("")); UObject* PinSubCategoryObject = AActor::StaticClass(); const bool bIsArray = false; const bool bIsReference = false; UEdGraphPin* NewPin = CreatePin( EGPD_Input, K2Schema->PC_Object, PinSubCategory, PinSubCategoryObject, bIsArray, bIsReference, PinName ); check( NewPin != NULL ); // Set the friendly name for this pin NewPin->PinFriendlyName = PinFriendlyName; // Place a literal for the bound object and hook it up to the pin // @todo sequencer: Should we instead set the default on the pin to the object instead? const TArray< UObject* >& Objects = BoundObject.GetObjects(); if( Objects.Num() > 0 ) { for( auto ObjectIter( Objects.CreateConstIterator() ); ObjectIter; ++ObjectIter ) { auto* Object = *ObjectIter; if( ensure( Object != NULL ) ) { // Check to see if we have a literal for this object already UK2Node_Literal* LiteralNode = NULL; { TArray< UK2Node_Literal* > LiteralNodes; GetGraph()->GetNodesOfClass( LiteralNodes ); for( auto NodeIt( LiteralNodes.CreateConstIterator() ); NodeIt; ++NodeIt ) { const auto CurLiteralNode = *NodeIt; if( CurLiteralNode->GetObjectRef() == Object ) { // Found one! LiteralNode = CurLiteralNode; break; } } } if( LiteralNode == NULL ) { // No literal for this object yet, so we'll make one now. UK2Node_Literal* LiteralNodeTemplate = NewObject<UK2Node_Literal>(); LiteralNodeTemplate->SetObjectRef( Object ); // Figure out a decent place to stick the node // @todo sequencer: The placement of these is really unacceptable. We should setup new logic specific for moviescene pin objects. const FVector2D NewNodePos = GetGraph()->GetGoodPlaceForNewNode(); LiteralNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_Literal>(GetGraph(), LiteralNodeTemplate, NewNodePos); } // Hook up the object reference literal to our pin LiteralNode->GetValuePin()->MakeLinkTo( NewPin ); } } } }