2025-11-18 15:37:21 +08:00

356 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<IToolkitHost>& InitToolkitHost, TObjectPtr<UDialogueAsset> 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<UObject*> 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<IDetailsView> 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<std::pair<FGuid, FGuid>> Connections;
TMap<FGuid, UDialogueRuntimePin*> IdToPinMap;
DialogueBeingEdited->NodeDatas.Empty();
for (UEdGraphNode* EditorNode : DialogueGraph->Nodes)
{
UDialogueRuntimeNode* RuntimeNode = NewObject<UDialogueRuntimeNode>(DialogueBeingEdited);
RuntimeNode->Position = FVector2D(EditorNode->NodePosX, EditorNode->NodePosY);
for (UEdGraphPin* EditorPin : EditorNode->Pins)
{
UDialogueRuntimePin* RuntimePin = NewObject<UDialogueRuntimePin>(RuntimeNode);
RuntimePin->PinName = EditorPin->PinName;
RuntimePin->PinId = EditorPin->PinId;
RuntimePin->Parent = RuntimeNode;
RuntimePin->Direction = EditorPin->Direction;
if (EditorPin->HasAnyConnections())
{
std::pair<FGuid, FGuid> 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<UDialogueGraphNode_Base>(EditorNode);
RuntimeNode->NodeData = DuplicateObject(EditorDialogueNode->GetDialogueNodeData(), DialogueBeingEdited);
RuntimeNode->NodeType = EditorDialogueNode->GetDialogueNodeType();
DialogueBeingEdited->NodeDatas.Add(RuntimeNode);
}
for (std::pair<FGuid, FGuid> Connection : Connections)
{
UDialogueRuntimePin* Pin1 = IdToPinMap[Connection.first];
UDialogueRuntimePin* Pin2 = IdToPinMap[Connection.second];
if (!Pin1->Connections.Contains(Pin2))
{
Pin1->Connections.Add(Pin2);
//按照横向位置进行排序
Pin1->Connections.Sort([](const UDialogueRuntimePin& A, const UDialogueRuntimePin& B)
{
return A.Parent->Position.X < B.Parent->Position.X;
});
if (Pin1->Direction == EGPD_Input)
{
if (!Pin2->Connections.Contains(Pin1))
{
Pin2->Connections.Add(Pin1);
//按照横向位置进行排序
Pin2->Connections.Sort([](const UDialogueRuntimePin& A, const UDialogueRuntimePin& B)
{
return A.Parent->Position.X < B.Parent->Position.X;
});
}
}
}
}
//标记数据更新
DialogueBeingEdited->Modify();
}
void FDialogueAssetEditor::LoadGraphData()
{
if (DialogueBeingEdited->NodeDatas.Num() == 0)
{
//空节点创建默认Root节点
DialogueGraph->GetSchema()->CreateDefaultNodesForGraph(*DialogueGraph);
UDialogueRuntimeNode* RootNode = NewObject<UDialogueRuntimeNode>();
RootNode->NodeType = EDialogueNodeType::Root;
return;
}
//读取节点信息创建pin及连线
TArray<std::pair<FGuid, FGuid>> Connections;
TMap<FGuid, UEdGraphPin*> IdToPinMap;
for (UDialogueRuntimeNode* RuntimeNode: DialogueBeingEdited->NodeDatas)
{
UDialogueGraphNode_Base* EditorNode = nullptr;
switch (RuntimeNode->NodeType)
{
case EDialogueNodeType::Root:
EditorNode = NewObject<UDialogueGraphNode_Root>(DialogueGraph);
break;
case EDialogueNodeType::NPC:
EditorNode = NewObject<UDialogueGraphNode_NPC>(DialogueGraph);
break;
case EDialogueNodeType::Player:
EditorNode = NewObject<UDialogueGraphNode_Player>(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<FGuid, FGuid> 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<UEdGraphNode>(*NodeIt);
if (EdNode == nullptr || !EdNode->CanUserDeleteNode())
continue;;
if (UDialogueGraphNode_Base* EdNode_Node = Cast<UDialogueGraphNode_Base>(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<UEdGraphNode>(*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<UDialogueGraphNode_Base>(Obj))
{
return Node;
}
}
return nullptr;
}
void FDialogueAssetEditor::OnNodeDetailViewPropertiesUpdated(const FPropertyChangedEvent& Event)
{
if (GraphEditor.IsValid())
{
GraphEditor->NotifyGraphChanged();
}
}
#undef LOCTEXT_NAMESPACE