添加对话系统插件

This commit is contained in:
997146918 2025-11-17 15:11:34 +08:00
parent 31311ad092
commit 45eec41cbf
67 changed files with 1915 additions and 0 deletions

View File

@ -8040,6 +8040,26 @@
{
"Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules",
"Type": "RequiredResource"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.dll",
"Type": "DynamicLibrary"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.pdb",
"Type": "SymbolFile"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.dll",
"Type": "DynamicLibrary"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.pdb",
"Type": "SymbolFile"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Binaries/Win64/UnrealEditor.modules",
"Type": "RequiredResource"
}
],
"RuntimeDependencies": [
@ -33775,6 +33795,10 @@
"Path": "$(ProjectDir)/Plugins/DeskMode/DeskMode.uplugin",
"Type": "UFS"
},
{
"Path": "$(ProjectDir)/Plugins/Dialogue/Dialogue.uplugin",
"Type": "UFS"
},
{
"Path": "$(ProjectDir)/ProjectFish.uproject",
"Type": "UFS"
@ -33844,6 +33868,7 @@
"DatasmithContent",
"DeformerGraph",
"DeskMode",
"Dialogue",
"DumpGPUServices",
"EOSShared",
"EditorDataStorage",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
{
"BuildId": "37670630",
"Modules":
{
"Dialogue": "UnrealEditor-Dialogue.dll",
"DialogueEditor": "UnrealEditor-DialogueEditor.dll"
}
}

View File

@ -0,0 +1,29 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Dialogue",
"Description": "",
"Category": "Other",
"CreatedBy": "",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "Dialogue",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "DialogueEditor",
"Type": "Editor",
"LoadingPhase": "Default"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -0,0 +1,54 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class Dialogue : ModuleRules
{
public Dialogue(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
//test
OptimizeCode = CodeOptimization.Never;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}

View File

@ -0,0 +1,20 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Dialogue.h"
#define LOCTEXT_NAMESPACE "FDialogueModule"
void FDialogueModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FDialogueModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FDialogueModule, Dialogue)

View File

@ -0,0 +1,86 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueAsset.h"
#include "UObject/ObjectSaveContext.h"
bool UDialogueConditions::IsConditionMet_Implementation()
{
return true;
}
void UDialogueAsset::BeginDialogue()
{
CurrentNode = NodeDatas[0];
}
FText UDialogueAsset::GetNextNodeText()
{
if (CurrentNode->OutputPins.Num() > 0 && CurrentNode->OutputPins[0]->Connections.Num() > 0)
{
UDialogueRuntimePin* Connection = CurrentNode->OutputPins[0]->Connections[0];
UDialogueRuntimeNode* ConnectionParent = Connection->Parent;
if (ConnectionParent->NodeType == EDialogueNodeType::NPC)
{
//处理下一个对话是npc的情况
for (auto Pin: CurrentNode->OutputPins[0]->Connections)
{
UDialogueRuntimeNode* ConnectionNode = Pin->Parent;
if (ConnectionNode->NodeData->Conditionses.Num() != 0)
{
//判断是否符合条件
bool ConditionMet = false;
for (auto Condition: ConnectionNode->NodeData->Conditionses)
{
ConditionMet = ConditionMet || Condition->IsConditionMet();
}
if (ConditionMet)
{
CurrentNode = ConnectionNode;
return ConnectionNode->NodeData->Text;
}
}
else
{
CurrentNode = ConnectionNode;
return ConnectionNode->NodeData->Text;
}
}
}
else if (ConnectionParent->NodeType == EDialogueNodeType::Player)
{
//处理下一个对话是玩家选项的情况
TArray<FText> Options;
for (UDialogueRuntimePin* Pin : CurrentNode->OutputPins[0]->Connections)
{
Options.Add(Pin->Parent->NodeData->Text);
}
OnNeedPlayerSelect.Broadcast(Options);
//返回值不变
return CurrentNode->NodeData->Text;
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("DialogueNode has no output pin"));
}
return FText::FromString(TEXT(""));
}
void UDialogueAsset::PlayerSelect(int32 index)
{
CurrentNode = CurrentNode->OutputPins[0]->Connections[index]->Parent;
}
void UDialogueAsset::PreSave(FObjectPreSaveContext saveContext)
{
if (OnPreSaveListener)
{
OnPreSaveListener();
}
}

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueSettings.h"

View File

@ -0,0 +1,14 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FDialogueModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,124 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "DialogueAsset.generated.h"
UCLASS(Blueprintable, BlueprintType, abstract, EditInlineNew, HideCategories = ("DoNotShow"), CollapseCategories, AutoExpandCategories = ("Default"))
class DIALOGUE_API UDialogueConditions : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Dialogue Conditions")
bool IsConditionMet();
};
UENUM()
enum class EDialogueNodeType {
None,
Root,
NPC,
Player
};
UCLASS()
class DIALOGUE_API UDialogueRuntimePin : public UObject
{
GENERATED_BODY()
public:
UPROPERTY()
FName PinName;
UPROPERTY()
FGuid PinId;
UPROPERTY()
TArray<UDialogueRuntimePin*> Connections;
UPROPERTY()
class UDialogueRuntimeNode* Parent = nullptr;
UPROPERTY()
int32 Direction;
bool operator ==(const UDialogueRuntimePin& other) const
{
return PinId == other.PinId;
}
friend uint32 GetTypeHash(const UDialogueRuntimePin& other)
{
return GetTypeHash(other.PinId);
}
};
UCLASS()
class DIALOGUE_API UDialogueNodeData_Base : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Node", meta = (MultiLine = true))
FText Text;
UPROPERTY(Instanced, EditAnywhere, BlueprintReadWrite, Category = "Dialogue Node")
TArray<UDialogueConditions*> Conditionses;
};
UCLASS()
class DIALOGUE_API UDialogueRuntimeNode : public UObject
{
GENERATED_BODY()
public:
UPROPERTY()
UDialogueRuntimePin* InputPin;
UPROPERTY()
TArray<UDialogueRuntimePin*> OutputPins;
UPROPERTY()
FVector2D Position;
UPROPERTY()
UDialogueNodeData_Base* NodeData = nullptr;
UPROPERTY()
EDialogueNodeType NodeType = EDialogueNodeType::None;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNeedPlayeSelectDelegate, const TArray<FText>&, Options);
/**
*
*/
UCLASS(BlueprintType)
class DIALOGUE_API UDialogueAsset : public UDataAsset
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = Dialogue)
void BeginDialogue();
UFUNCTION(BlueprintPure, Category = Dialogue)
FText GetNextNodeText();
UFUNCTION(BlueprintCallable, Category = Dialogue)
void PlayerSelect(int32 index);
public:
//设置修改过程中回调
void SetPreSaveListener(std::function<void()> InPreSaveListener) { OnPreSaveListener = InPreSaveListener; }
virtual void PreSave(FObjectPreSaveContext saveContext) override;
public:
UPROPERTY(BlueprintAssignable, Category=Dialogue)
FNeedPlayeSelectDelegate OnNeedPlayerSelect;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Dialogue)
TArray<UDialogueRuntimeNode*> NodeDatas;
private:
UDialogueRuntimeNode* CurrentNode = nullptr;
std::function<void()> OnPreSaveListener = nullptr;
};

View File

@ -0,0 +1,16 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DialogueSettings.generated.h"
/**
*
*/
UCLASS(Config = Dialogue, DefaultConfig)
class DIALOGUE_API UDialogueSettings : public UObject
{
GENERATED_BODY()
};

View File

@ -0,0 +1,34 @@
using UnrealBuildTool;
public class DialogueEditor : ModuleRules
{
public DialogueEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
//test
OptimizeCode = CodeOptimization.Never;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"AssetTools"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"Dialogue",
"Projects",
"UnrealEd",
"GraphEditor",
"Kismet"
}
);
}
}

View File

@ -0,0 +1,345 @@
// 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);
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<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

View File

@ -0,0 +1,35 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueAssetFactory.h"
#include "DialogueAsset.h"
#define LOCTEXT_NAMESPACE "DialoguePluginEditor"
UDialogueAssetFactory::UDialogueAssetFactory(const FObjectInitializer& ObjectInitializer)
{
SupportedClass = UDialogueAsset::StaticClass();
}
UObject* UDialogueAssetFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags,
UObject* Context, FFeedbackContext* Warn, FName CallingContext)
{
UDialogueAsset* NewObjectAsset = NewObject<UDialogueAsset>(InParent, InClass, InName, Flags | RF_Transactional);
return NewObjectAsset;
}
FString UDialogueAssetFactory::GetDefaultNewAssetName() const
{
return FString(TEXT("NewDialogue"));
}
bool UDialogueAssetFactory::ShouldShowInNewMenu() const
{
return true;
}
FText UDialogueAssetFactory::GetDisplayName() const
{
return LOCTEXT("DialogueName", "Dialogue");
}
#undef LOCATEXT_NAMESPACE

View File

@ -0,0 +1,55 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueDetailsTabSummoner.h"
#include "DialogueAssetEditor.h"
#include "PropertyEditorModule.h"
#include "IDetailsView.h"
#define LOCTEXT_NAMESPACE "DialogueDetailsTabSummoner"
const FName DialogueDetailsTabId = FName(TEXT("DialogueDetailsTabId"));
FDialogueDetailsTabSummoner::FDialogueDetailsTabSummoner(TSharedPtr<FDialogueAssetEditor> InDialogueEditor)
: FWorkflowTabFactory(DialogueDetailsTabId, InDialogueEditor)
{
DialogueEditor = InDialogueEditor;
TabLabel = LOCTEXT("DialogueDetailsTabLabel", "Details");
ViewMenuDescription = LOCTEXT("DialogueDetails_ViewMenu_Desc", "Details");
ViewMenuTooltip = LOCTEXT("DialogueDetails_ViewMenu_ToolTip", "Show the Details Panel");
}
TSharedRef<SWidget> FDialogueDetailsTabSummoner::CreateTabBody(const FWorkflowTabSpawnInfo& Info) const
{
TSharedPtr<FDialogueAssetEditor> Editor = DialogueEditor.Pin();
FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
FDetailsViewArgs DetailsViewArgs;
DetailsViewArgs.bAllowSearch = false;
DetailsViewArgs.bHideSelectionTip = true;
DetailsViewArgs.bLockable = false;
DetailsViewArgs.bSearchInitialKeyFocus = true;
DetailsViewArgs.bUpdatesFromSelection = false;
DetailsViewArgs.NotifyHook = nullptr;
DetailsViewArgs.bShowOptions = true;
DetailsViewArgs.bShowModifiedPropertiesOption = false;
DetailsViewArgs.bShowScrollBar = false;
TSharedPtr<IDetailsView> DetailsView = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
DetailsView->SetObject(nullptr);
Editor->SetDetailsView(DetailsView);
return SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.HAlign(HAlign_Fill)
[
DetailsView.ToSharedRef()
];
}
FText FDialogueDetailsTabSummoner::GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const
{
return LOCTEXT("DialogueDetailsTooltip", "The details panel for selected nodes");
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,64 @@
#include "DialogueEditor.h"
#include "AssetToolsModule.h"
#include "DialogueEditorStyle.h"
#include "DialogueSettings.h"
#include "FDialogueAssetTypeActions.h"
#include "IAssetTools.h"
#include "ISettingsModule.h"
#define LOCTEXT_NAMESPACE "FDialogueEditorModule"
void FDialogueEditorModule::StartupModule()
{
//添加需要用到的图标资源
FDialogueEditorStyle::Initialize();
//添加对话相关属性设置
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->RegisterSettings("Project", "Plugins", "Dialogue Plugin",
LOCTEXT("SettinsName", "Dialogue Plugin"),
LOCTEXT("SettingsDes", "Configue Dialogue Plugin"),
GetMutableDefault<UDialogueSettings>()
);
}
//添加对话资源创建菜单
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
EAssetTypeCategories::Type DialogueAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Dialogue")), LOCTEXT("Dialogue", "Dialogue"));
TSharedRef<FAssetTypeActions_Base> DialogueAssetTypeActions = MakeShareable(new FDialogueAssetTypeActions(DialogueAssetCategory));
CreatedAssetTypeActions.Add(DialogueAssetTypeActions);
AssetTools.RegisterAssetTypeActions(DialogueAssetTypeActions);
// //添加自定义的属性窗口
// FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
// PropertyModule.RegisterCustomClassLayout("DialogueAsset", FOnGetDetailCustomizationInstance::CreateStatic(&FDialogueAssetEditorDetails::MakeInstance));
// PropertyModule.NotifyCustomizationModuleChanged();
}
void FDialogueEditorModule::ShutdownModule()
{
//注销图标资源
FDialogueEditorStyle::Shutdown();
//注销setting
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "Plugins", "Dialogue Plugin");
}
//注销typeaction
if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
{
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
IAssetTools& AssetTools = AssetToolsModule.Get();
for (auto Action : CreatedAssetTypeActions)
{
AssetTools.UnregisterAssetTypeActions(Action.ToSharedRef());
}
CreatedAssetTypeActions.Empty();
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FDialogueEditorModule, DialogueEditor)

View File

@ -0,0 +1,48 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueEditorMode.h"
#include "DialogueAssetEditor.h"
#include "DialogueGraphTabSummoner.h"
#include "DialogueDetailsTabSummoner.h"
FDialogueEditorMode::FDialogueEditorMode(TSharedPtr<FDialogueAssetEditor> InDialogueEditor)
: FApplicationMode(TEXT("DialogueEditorMode"))
{
DialogueEditor = InDialogueEditor;
// 注册Tab工厂
TabFactories.RegisterFactory(MakeShared<FDialogueGraphTabSummoner>(InDialogueEditor));
TabFactories.RegisterFactory(MakeShared<FDialogueDetailsTabSummoner>(InDialogueEditor));
// 定义布局
TabLayout = FTabManager::NewLayout("Standalone_DialogueEditor_Layout_v1")
->AddArea
(
FTabManager::NewPrimaryArea()
->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewSplitter()
->SetOrientation(Orient_Horizontal)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.7f)
->AddTab(FName(TEXT("DialogueGraphTabId")), ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.3f)
->AddTab(FName(TEXT("DialogueDetailsTabId")), ETabState::OpenedTab)
)
)
);
}
void FDialogueEditorMode::RegisterTabFactories(TSharedPtr<FTabManager> InTabManager)
{
TSharedPtr<FDialogueAssetEditor> Editor = DialogueEditor.Pin();
Editor->PushTabFactories(TabFactories);
FApplicationMode::RegisterTabFactories(InTabManager);
}

View File

@ -0,0 +1,45 @@
#include "DialogueEditorStyle.h"
#include "Styling/SlateStyle.h"
#include "Styling/SlateStyleRegistry.h"
#include "Interfaces/IPluginManager.h"
#include "Styling/SlateStyleRegistry.h"
TSharedPtr< FSlateStyleSet > FDialogueEditorStyle::StyleSet = nullptr;
TSharedPtr< ISlateStyle > FDialogueEditorStyle::Get() { return StyleSet; }
void FDialogueEditorStyle::Initialize()
{
if (StyleSet.IsValid())
{
return;
}
StyleSet = MakeShareable(new FSlateStyleSet(FName(TEXT("DialogueEditorStyle"))));
FString ResourceDir = IPluginManager::Get().FindPlugin("Dialogue")->GetBaseDir() / TEXT("Resources");
//加载图标相关资源
StyleSet->Set("PlayerNodeStyle", new FSlateBoxBrush(ResourceDir/"playerNode.png", FMargin(5.f / 138.f, 5.f / 56.f, 5.f / 138.f, 5.f / 56.f))); // left top right bottom
StyleSet->Set("NpcNodeStyle", new FSlateBoxBrush(ResourceDir/"npcNode.png", FMargin(5.f / 138.f, 5.f / 56.f, 5.f / 138.f, 5.f / 56.f)));
StyleSet->Set("StartNodeStyle", new FSlateBoxBrush(ResourceDir/"startNode.png", FMargin(5.f / 138.f, 5.f / 56.f, 5.f / 138.f, 5.f / 56.f)));
StyleSet->Set("EventIcon", new FSlateImageBrush(ResourceDir/"event.png", FVector2D(14, 16)));
StyleSet->Set("ConditionIcon", new FSlateImageBrush(ResourceDir/"condition.png", FVector2D(16, 16)));
StyleSet->Set("VoiceIcon", new FSlateImageBrush(ResourceDir/"sound.png", FVector2D(17, 16)));
StyleSet->Set(FName(TEXT("ClassThumbnail.DialogueAsset")), new FSlateImageBrush(ResourceDir/"dialogue_64.png", FVector2D(64, 64)));
StyleSet->Set(FName(TEXT("ClassIcon.DialogueAsset")), new FSlateImageBrush(ResourceDir/"dialogue_16.png", FVector2D(16, 16)));
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());
}
void FDialogueEditorStyle::Shutdown()
{
if (StyleSet.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get());
StyleSet.Reset();
}
}

View File

@ -0,0 +1,12 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueGraph.h"
UDialogueGraph::UDialogueGraph()
{
}
void UDialogueGraph::AddNode(UEdGraphNode* NodeToAdd, bool bUserAction, bool bSelectNewNode)
{
Super::AddNode(NodeToAdd, bUserAction, bSelectNewNode);
}

View File

@ -0,0 +1,19 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueGraphPanelNodeFactory.h"
#include "Node//DialogueGraphNode_NPC.h"
#include "Node/DialogueGraphNode_Player.h"
#include "Widget/DialogueGraphNode.h"
TSharedPtr<SGraphNode> FDialogueGraphPanelNodeFactory::CreateNode(class UEdGraphNode* InNode) const
{
if (UDialogueGraphNode_NPC* NPCNode = Cast<UDialogueGraphNode_NPC>(InNode))
return SNew(SDialogueGraphNode, NPCNode);
if (UDialogueGraphNode_Player* PlayerNode = Cast<UDialogueGraphNode_Player>(InNode))
return SNew(SDialogueGraphNode, PlayerNode);
return nullptr;
}

View File

@ -0,0 +1,26 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueGraphPanelPinFactory.h"
#include "Node/DialogueGraphNode_Base.h"
#include "Node/DialogueGraphNode_NPC.h"
#include "Node/DialogueGraphNode_Player.h"
#include "Widget/DialogueGraphPin.h"
TSharedPtr<class SGraphPin> FDialogueGraphPanelPinFactory::CreatePin(class UEdGraphPin* InPin) const
{
UEdGraphNode* ParentNode = InPin->GetOwningNode();
if (ParentNode->IsA(UDialogueGraphNode_NPC::StaticClass())
|| ParentNode->IsA(UDialogueGraphNode_Player::StaticClass()))
{
return SNew(SDialogueGraphPin, InPin);
}
else
{
return SNew(SDialogueNoneGraphPin, InPin);
}
// For response nodes, return horizontal box with textblock and pin
return nullptr;
}

View File

@ -0,0 +1,84 @@
// 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<UDialogueGraphNode_Base>(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<UDialogueGraphSchema>()->TryCreateConnection(FromPin, Pin);
break;
}
}
}
return NewNode;
}
void UDialogueGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const
{
FGraphNodeCreator<UDialogueGraphNode_Root> 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<FDialogueGraphSchemaAction_NewNode> 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<FDialogueGraphSchemaAction_NewNode> 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(""));
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,42 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DialogueGraphTabSummoner.h"
#include "DialogueAssetEditor.h"
#define LOCTEXT_NAMESPACE "DialogueGraphTabSummoner"
const FName DialogueGraphTabId = FName(TEXT("DialogueGraphTabId"));
FDialogueGraphTabSummoner::FDialogueGraphTabSummoner(TSharedPtr<FDialogueAssetEditor> InDialogueEditor)
: FWorkflowTabFactory(DialogueGraphTabId, InDialogueEditor)
{
DialogueEditor = InDialogueEditor;
TabLabel = LOCTEXT("DialogueGraphTabLabel", "Dialogue Graph");
ViewMenuDescription = LOCTEXT("DialogueGraph_ViewMenu_Desc", "Dialogue Graph");
ViewMenuTooltip = LOCTEXT("DialogueGraph_ViewMenu_ToolTip", "Show the Dialogue Graph");
}
TSharedRef<SWidget> FDialogueGraphTabSummoner::CreateTabBody(const FWorkflowTabSpawnInfo& Info) const
{
TSharedPtr<FDialogueAssetEditor> Editor = DialogueEditor.Pin();
SGraphEditor::FGraphEditorEvents GraphEvents;
GraphEvents.OnSelectionChanged.BindSP(Editor.Get(), &FDialogueAssetEditor::OnGraphSelectionChanged);
TSharedPtr<SGraphEditor> GraphEditor =
SNew(SGraphEditor)
.IsEditable(true)
.GraphEvents(GraphEvents)
.GraphToEdit(Editor->GetDialogueGraph());
Editor->SetGraphEditor(GraphEditor);
return GraphEditor.ToSharedRef();
}
FText FDialogueGraphTabSummoner::GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const
{
return LOCTEXT("DialogueGraphTooltip", "The dialogue graph editor");
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,52 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "FDialogueAssetTypeActions.h"
#include "DialogueAsset.h"
#include "DialogueAssetEditor.h"
#define LOCTEXT_NAMESPACE "AssetTypeActions"
FDialogueAssetTypeActions::FDialogueAssetTypeActions(uint32 InAssetCategory)
: MyAssetCategory(InAssetCategory)
{
}
FText FDialogueAssetTypeActions::GetName() const
{
return LOCTEXT("DialogueAssetTypeActionsName", "Dialogue");
}
FColor FDialogueAssetTypeActions::GetTypeColor() const
{
return FColor(255, 55, 220);
}
UClass* FDialogueAssetTypeActions::GetSupportedClass() const
{
return UDialogueAsset::StaticClass();
}
void FDialogueAssetTypeActions::OpenAssetEditor(const TArray<UObject*>& InObjects,
TSharedPtr<IToolkitHost> EditWithinLevelEditor)
{
EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;
for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt)
{
if (UDialogueAsset* DialogueAsset = Cast<UDialogueAsset>(*ObjIt))
{
TSharedRef<FDialogueAssetEditor> Editor = MakeShareable(new FDialogueAssetEditor());
Editor->InitDialogueEditor(Mode, EditWithinLevelEditor, DialogueAsset);
}
}
//FAssetTypeActions_Base::OpenAssetEditor(InObjects, EditWithinLevelEditor);
}
uint32 FDialogueAssetTypeActions::GetCategories()
{
return MyAssetCategory;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,34 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Node/DialogueGraphNode_Base.h"
#define LOCTEXT_NAMESPACE "DialogueGraphNode"
UDialogueGraphNode_Base::UDialogueGraphNode_Base()
{
}
void UDialogueGraphNode_Base::AllocateDefaultPins()
{
DefaultInputPin = CreatePin(EGPD_Input, TEXT("PinCategory_Default"), TEXT("In"));
CreatePin(EGPD_Output, TEXT("PinCategory_Default"), TEXT("Out"));
}
void UDialogueGraphNode_Base::SetPostion(FVector2D InPos)
{
NodePosX = InPos.X;
NodePosY = InPos.Y;
}
void UDialogueGraphNode_Base::InitializeNodeData()
{
NodeData = NewObject<UDialogueNodeData_Base>(this);
}
void UDialogueGraphNode_Base::OnPropertiesChanged()
{
GetGraph()->NotifyNodeChanged(this);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,14 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Node/DialogueGraphNode_NPC.h"
UDialogueGraphNode_NPC::UDialogueGraphNode_NPC()
{
}

View File

@ -0,0 +1,6 @@
#include "Node/DialogueGraphNode_Player.h"
UDialogueGraphNode_Player::UDialogueGraphNode_Player()
{
}

View File

@ -0,0 +1,9 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Node/DialogueGraphNode_Root.h"
void UDialogueGraphNode_Root::AllocateDefaultPins()
{
CreatePin(EGPD_Output, TEXT("PinCategory_Default"), TEXT("In"));
}

View File

@ -0,0 +1,62 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Widget/DialogueGraphNode.h"
void SDialogueGraphNode::Construct(const FArguments& InArgs, UDialogueGraphNode_Base* InNode)
{
GraphNode = InNode;
UpdateGraphNode();
}
FText SDialogueGraphNode::GetDialogueText() const
{
UDialogueGraphNode_Base* DialogueNode = Cast<UDialogueGraphNode_Base>(GraphNode);
return DialogueNode->GetDialogueNodeData()->Text;
}
TSharedRef<SWidget> SDialogueGraphNode::CreateNodeContentArea()
{
// NODE CONTENT AREA
return SNew(SBorder)
.BorderImage( FAppStyle::GetBrush("NoBorder") )
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding( FMargin(0,3) )
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.HAlign(HAlign_Left)
.FillWidth(1.0f)
[
// LEFT
SAssignNew(LeftNodeBox, SVerticalBox)
]
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Center)
.Padding(10.0f, 5.0f)
[
SNew(SBorder)
.BorderBackgroundColor(FSlateColor(FLinearColor::Gray))
.Padding(10.0f)
.Content()
[
SAssignNew(DialogueText, STextBlock)
.MinDesiredWidth(260.0f)
.Text(this, &SDialogueGraphNode::GetDialogueText)
.WrapTextAt(250.0f)
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
[
// RIGHT
SAssignNew(RightNodeBox, SVerticalBox)
]
];
}

View File

@ -0,0 +1,36 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Widget/DialogueGraphPin.h"
#include "Node/DialogueGraphNode_Base.h"
void SDialogueGraphPin::Construct(const FArguments& InArgs, UEdGraphPin* InPin)
{
SGraphPin::Construct(SGraphPin::FArguments(), InPin);
// FullPinHorizontalRowWidget.Pin()
// ->InsertSlot(0)
// [
// SNew(SBorder)
// .BorderBackgroundColor(FSlateColor(FLinearColor::Gray))
// .Padding(10.0f)
// .Content()
// [
// SAssignNew(DialogueText, STextBlock)
// .MinDesiredWidth(260.0f)
// .WrapTextAt(250.0f)
// .Text(this, &SDialogueGraphPin::GetDialogueText)
// ]
// ];
}
FText SDialogueGraphPin::GetDialogueText() const
{
UEdGraphPin* ParentPin = GetPinObj();
if (UDialogueGraphNode_Base* ResponseNode = Cast<UDialogueGraphNode_Base>(ParentPin->GetOwningNode()))
{
return ResponseNode->GetDialogueNodeData()->Text;
}
return FText();
}

View File

@ -0,0 +1,75 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DialogueAsset.h"
#include "GraphEditor.h"
#include "WorkflowOrientedApp/WorkflowCentricApplication.h"
class UDialogueGraph;
class IDetailsView;
class UDialogueGraphNode_Base;
/**
*
*/
class DIALOGUEEDITOR_API FDialogueAssetEditor : public FWorkflowCentricApplication, public FEditorUndoClient, public FNotifyHook
{
public:
/** IAssetEditorToolkit interface */
virtual FName GetToolkitFName() const override { return FName("DialogueEditor"); }
virtual FText GetBaseToolkitName() const override;
virtual FString GetWorldCentricTabPrefix() const override { return TEXT("DialogueEditor"); }
virtual FLinearColor GetWorldCentricTabColorScale() const override { return FLinearColor::White; }
virtual void OnClose() override;
/** IAssetEditorToolkit interface */
FDialogueAssetEditor();
virtual ~FDialogueAssetEditor();
public:
void InitDialogueEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, TObjectPtr<class UDialogueAsset> InitDialogueAsset);
// 获取图表
UEdGraph* GetDialogueGraph() const { return DialogueGraph; }
// 设置图表编辑器
void SetGraphEditor(TSharedPtr<SGraphEditor> InGraphEditor) { GraphEditor = InGraphEditor; }
// 设置细节面板
void SetDetailsView(TSharedPtr<IDetailsView> InDetailsView);
// 图表选择改变回调,传递属性页需要显示的内容
void OnGraphSelectionChanged(const FGraphPanelSelectionSet& Selection);
protected:
void SaveGraphData();
void LoadGraphData();
protected:
TObjectPtr<UDialogueAsset> DialogueBeingEdited;
// 图表对象
UEdGraph* DialogueGraph;
// 图表编辑器Widget
TSharedPtr<SGraphEditor> GraphEditor;
// 细节面板
TSharedPtr<IDetailsView> DetailsView;
private:
// 绑定图表命令
void BindGraphCommands();
// 删除选中节点
void DeleteSelectedNodes();
bool CanDeleteNodes();
// 获取选中节点
UDialogueGraphNode_Base* GetSelectedNode(const FGraphPanelSelectionSet& Selection);
// 属性更新回调
void OnNodeDetailViewPropertiesUpdated(const FPropertyChangedEvent& Event);
};

View File

@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "DialogueAssetFactory.generated.h"
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueAssetFactory : public UFactory
{
GENERATED_BODY()
public:
UDialogueAssetFactory(const FObjectInitializer& ObjectInitializer);
virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override;
virtual FText GetDisplayName() const override;
virtual FString GetDefaultNewAssetName() const override;
virtual bool ShouldShowInNewMenu() const override;
};

View File

@ -0,0 +1,25 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "WorkflowOrientedApp/WorkflowTabFactory.h"
class FDialogueAssetEditor;
/**
* Tab工厂
*/
class FDialogueDetailsTabSummoner : public FWorkflowTabFactory
{
public:
FDialogueDetailsTabSummoner(TSharedPtr<FDialogueAssetEditor> InDialogueEditor);
private:
TWeakPtr<FDialogueAssetEditor> DialogueEditor;
public:
/** FWorkflowTabFactory Interface */
virtual TSharedRef<SWidget> CreateTabBody(const FWorkflowTabSpawnInfo& Info) const override;
virtual FText GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const override;
};

View File

@ -0,0 +1,14 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FDialogueEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
TArray<TSharedPtr<class FAssetTypeActions_Base>>CreatedAssetTypeActions;
};

View File

@ -0,0 +1,27 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "WorkflowOrientedApp/ApplicationMode.h"
#include "WorkflowOrientedApp/WorkflowTabManager.h"
/**
*
*/
class FDialogueEditorMode : public FApplicationMode
{
public:
FDialogueEditorMode(TSharedPtr<class FDialogueAssetEditor> InDialogueEditor);
private:
TWeakPtr<class FDialogueAssetEditor> DialogueEditor;
FWorkflowAllowedTabSet TabFactories;
public:
/** FApplicationMode */
virtual void RegisterTabFactories(TSharedPtr<FTabManager> InTabManager) override;
};

View File

@ -0,0 +1,12 @@
#pragma once
class FDialogueEditorStyle
{
public:
static void Initialize();
static void Shutdown();
static TSharedPtr< class ISlateStyle > Get();
private:
static TSharedPtr<class FSlateStyleSet> StyleSet;
};

View File

@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "EdGraph/EdGraph.h"
#include "DialogueGraph.generated.h"
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueGraph : public UEdGraph
{
GENERATED_BODY()
public:
UDialogueGraph();
/** UEdGraph interface */
virtual void AddNode(UEdGraphNode* NodeToAdd, bool bUserAction, bool bSelectNewNode) override;
};

View File

@ -0,0 +1,15 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "EdGraphUtilities.h"
/**
*
*/
class DIALOGUEEDITOR_API FDialogueGraphPanelNodeFactory: public FGraphPanelNodeFactory
{
public:
virtual TSharedPtr<class SGraphNode> CreateNode(class UEdGraphNode* Node) const override;
};

View File

@ -0,0 +1,15 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DialogueGraphPanelNodeFactory.h"
/**
*
*/
class DIALOGUEEDITOR_API FDialogueGraphPanelPinFactory: public FGraphPanelPinFactory
{
public:
virtual TSharedPtr<class SGraphPin> CreatePin(class UEdGraphPin* InPin) const override;
};

View File

@ -0,0 +1,45 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "EdGraph/EdGraphSchema.h"
#include "DialogueGraphSchema.generated.h"
/**
* Action
*/
USTRUCT()
struct FDialogueGraphSchemaAction_NewNode : public FEdGraphSchemaAction
{
GENERATED_BODY()
public:
FDialogueGraphSchemaAction_NewNode() : FEdGraphSchemaAction() {}
FDialogueGraphSchemaAction_NewNode(UClass* InClassTemplate, FText InNodeCategory, FText InMenuDesc, FText InToolTip)
: FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, 0), ClassTemplate(InClassTemplate) {}
virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override;
protected:
UClass* ClassTemplate = nullptr;
};
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueGraphSchema : public UEdGraphSchema
{
GENERATED_BODY()
public:
/** UEdGraphSchema */
virtual void CreateDefaultNodesForGraph(UEdGraph& Graph) const override;
// 右键菜单创建节点
virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override;
// 连接规则
virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override;
};

View File

@ -0,0 +1,25 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "WorkflowOrientedApp/WorkflowTabFactory.h"
class FDialogueAssetEditor;
/**
* Tab工厂
*/
class FDialogueGraphTabSummoner : public FWorkflowTabFactory
{
public:
FDialogueGraphTabSummoner(TSharedPtr<FDialogueAssetEditor> InDialogueEditor);
private:
TWeakPtr<FDialogueAssetEditor> DialogueEditor;
public:
/** FWorkflowTabFactory Interface */
virtual TSharedRef<SWidget> CreateTabBody(const FWorkflowTabSpawnInfo& Info) const override;
virtual FText GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const override;
};

View File

@ -0,0 +1,26 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "AssetTypeActions_Base.h"
/**
*
*/
class DIALOGUEEDITOR_API FDialogueAssetTypeActions:public FAssetTypeActions_Base
{
public:
FDialogueAssetTypeActions(uint32 InAssetCategory);
/** IAssetTypeActions interface */
virtual FText GetName() const override;
virtual FColor GetTypeColor() const override;
virtual UClass* GetSupportedClass() const override;
virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
virtual uint32 GetCategories() override;
/** IAssetTypeActions interface */
private:
uint32 MyAssetCategory;
};

View File

@ -0,0 +1,48 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DialogueAsset.h"
#include "DialogueGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "DialogueGraphNode_Base.generated.h"
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueGraphNode_Base : public UEdGraphNode
{
GENERATED_BODY()
public:
UDialogueGraphNode_Base();
/** UEdGraphNode */
virtual bool CanUserDeleteNode() const override { return true; }
virtual void AllocateDefaultPins() override;
void SetPostion(FVector2D InPos);
void SetDialogueNodeData(UDialogueNodeData_Base* InNodeData) { NodeData = InNodeData; }
virtual void InitializeNodeData();
virtual void OnPropertiesChanged();
UEdGraphPin* GetDefaultInputPin() { return DefaultInputPin; }
UDialogueNodeData_Base* GetDialogueNodeData() {return NodeData;}
virtual EDialogueNodeType GetDialogueNodeType() { return EDialogueNodeType::None; }
// // 节点标题
// virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
//
// // 节点颜色
// virtual FLinearColor GetNodeTitleColor() const override;
//
protected:
UEdGraphPin* DefaultInputPin = nullptr;
UPROPERTY()
UDialogueNodeData_Base* NodeData;
};

View File

@ -0,0 +1,24 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DialogueGraphNode_Base.h"
#include "DialogueGraphNode_NPC.generated.h"
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueGraphNode_NPC: public UDialogueGraphNode_Base
{
GENERATED_BODY()
public:
UDialogueGraphNode_NPC();
/** UEdGraphNode Interface */
virtual FText GetNodeTitle(ENodeTitleType::Type titalType) const override { return FText::FromString("NPC"); }
virtual FLinearColor GetNodeTitleColor() const override { return FLinearColor(FColor::Green); }
virtual EDialogueNodeType GetDialogueNodeType() override { return EDialogueNodeType::NPC;}
};

View File

@ -0,0 +1,19 @@
#pragma once
#include "CoreMinimal.h"
#include "DialogueGraphNode_Base.h"
#include "DialogueGraphNode_Player.generated.h"
UCLASS()
class UDialogueGraphNode_Player: public UDialogueGraphNode_Base
{
GENERATED_BODY()
public:
UDialogueGraphNode_Player();
/** UEdGraphNode Interface */
virtual FText GetNodeTitle(ENodeTitleType::Type titalType) const override { return FText::FromString("Player"); }
virtual FLinearColor GetNodeTitleColor() const override { return FLinearColor(FColor::Green); }
virtual EDialogueNodeType GetDialogueNodeType() override { return EDialogueNodeType::Player;}
};

View File

@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DialogueGraph.h"
#include "DialogueGraphNode_Base.h"
#include "DialogueGraphNode_Root.generated.h"
/**
*
*/
UCLASS()
class DIALOGUEEDITOR_API UDialogueGraphNode_Root: public UDialogueGraphNode_Base
{
GENERATED_BODY()
public:
/** UEdGraphNode Interface */
virtual FText GetNodeTitle(ENodeTitleType::Type titalType) const override { return FText::FromString("Start"); }
virtual FLinearColor GetNodeTitleColor() const override { return FLinearColor(FColor::Red); }
virtual bool CanUserDeleteNode() const override { return false; }
virtual void AllocateDefaultPins() override;
virtual EDialogueNodeType GetDialogueNodeType() override { return EDialogueNodeType::Root;}
};

View File

@ -0,0 +1,26 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Node/DialogueGraphNode_Base.h"
#include "SGraphNode.h"
/**
*
*/
class DIALOGUEEDITOR_API SDialogueGraphNode: public SGraphNode
{
public:
SLATE_BEGIN_ARGS(SDialogueGraphNode)
{}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, UDialogueGraphNode_Base* InNode);
FText GetDialogueText() const;
/** SGraphNode Interface */
virtual TSharedRef<SWidget> CreateNodeContentArea() override;
private:
TSharedPtr<STextBlock> DialogueText;
};

View File

@ -0,0 +1,48 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SGraphPin.h"
/**
* pin
*/
class DIALOGUEEDITOR_API SDialogueNoneGraphPin: public SGraphPin
{
public:
SLATE_BEGIN_ARGS(SDialogueNoneGraphPin)
{}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) {
SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj);
}
protected:
virtual FSlateColor GetPinColor() const override {
return FSlateColor(FLinearColor::White);
}
};
/**
* pin
*/
class DIALOGUEEDITOR_API SDialogueGraphPin: public SGraphPin
{
public:
SLATE_BEGIN_ARGS(SDialogueGraphPin)
{}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, UEdGraphPin* InPin);
FText GetDialogueText() const;
protected:
/** SGraphPin Interface */
virtual FSlateColor GetPinColor() const override {return FSlateColor(FLinearColor::White);}
private:
TSharedPtr<STextBlock> DialogueText;
};