// Fill out your copyright notice in the Description page of Project Settings. #include "DialogueGraphSchema.h" #include "Node/DialogueGraphNode_Base.h" #include "Node/DialogueGraphNode_NPC.h" #include "Node/DialogueGraphNode_Player.h" #include "Node/DialogueGraphNode_Root.h" #define LOCTEXT_NAMESPACE "DialogueGraphSchema" UEdGraphNode* FDialogueGraphSchemaAction_NewNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) { UDialogueGraphNode_Base* NewNode = NewObject(ParentGraph, ClassTemplate); NewNode->CreateNewGuid(); NewNode->SetPostion(Location); NewNode->InitializeNodeData(); NewNode->AllocateDefaultPins(); ParentGraph->Modify(); ParentGraph->AddNode(NewNode, true, bSelectNewNode); // 如果从某个Pin拖拽创建,自动连接 if (FromPin && NewNode->Pins.Num() > 0) { for (UEdGraphPin* Pin : NewNode->Pins) { if (Pin->Direction != FromPin->Direction) { GetDefault()->TryCreateConnection(FromPin, Pin); break; } } } return NewNode; } FVerticalConnectionDrawingPolicy::FVerticalConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements) : FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements) { } void FVerticalConnectionDrawingPolicy::DrawSplineWithArrow(const FVector2D& StartPoint, const FVector2D& EndPoint, const FConnectionParams& Params) { // Draw the spline DrawConnection( WireLayerID, StartPoint, EndPoint, Params); // Draw the arrow if (ArrowImage != nullptr) { FVector2D ArrowPoint = EndPoint - ArrowRadius; FSlateDrawElement::MakeRotatedBox( DrawElementsList, ArrowLayerID, FPaintGeometry(ArrowPoint, ArrowImage->ImageSize * ZoomFactor, ZoomFactor), ArrowImage, ESlateDrawEffect::None, FMath::DegreesToRadians(90), // 旋转角度(弧度) ArrowImage->ImageSize * 0.5f * ZoomFactor, // 旋转中心 FSlateDrawElement::RelativeToElement, Params.WireColor ); } } void FVerticalConnectionDrawingPolicy::DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params) { //@TODO: These values should be pushed into the Slate style, they are compensating for a bit of // empty space inside of the pin brush images. const float StartFudgeX = 4.0f; const float EndFudgeX = 4.0f; const FVector2D StartPoint = FGeometryHelper::CenterOf(StartGeom) - FVector2D(ArrowRadius.X, StartFudgeX); const FVector2D EndPoint = FGeometryHelper::CenterOf(EndGeom) - FVector2D(0, ArrowRadius.Y - EndFudgeX); DrawSplineWithArrow(StartPoint, EndPoint, Params); } // void FVerticalConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, // const FConnectionParams& Params) // { // const FVector2D& P0 = Start; // const FVector2D& P1 = End; // // FVector2D P0 = GetCorrectedPinPosition(Start, Params.AssociatedPin1, Params.StartDirection); // // FVector2D P1 = GetCorrectedPinPosition(End, Params.AssociatedPin2, Params.EndDirection); // // // 计算连线方向 // const FVector2D Delta = P1 - P0; // const bool bIsVerticalConnection = FMath::Abs(Delta.Y) > FMath::Abs(Delta.X); // // // 根据连线方向计算切线 // FVector2D SplineTangent; // if (bIsVerticalConnection) // { // // 垂直连线:主要控制垂直方向的弯曲 // SplineTangent = FVector2D(0, FMath::Abs(Delta.Y) * 0.5f); // } // else // { // // 水平连线:保持原有逻辑 // SplineTangent = FVector2D(FMath::Abs(Delta.X) * 0.5f, 0); // } // // // 修改切线方向计算,适应垂直连线 // FVector2D P0Tangent, P1Tangent; // // if (bIsVerticalConnection) // { // // 垂直连线的切线逻辑 // if (Params.StartDirection == EGPD_Output) // { // // 输出引脚:根据连线方向调整 // P0Tangent = (Delta.Y >= 0) ? SplineTangent : -SplineTangent; // } // else // { // // 输入引脚:根据连线方向调整 // P0Tangent = (Delta.Y >= 0) ? -SplineTangent : SplineTangent; // } // // if (Params.EndDirection == EGPD_Input) // { // // 输入引脚:根据连线方向调整 // P1Tangent = (Delta.Y >= 0) ? SplineTangent : -SplineTangent; // } // else // { // // 输出引脚:根据连线方向调整 // P1Tangent = (Delta.Y >= 0) ? -SplineTangent : SplineTangent; // } // } // else // { // // 水平连线保持原有逻辑 // P0Tangent = (Params.StartDirection == EGPD_Output) ? SplineTangent : -SplineTangent; // P1Tangent = (Params.EndDirection == EGPD_Input) ? SplineTangent : -SplineTangent; // } // // // 使用自定义切线(如果提供了的话) // if (!Params.StartTangent.IsNearlyZero()) // { // P0Tangent = Params.StartTangent; // } // if (!Params.EndTangent.IsNearlyZero()) // { // P1Tangent = Params.EndTangent; // } // // // 后面的碰撞检测等逻辑保持不变... // if (Settings->bTreatSplinesLikePins) // { // // [保持原有的碰撞检测逻辑不变] // // ... 这里是你原有的碰撞检测代码 ... // } // // // 绘制连线(这部分保持不变) // FSlateDrawElement::MakeDrawSpaceSpline( // DrawElementsList, // LayerId, // P0, P0Tangent, // P1, P1Tangent, // Params.WireThickness, // ESlateDrawEffect::None, // Params.WireColor // ); // // // 修改中点的方向计算 // if (Params.bDrawBubbles || (MidpointImage != nullptr)) // { // // 重新参数化表计算(保持不变) // FInterpCurve SplineReparamTable; // const float SplineLength = MakeSplineReparamTable(P0, P0Tangent, P1, P1Tangent, SplineReparamTable); // // // 气泡绘制逻辑(保持不变) // if (Params.bDrawBubbles) // { // // [保持原有的气泡绘制逻辑] // // ... 这里是你原有的气泡绘制代码 ... // } // // // 修改中点图标的旋转逻辑 // if (MidpointImage != nullptr) // { // const float MidpointAlpha = SplineReparamTable.Eval(SplineLength * 0.5f, 0.f); // const FVector2D Midpoint = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha); // // const FVector2D MidpointPlusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha + KINDA_SMALL_NUMBER); // const FVector2D MidpointMinusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha - KINDA_SMALL_NUMBER); // FVector2D SlopeUnnormalized = MidpointPlusE - MidpointMinusE; // // // 如果是垂直连线,调整角度计算 // float AngleInRadians; // if (bIsVerticalConnection) // { // // 垂直连线:主要考虑Y轴方向 // if (FMath::Abs(SlopeUnnormalized.Y) > FMath::Abs(SlopeUnnormalized.X)) // { // // 主要垂直方向,旋转90度 // AngleInRadians = FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X) + FMath::DegreesToRadians(90.0f); // } // else // { // AngleInRadians = FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X); // } // } // else // { // AngleInRadians = SlopeUnnormalized.IsNearlyZero() ? 0.0f : FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X); // } // // const FVector2D MidpointDrawPos = Midpoint - MidpointRadius; // // FSlateDrawElement::MakeRotatedBox( // DrawElementsList, // LayerId, // FPaintGeometry(MidpointDrawPos, MidpointImage->ImageSize * ZoomFactor, ZoomFactor), // MidpointImage, // ESlateDrawEffect::None, // AngleInRadians, // TOptional(), // FSlateDrawElement::RelativeToElement, // Params.WireColor // ); // } // } // } FVector2D FVerticalConnectionDrawingPolicy::ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const { // 修改为垂直方向: const FVector2D Delta = End - Start; // 如果是主要垂直方向的连线,使用垂直切线 if (FMath::Abs(Delta.Y) > FMath::Abs(Delta.X)) { // 垂直连线:主要控制垂直曲线 return FVector2D(0, FMath::Abs(Delta.Y) * 0.5f); } else { // 水平连线:保持原有逻辑 return FVector2D(FMath::Abs(Delta.X) * 0.5f, 0); } } FVector2D FVerticalConnectionDrawingPolicy::GetPinPos(const UEdGraphPin* Pin) const { // const FVector2D NodeSize = GetDesiredSize(); // FVector2D NodePosition; // if (Pin->Direction == EGPD_Input) // { // // 输入引脚:位于节点顶部中央 // NodePosition = FVector2D(NodeWidth * 0.5f, 0.0f); // } // else // { // // 输出引脚:位于节点底部中央 // NodePosition = FVector2D(NodeWidth * 0.5f, NodeHeight); // } // // // if (Pin->Direction == EGPD_Input) // { // // 输入引脚在顶部中央 // return NodePosition + FVector2D(NodeSize.X * 0.5f, 0.0f); // } // else // { // // 输出引脚在底部中央 // return NodePosition + FVector2D(NodeSize.X * 0.5f, NodeSize.Y); // } return FVector2D(); } // FVector2D FVerticalConnectionDrawingPolicy::GetCorrectedPinPosition(const FVector2D& OriginalPosition, UEdGraphPin* Pin, // EEdGraphPinDirection Direction) // { // if (!Pin) // return OriginalPosition; // // // 获取引脚的视觉widget // TSharedPtr GraphPin = GetPinWidget(Pin); // if (!GraphPin.IsValid()) // return OriginalPosition; // // // 计算引脚的真正中心点 // FGeometry PinGeometry = GraphPin->GetCachedGeometry(); // FVector2D PinCenter = PinGeometry.GetAbsolutePosition() + PinGeometry.GetAbsoluteSize() * 0.5f; // // // 转换为绘制空间坐标 // return PinCenter; // } void UDialogueGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const { FGraphNodeCreator NodeCreator(Graph); UDialogueGraphNode_Root* MyNode = NodeCreator.CreateNode(true, UDialogueGraphNode_Root::StaticClass()); NodeCreator.Finalize(); SetNodeMetaData(MyNode, FNodeMetadata::DefaultGraphNode); } void UDialogueGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const { // 添加npc对话节点 TSharedPtr NPCNodeAction = MakeShareable(new FDialogueGraphSchemaAction_NewNode( UDialogueGraphNode_NPC::StaticClass(), LOCTEXT("DialogueNode_NPC_Category", "NPC Dialogue"), LOCTEXT("DialogueNode_NPC_Desc", "NPC Dialogue Node"), LOCTEXT("DialogueNode_NPC_Tooltip", "Create a npc dialogue node") )); ContextMenuBuilder.AddAction(NPCNodeAction); // 添加npc对话节点 TSharedPtr PlayerNodeAction = MakeShareable(new FDialogueGraphSchemaAction_NewNode( UDialogueGraphNode_Player::StaticClass(), LOCTEXT("DialogueNode_Player_Category", "Player Dialogue"), LOCTEXT("DialogueNode_Player_Desc", "Player Dialogue Node"), LOCTEXT("DialogueNode_Player_Tooltip", "Create a Player dialogue node") )); ContextMenuBuilder.AddAction(PlayerNodeAction); } const FPinConnectionResponse UDialogueGraphSchema::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const { // 不能连接自己 if (A->GetOwningNode() == B->GetOwningNode()) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("ConnectionSameNode", "Cannot connect to self")); } // 必须一个输入一个输出 if (A->Direction == B->Direction) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("ConnectionSameDirection", "Must connect input to output")); } return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT("")); } FConnectionDrawingPolicy* UDialogueGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const { return new FVerticalConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements); } #undef LOCTEXT_NAMESPACE