// Fill out your copyright notice in the Description page of Project Settings. #include "DialogueAssetEditor.h" #include "DialogueGraph.h" #include "DialogueGraphSchema.h" #include "DialogueEditorMode.h" #include "DialogueGraphPanelNodeFactory.h" #include "DialogueGraphPanelPinFactory.h" #include "EdGraphUtilities.h" #include "Kismet2/BlueprintEditorUtils.h" #include "GraphEditorActions.h" #include "Framework/Commands/GenericCommands.h" #include "IDetailsView.h" #include "Node/DialogueGraphNode_Base.h" #include "Node/DialogueGraphNode_NPC.h" #include "Node/DialogueGraphNode_Player.h" #include "Node/DialogueGraphNode_Root.h" #define LOCTEXT_NAMESPACE "DialogueEditorToolkit" const FName DialogueEditorAppName = FName(TEXT("DialogueEditorApp")); void FDialogueAssetEditor::OnClose() { SaveGraphData(); DialogueBeingEdited->SetPreSaveListener(nullptr); FWorkflowCentricApplication::OnClose(); } FDialogueAssetEditor::FDialogueAssetEditor() : DialogueGraph(nullptr) { } FDialogueAssetEditor::~FDialogueAssetEditor() { } FText FDialogueAssetEditor::GetBaseToolkitName() const { return IsValid(DialogueBeingEdited) ? FText::FromString(DialogueBeingEdited->GetName()) : LOCTEXT("AppLabel", "Dialogue Editor"); } void FDialogueAssetEditor::InitDialogueEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, TObjectPtr InitDialogueAsset) { DialogueBeingEdited = InitDialogueAsset; //修改过程中保存 DialogueBeingEdited->SetPreSaveListener([this] () { SaveGraphData();; }); // 注册命令 FGenericCommands::Register(); FGraphEditorCommands::Register(); BindGraphCommands(); //注册节点的创建 FEdGraphUtilities::RegisterVisualNodeFactory(MakeShareable(new FDialogueGraphPanelNodeFactory())); FEdGraphUtilities::RegisterVisualPinFactory(MakeShareable(new FDialogueGraphPanelPinFactory())); // 创建图表 DialogueGraph = FBlueprintEditorUtils::CreateNewGraph( DialogueBeingEdited.Get(), NAME_None, UDialogueGraph::StaticClass(), UDialogueGraphSchema::StaticClass() ); TArray ObjectsToEdit; ObjectsToEdit.Add(InitDialogueAsset); // 初始化编辑器 InitAssetEditor( Mode, InitToolkitHost, DialogueEditorAppName, FTabManager::FLayout::NullLayout, true, true, ObjectsToEdit ); // 添加应用模式 AddApplicationMode(TEXT("DialogueEditorMode"), MakeShareable(new FDialogueEditorMode(SharedThis(this)))); SetCurrentMode(TEXT("DialogueEditorMode")); //加载资源中的数据节点 LoadGraphData(); } void FDialogueAssetEditor::SetDetailsView(TSharedPtr InDetailsView) { DetailsView = InDetailsView; if (DetailsView.IsValid()) { DetailsView->OnFinishedChangingProperties().AddSP(this, &FDialogueAssetEditor::OnNodeDetailViewPropertiesUpdated); } } void FDialogueAssetEditor::OnGraphSelectionChanged(const FGraphPanelSelectionSet& Selection) { if (!DetailsView.IsValid()) return; if (UDialogueGraphNode_Base* SelectedNode = GetSelectedNode(Selection)) { DetailsView->SetObject(SelectedNode->GetDialogueNodeData()); } else { DetailsView->SetObject(nullptr); } } void FDialogueAssetEditor::SaveGraphData() { if (!DialogueBeingEdited || !DialogueGraph) { return; } TArray> Connections; TMap IdToPinMap; DialogueBeingEdited->NodeDatas.Empty(); for (UEdGraphNode* EditorNode : DialogueGraph->Nodes) { UDialogueRuntimeNode* RuntimeNode = NewObject(DialogueBeingEdited); RuntimeNode->Position = FVector2D(EditorNode->NodePosX, EditorNode->NodePosY); for (UEdGraphPin* EditorPin : EditorNode->Pins) { UDialogueRuntimePin* RuntimePin = NewObject(RuntimeNode); RuntimePin->PinName = EditorPin->PinName; RuntimePin->PinId = EditorPin->PinId; RuntimePin->Parent = RuntimeNode; RuntimePin->Direction = EditorPin->Direction; if (EditorPin->HasAnyConnections()) { std::pair Connection = std::make_pair(EditorPin->PinId, EditorPin->LinkedTo[0]->PinId); Connections.Add(Connection); } IdToPinMap.Add(EditorPin->PinId, RuntimePin); if (RuntimePin->Direction == EGPD_Input) { RuntimeNode->InputPin = RuntimePin; } else { RuntimeNode->OutputPins.Add(RuntimePin); } } UDialogueGraphNode_Base* EditorDialogueNode = Cast(EditorNode); RuntimeNode->NodeData = DuplicateObject(EditorDialogueNode->GetDialogueNodeData(), DialogueBeingEdited); RuntimeNode->NodeType = EditorDialogueNode->GetDialogueNodeType(); DialogueBeingEdited->NodeDatas.Add(RuntimeNode); } for (std::pair Connection : Connections) { UDialogueRuntimePin* Pin1 = IdToPinMap[Connection.first]; UDialogueRuntimePin* Pin2 = IdToPinMap[Connection.second]; if (!Pin1->Connections.Contains(Pin2)) { Pin1->Connections.Add(Pin2); if (Pin1->Direction == EGPD_Input) { if (!Pin2->Connections.Contains(Pin1)) { Pin2->Connections.Add(Pin1); } } } } //标记数据更新 DialogueBeingEdited->Modify(); } void FDialogueAssetEditor::LoadGraphData() { if (DialogueBeingEdited->NodeDatas.Num() == 0) { //空节点,创建默认Root节点 DialogueGraph->GetSchema()->CreateDefaultNodesForGraph(*DialogueGraph); UDialogueRuntimeNode* RootNode = NewObject(); RootNode->NodeType = EDialogueNodeType::Root; return; } //读取节点信息,创建pin及连线 TArray> Connections; TMap IdToPinMap; for (UDialogueRuntimeNode* RuntimeNode: DialogueBeingEdited->NodeDatas) { UDialogueGraphNode_Base* EditorNode = nullptr; switch (RuntimeNode->NodeType) { case EDialogueNodeType::Root: EditorNode = NewObject(DialogueGraph); break; case EDialogueNodeType::NPC: EditorNode = NewObject(DialogueGraph); break; case EDialogueNodeType::Player: EditorNode = NewObject(DialogueGraph); break; default: ; continue; } EditorNode->CreateNewGuid(); EditorNode->SetPostion(RuntimeNode->Position); if (RuntimeNode->NodeData) { EditorNode->SetDialogueNodeData(DuplicateObject(RuntimeNode->NodeData, EditorNode)); } else { EditorNode->InitializeNodeData(); } if (RuntimeNode->InputPin) { UDialogueRuntimePin* InputPin = RuntimeNode->InputPin; FName Category = TEXT("Inputs"); UEdGraphPin* EditorPin = EditorNode->CreatePin((EEdGraphPinDirection)InputPin->Direction, Category, InputPin->PinName); EditorPin->PinId = InputPin->PinId; if (InputPin->Connections.Num() > 0) { Connections.Add(std::make_pair(InputPin->PinId, InputPin->Connections[0]->PinId)); } IdToPinMap.Add(InputPin->PinId, EditorPin); } for (UDialogueRuntimePin* RuntimePin : RuntimeNode->OutputPins) { FName Category = TEXT("Outputs"); UEdGraphPin* EditorPin = EditorNode->CreatePin((EEdGraphPinDirection)RuntimePin->Direction, Category, RuntimePin->PinName); EditorPin->PinId = RuntimePin->PinId; if (RuntimePin->Connections.Num() > 0) { Connections.Add(std::make_pair(RuntimePin->PinId, RuntimePin->Connections[0]->PinId)); } IdToPinMap.Add(RuntimePin->PinId, EditorPin); } DialogueGraph->AddNode(EditorNode, true, true); } for (std::pair Connection : Connections) { UEdGraphPin* FromPin = IdToPinMap[Connection.first]; UEdGraphPin* ToPin = IdToPinMap[Connection.second]; FromPin->LinkedTo.Add(ToPin); ToPin->LinkedTo.Add(FromPin); } } void FDialogueAssetEditor::BindGraphCommands() { ToolkitCommands->MapAction(FGenericCommands::Get().Delete, FExecuteAction::CreateSP(this, &FDialogueAssetEditor::DeleteSelectedNodes), FCanExecuteAction::CreateSP(this, &FDialogueAssetEditor::CanDeleteNodes)); } void FDialogueAssetEditor::DeleteSelectedNodes() { if (!GraphEditor.IsValid()) return; const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription()); GraphEditor->GetCurrentGraph()->Modify(); const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); GraphEditor->ClearSelectionSet(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { UEdGraphNode* EdNode = Cast(*NodeIt); if (EdNode == nullptr || !EdNode->CanUserDeleteNode()) continue;; if (UDialogueGraphNode_Base* EdNode_Node = Cast(EdNode)) { EdNode_Node->Modify(); const UEdGraphSchema* Schema = EdNode_Node->GetSchema(); if (Schema != nullptr) { Schema->BreakNodeLinks(*EdNode_Node); } EdNode_Node->DestroyNode(); } else { EdNode->Modify(); EdNode->DestroyNode(); } } } bool FDialogueAssetEditor::CanDeleteNodes() { if (!GraphEditor.IsValid()) return false; const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { UEdGraphNode* Node = Cast(*SelectedIter); if (Node && Node->CanUserDeleteNode()) { return true; } } return false; } UDialogueGraphNode_Base* FDialogueAssetEditor::GetSelectedNode(const FGraphPanelSelectionSet& Selection) { for (UObject* Obj : Selection) { if (UDialogueGraphNode_Base* Node = Cast(Obj)) { return Node; } } return nullptr; } void FDialogueAssetEditor::OnNodeDetailViewPropertiesUpdated(const FPropertyChangedEvent& Event) { if (GraphEditor.IsValid()) { GraphEditor->NotifyGraphChanged(); } } #undef LOCTEXT_NAMESPACE