356 lines
9.8 KiB
C++
Raw Normal View History

2025-11-17 15:11:34 +08:00
// 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);
2025-11-18 15:37:21 +08:00
//按照横向位置进行排序
Pin1->Connections.Sort([](const UDialogueRuntimePin& A, const UDialogueRuntimePin& B)
{
return A.Parent->Position.X < B.Parent->Position.X;
});
2025-11-17 15:11:34 +08:00
if (Pin1->Direction == EGPD_Input)
{
if (!Pin2->Connections.Contains(Pin1))
{
Pin2->Connections.Add(Pin1);
2025-11-18 15:37:21 +08:00
//按照横向位置进行排序
Pin2->Connections.Sort([](const UDialogueRuntimePin& A, const UDialogueRuntimePin& B)
{
return A.Parent->Position.X < B.Parent->Position.X;
});
2025-11-17 15:11:34 +08:00
}
}
}
}
//标记数据更新
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