From 2af5ba13576bb7c1371b5465ffb3ec303f76d0f1 Mon Sep 17 00:00:00 2001 From: nakjun Date: Fri, 19 Jun 2026 15:30:42 +0900 Subject: [PATCH] =?UTF-8?q?2026-06-19=20=EB=8B=A4=EC=9D=B4=EC=96=BC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EA=B7=B8=EB=9E=98=ED=94=84=ED=88=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialog/CatsRoomDialogTest.unity | 3 + .../Dialog/CatsRoomDialogTest.unity.meta | 7 + .../Communication/Dialog/GraphTool.meta | 8 + .../Dialog/GraphTool/Editor.meta | 8 + .../Dialog/GraphTool/Editor/DialogGraph.cs | 43 +++ .../GraphTool/Editor/DialogGraph.cs.meta | 2 + .../GraphTool/Editor/DialogGraphImporter.cs | 166 ++++++++++++ .../Editor/DialogGraphImporter.cs.meta | 2 + .../GraphTool/Editor/DialogGraphNode.cs | 33 +++ .../GraphTool/Editor/DialogGraphNode.cs.meta | 2 + .../Dialog/GraphTool/Editor/DialogLineNode.cs | 75 ++++++ .../GraphTool/Editor/DialogLineNode.cs.meta | 2 + .../GraphTool/Editor/DialogStartNode.cs | 16 ++ .../GraphTool/Editor/DialogStartNode.cs.meta | 2 + .../Dialog/GraphTool/Editor/DialogText.cs | 17 ++ .../GraphTool/Editor/DialogText.cs.meta | 2 + .../GraphTool/Editor/DialogTextDrawer.cs | 30 +++ .../GraphTool/Editor/DialogTextDrawer.cs.meta | 2 + .../Communication/Dialog/GraphTool/README.md | 47 ++++ .../Dialog/GraphTool/README.md.meta | 7 + Assets/07_Data/Character/Cat1.asset | 3 + Assets/07_Data/Character/Cat1.asset.meta | 8 + Assets/07_Data/Communication/DialogGraph.meta | 8 + .../DialogGraph/DialogGraph_Test.wdg | 248 ++++++++++++++++++ .../DialogGraph/DialogGraph_Test.wdg.meta | 10 + .../Dialogue Datas/Gepetto_Intro.asset | 4 +- 26 files changed, 753 insertions(+), 2 deletions(-) create mode 100644 Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity create mode 100644 Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs.meta create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/README.md create mode 100644 Assets/02_Scripts/Communication/Dialog/GraphTool/README.md.meta create mode 100644 Assets/07_Data/Character/Cat1.asset create mode 100644 Assets/07_Data/Character/Cat1.asset.meta create mode 100644 Assets/07_Data/Communication/DialogGraph.meta create mode 100644 Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg create mode 100644 Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg.meta diff --git a/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity new file mode 100644 index 00000000..2438f4cc --- /dev/null +++ b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e28e5a3621722baa64499bcb60bd24f045f86eb725b9ccc3bc2db33147168b5 +size 2080396 diff --git a/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity.meta b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity.meta new file mode 100644 index 00000000..bc05b26e --- /dev/null +++ b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 22ed8e3dbc3ea934f8aac5f96aa1ba2f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool.meta new file mode 100644 index 00000000..6f17bd5d --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb0dad36a50363647a4b9cd61d078b20 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor.meta new file mode 100644 index 00000000..a17308a2 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bad231f70f838c84288d07f754f51ed7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs new file mode 100644 index 00000000..665c3783 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs @@ -0,0 +1,43 @@ +using System.Linq; +using Unity.GraphToolkit.Editor; +using UnityEditor; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // 고래 대화 그래프. + // 기존 Communication/Dialog 시스템(DialogGroup / DialogNode / DialogChoice)을 + // 노드 그래프로 저작하기 위한 에디터 전용 그래프 타입이다. + // 임포트 시 DialogGraphImporter가 이 그래프를 DialogGroup 에셋으로 변환한다. + [Graph(AssetExtension)] + internal class DialogGraph : Graph + { + // ScriptedImporter가 사용하는 확장자. 프로젝트 내에서 유일해야 한다. + public const string AssetExtension = "wdg"; // Whale Dialog Graph + + const string k_DefaultName = "New Dialog Graph"; + + [MenuItem("Assets/Create/Communication/Dialog Graph")] + static void CreateAssetFile() + { + GraphDatabase.PromptInProjectBrowserToCreateNewAsset(k_DefaultName); + } + + // 그래프가 바뀔 때마다 호출되어 에러/경고를 보고한다. + public override void OnGraphChanged(GraphLogger infos) + { + base.OnGraphChanged(infos); + + var startNodes = GetNodes().OfType().ToList(); + switch (startNodes.Count) + { + case 0: + infos.LogError("Start 노드가 필요합니다. (Dialog Start Node를 추가하세요)", this); + break; + case >= 2: + foreach (var extra in startNodes.Skip(1)) + infos.LogWarning("Start 노드는 하나만 사용됩니다. 가장 먼저 생성된 노드만 적용됩니다.", extra); + break; + } + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs.meta new file mode 100644 index 00000000..934accb8 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraph.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f25a0745082bd7c439679495337d3598 \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs new file mode 100644 index 00000000..9dd57ab1 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs @@ -0,0 +1,166 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Unity.GraphToolkit.Editor; +using UnityEditor.AssetImporters; +using UnityEngine; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // .wdg 그래프 에셋을 기존 런타임 타입(DialogGroup / DialogNode / DialogChoice)으로 변환한다. + // 생성된 DialogNode들은 서브에셋으로, DialogGroup이 메인 에셋으로 등록된다. + // 따라서 DialogPlayer는 수정 없이 임포트된 .wdg 에셋(= DialogGroup)을 그대로 사용한다. + [ScriptedImporter(1, DialogGraph.AssetExtension)] + internal class DialogGraphImporter : ScriptedImporter + { + public override void OnImportAsset(AssetImportContext ctx) + { + var graph = GraphDatabase.LoadGraphForImporter(ctx.assetPath); + if (graph == null) + { + Debug.LogError($"[DialogGraphImporter] 그래프 로드 실패: {ctx.assetPath}"); + return; + } + + // 메인 에셋: DialogGroup (이름은 파일명 기준 — DialogPlayer가 이름으로 조회) + var groupName = Path.GetFileNameWithoutExtension(ctx.assetPath); + var group = ScriptableObject.CreateInstance(); + group.name = groupName; + group.DialogGroupName = groupName; + ctx.AddObjectToAsset("Group", group); + ctx.SetMainObject(group); + + var startNode = graph.GetNodes().OfType().FirstOrDefault(); + if (startNode == null) + return; // OnGraphChanged에서 에러 로깅됨 + + var firstGraphNode = GetConnectedNode(startNode, DialogGraphNode.EXEC_OUT); + if (firstGraphNode == null) + return; // Start만 있고 연결 없음 + + // 1패스: 도달 가능한 모든 라인 노드 → DialogNode 인스턴스 생성 (중복 제거) + var map = new Dictionary(); + var order = new List(); + var queue = new Queue(); + queue.Enqueue(firstGraphNode); + + while (queue.Count > 0) + { + var gn = queue.Dequeue(); + if (gn == null || map.ContainsKey(gn) || gn is not DialogLineNode) + continue; + + var dn = ScriptableObject.CreateInstance(); + dn.Choices = new List(); + map[gn] = dn; + order.Add(gn); + + foreach (var next in GetSuccessors(gn)) + if (next != null && !map.ContainsKey(next)) + queue.Enqueue(next); + } + + // 서브에셋 등록 + 이름 지정 + for (int i = 0; i < order.Count; i++) + { + var dn = map[order[i]]; + dn.name = $"Node_{i:00}"; + ctx.AddObjectToAsset(dn.name, dn); + } + + // 2패스: 데이터/링크 채우기 + foreach (var gn in order) + { + var line = (DialogLineNode)gn; + var dn = map[gn]; + + dn.Speaker = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_SPEAKER)); + dn.TalkText = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_TALK)).Value; + dn.Gesture = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_GESTURE)); + dn.Expression = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_EXPRESSION)); + dn.Voice = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_VOICE)); + dn.LineDuration = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_DURATION)); + dn.LookAtPlayer = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_LOOKAT)); + + int choiceCount = 0; + line.GetNodeOptionByName(DialogLineNode.OPTION_CHOICE_COUNT)?.TryGetValue(out choiceCount); + + if (choiceCount <= 0) + { + var next = GetConnectedNode(gn, DialogGraphNode.EXEC_OUT); + dn.Next = next != null && map.TryGetValue(next, out var nextDn) ? nextDn : null; + } + else + { + dn.ChoiceQuestion = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.PORT_QUESTION)); + for (int i = 0; i < choiceCount; i++) + { + var choiceText = GetInputPortValue(gn.GetInputPortByName(DialogLineNode.ChoiceTextPort(i))); + var dest = GetConnectedNode(gn, DialogLineNode.ChoiceOutPort(i)); + dn.Choices.Add(new DialogChoice + { + ChoiceText = choiceText, + DestinationNode = dest != null && map.TryGetValue(dest, out var destDn) ? destDn : null + }); + } + } + } + + group.StartNode = map.TryGetValue(firstGraphNode, out var startDn) ? startDn : null; + } + + // 노드의 실행 흐름상 후속 노드들 (선형이면 1개, N지선다면 N개) + static IEnumerable GetSuccessors(INode node) + { + if (node is not DialogLineNode line) + yield break; + + int choiceCount = 0; + line.GetNodeOptionByName(DialogLineNode.OPTION_CHOICE_COUNT)?.TryGetValue(out choiceCount); + + if (choiceCount <= 0) + { + yield return GetConnectedNode(node, DialogGraphNode.EXEC_OUT); + } + else + { + for (int i = 0; i < choiceCount; i++) + yield return GetConnectedNode(node, DialogLineNode.ChoiceOutPort(i)); + } + } + + // 출력 실행 포트에 연결된 노드 (없으면 null) + static INode GetConnectedNode(INode node, string outputPortName) + { + var port = node.GetOutputPortByName(outputPortName); + return port?.firstConnectedPort?.GetNode(); + } + + // 입력 포트 값 읽기. (연결된 변수/상수 노드 → 임베드 값 → 기본값 순) + static T GetInputPortValue(IPort port) + { + T value = default; + if (port == null) + return value; + + if (port.isConnected) + { + switch (port.firstConnectedPort.GetNode()) + { + case IVariableNode variableNode: + variableNode.variable.TryGetDefaultValue(out value); + return value; + case IConstantNode constantNode: + constantNode.TryGetValue(out value); + return value; + } + } + else + { + port.TryGetValue(out value); + } + + return value; + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs.meta new file mode 100644 index 00000000..b5a67fdf --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphImporter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2ae5ca89bbed445479d9023586f0c041 \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs new file mode 100644 index 00000000..860dcdeb --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs @@ -0,0 +1,33 @@ +using System; +using Unity.GraphToolkit.Editor; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // 대화 그래프 노드들의 공통 베이스. + // 실행 흐름(Execution) 포트를 추가하는 헬퍼를 제공한다. + // 실행 포트는 화살촉(Arrowhead) 커넥터를 쓰고, 데이터 포트(원형)와 구분된다. + [Serializable] + internal abstract class DialogGraphNode : Node + { + public const string EXEC_IN = "In"; + public const string EXEC_OUT = "Out"; + + // 입력 실행 포트 (이 노드로 들어오는 흐름) + protected void AddExecInput(IPortDefinitionContext context) + { + context.AddInputPort(EXEC_IN) + .WithDisplayName(string.Empty) + .WithConnectorUI(PortConnectorUI.Arrowhead) + .Build(); + } + + // 출력 실행 포트 (이 노드에서 나가는 흐름) + protected void AddExecOutput(IPortDefinitionContext context, string portName, string displayName) + { + context.AddOutputPort(portName) + .WithDisplayName(displayName) + .WithConnectorUI(PortConnectorUI.Arrowhead) + .Build(); + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs.meta new file mode 100644 index 00000000..6af1028a --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogGraphNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: be386f8d9f138f8449c9e84034f5e1ef \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs new file mode 100644 index 00000000..69b6577f --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs @@ -0,0 +1,75 @@ +using System; +using Unity.GraphToolkit.Editor; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // 대사 한 노드. DialogNode 한 개로 변환된다. + // + // ChoiceCount 옵션으로 분기 방식을 정한다: + // - 0 : 선형 진행. 출력 실행 포트 "Out" 하나(→ DialogNode.Next) + // - 1 이상 : 가변 N지선다. ChoiceQuestion + 각 선택지마다 + // [Choice{i} Text 입력 포트] + [Choice{i} 출력 실행 포트] 생성 + // (→ DialogNode.Choices / ChoiceQuestion) + [Serializable] + internal class DialogLineNode : DialogGraphNode + { + public const string PORT_SPEAKER = "Speaker"; + public const string PORT_TALK = "TalkText"; + public const string PORT_GESTURE = "Gesture"; + public const string PORT_EXPRESSION = "Expression"; + public const string PORT_VOICE = "Voice"; + public const string PORT_DURATION = "LineDuration"; + public const string PORT_LOOKAT = "LookAtPlayer"; + public const string PORT_QUESTION = "ChoiceQuestion"; + + public const string OPTION_CHOICE_COUNT = "ChoiceCount"; + + // 선택지별 포트 이름 규칙 (임포터와 공유) + public static string ChoiceTextPort(int i) => $"Choice{i}Text"; + public static string ChoiceOutPort(int i) => $"Choice{i}Out"; + + protected override void OnDefineOptions(IOptionDefinitionContext context) + { + context.AddOption(OPTION_CHOICE_COUNT) + .WithDisplayName("Choice Count") + .WithTooltip("0이면 선형 진행(Next), 1 이상이면 가변 N지선다 분기") + .WithDefaultValue(0) + .Delayed(); + } + + protected override void OnDefinePorts(IPortDefinitionContext context) + { + AddExecInput(context); + + // DialogNode의 라인 데이터 (모두 선택 입력, 비워두면 default) + context.AddInputPort(PORT_SPEAKER).WithDisplayName("Speaker").Build(); + context.AddInputPort(PORT_TALK).WithDisplayName("Talk Text").Build(); + context.AddInputPort(PORT_GESTURE).WithDisplayName("Gesture").Build(); + context.AddInputPort(PORT_EXPRESSION).WithDisplayName("Expression").Build(); + context.AddInputPort(PORT_VOICE).WithDisplayName("Voice").Build(); + context.AddInputPort(PORT_DURATION).WithDisplayName("Line Duration").Build(); + context.AddInputPort(PORT_LOOKAT).WithDisplayName("Look At Player").Build(); + + int choiceCount = 0; + GetNodeOptionByName(OPTION_CHOICE_COUNT)?.TryGetValue(out choiceCount); + + if (choiceCount <= 0) + { + // 선형 진행 + AddExecOutput(context, EXEC_OUT, string.Empty); + return; + } + + // 가변 N지선다 + context.AddInputPort(PORT_QUESTION).WithDisplayName("Choice Question").Build(); + + for (int i = 0; i < choiceCount; i++) + { + context.AddInputPort(ChoiceTextPort(i)) + .WithDisplayName($"Choice {i + 1} Text") + .Build(); + AddExecOutput(context, ChoiceOutPort(i), $"Choice {i + 1} →"); + } + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs.meta new file mode 100644 index 00000000..c70c3f22 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogLineNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: be6c5a15779e30f40910985e5d7cfbd9 \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs new file mode 100644 index 00000000..23e99527 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs @@ -0,0 +1,16 @@ +using System; +using Unity.GraphToolkit.Editor; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // 대화의 진입점. 출력 실행 포트 하나만 가진다. + // 임포터는 이 노드에 연결된 첫 노드를 DialogGroup.StartNode로 설정한다. + [Serializable] + internal class DialogStartNode : DialogGraphNode + { + protected override void OnDefinePorts(IPortDefinitionContext context) + { + AddExecOutput(context, EXEC_OUT, string.Empty); + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs.meta new file mode 100644 index 00000000..0c039946 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogStartNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4f76763c19a33214ca518048c8a89799 \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs new file mode 100644 index 00000000..0324a228 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs @@ -0,0 +1,17 @@ +using System; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // 그래프 노드의 TalkText 포트를 여러 줄(멀티라인)로 편집하기 위한 래퍼 타입. + // 전용 DialogTextDrawer가 multiline TextField로 렌더한다. + // 임포트 시 Value 문자열만 DialogNode.TalkText로 전달된다(런타임은 이 타입을 모름). + // + // public인 이유: GraphToolkit이 포트 임베드 값을 편집할 때 이 타입을 감싸는 + // 래퍼 ScriptableObject를 Reflection.Emit으로 다른 어셈블리에 생성하므로 + // 접근 가능해야 한다. + [Serializable] + public struct DialogText + { + public string Value; + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs.meta new file mode 100644 index 00000000..c8ed0701 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogText.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9ad54cf039f672845a54666166b5021c \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs new file mode 100644 index 00000000..dfd6ab7f --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace WhaleAdventure.Dialog.GraphTool.Editor +{ + // DialogText를 여러 줄 입력 필드로 그린다. + // GraphToolkit의 포트 값 에디터(ConstantField)는 CustomPropertyDrawer가 있는 타입을 + // Unity PropertyField로 렌더하므로, 이 드로어가 노드/인스펙터의 TalkText 칸을 멀티라인으로 만든다. + [CustomPropertyDrawer(typeof(DialogText))] + internal class DialogTextDrawer : PropertyDrawer + { + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + var valueProp = property.FindPropertyRelative(nameof(DialogText.Value)); + + var field = new TextField + { + multiline = true + }; + field.style.minHeight = 72; // 약 4~5줄 높이 + field.style.whiteSpace = WhiteSpace.Normal; // 줄바꿈(wrap) 허용 + + if (valueProp != null) + field.BindProperty(valueProp); + + return field; + } + } +} diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs.meta new file mode 100644 index 00000000..341459a3 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/Editor/DialogTextDrawer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d31004aee38951a4faaff37d886d3a65 \ No newline at end of file diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md b/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md new file mode 100644 index 00000000..b4ed90e4 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md @@ -0,0 +1,47 @@ +# Dialog Graph Tool + +`Communication/Dialog` 시스템(`DialogGroup` / `DialogNode` / `DialogChoice`)을 +**노드 그래프로 저작**하기 위한 에디터 전용 도구입니다. +Unity GraphToolkit(`com.unity.graphtoolkit`, experimental) 기반. + +## 동작 개요 +- `.wdg` 그래프 에셋을 노드로 편집 → 저장하면 `DialogGraphImporter`가 + 기존 런타임 타입(`DialogGroup` + 여러 `DialogNode`)으로 자동 변환합니다. +- 변환 결과: **메인 에셋 = `DialogGroup`**, 서브에셋 = 각 `DialogNode`. +- `DialogPlayer`는 **수정 없이** 임포트된 `.wdg`(=DialogGroup)를 그대로 사용합니다. + +## 사용법 +1. Project 창에서 우클릭 → `Create > Communication > Dialog Graph` 로 `.wdg` 생성. +2. 더블클릭해 그래프 에디터를 엽니다. +3. 노드 추가: + - **Dialog Start Node** : 진입점. 출력 화살표를 첫 대사 노드에 연결. (그래프당 1개) + - **Dialog Line Node** : 대사 1줄. Speaker/TalkText/Gesture/Expression/Voice/ + LineDuration/LookAtPlayer 입력. + - `Choice Count = 0` → 선형. `Out` 출력을 다음 노드로 연결(= `DialogNode.Next`). + - `Choice Count = N` → 가변 N지선다. `Choice Question` + 선택지마다 + `Choice i Text`(텍스트) 와 `Choice i →`(분기 출력) 생성. + 각 분기 출력을 목적지 노드에 연결(= `DialogNode.Choices[i].DestinationNode`). +4. 저장(임포트)되면 `.wdg` 에셋이 `DialogGroup`이 됩니다. + 이를 `DialogPlayer`의 `_dialogGroups` 리스트에 드래그하면 끝. + (그룹 이름 = 파일명. `DialogPlayer.Play(groupName)` 으로 호출) + +## 구성 파일 (모두 Editor 전용) +- `DialogGraph.cs` — 그래프 타입/생성 메뉴/검증 +- `DialogGraphNode.cs` — 공통 베이스(실행 포트 헬퍼) +- `DialogStartNode.cs` — 진입 노드 +- `DialogLineNode.cs` — 대사 + 가변 N지선다 노드 +- `DialogText.cs` — TalkText 멀티라인 입력용 래퍼 타입 +- `DialogTextDrawer.cs` — DialogText를 여러 줄 TextField로 그리는 CustomPropertyDrawer +- `DialogGraphImporter.cs`— .wdg → DialogGroup/DialogNode 변환 + +## TalkText 멀티라인 +- TalkText 포트는 `string`이 아니라 `DialogText` 타입을 쓴다. +- GraphToolkit은 `[CustomPropertyDrawer]`가 있는 타입을 Unity PropertyField로 렌더하므로, + `DialogTextDrawer`가 노드의 TalkText 칸을 여러 줄(멀티라인)로 만든다. +- 임포터는 `DialogText.Value`만 꺼내 `DialogNode.TalkText`(string)에 넣는다 — 런타임은 영향 없음. +- 높이를 더 키우려면 `DialogTextDrawer`의 `minHeight` 값을 조정. + +## 메모 +- GraphToolkit은 experimental(0.4.0-exp.2)이라 API가 바뀔 수 있습니다. +- 분기 출력이 비어 있으면 해당 선택지의 `DestinationNode`는 null이 되어 대화가 종료됩니다. +- 여러 경로에서 같은 노드로 연결하면(루프 포함) 하나의 `DialogNode`로 합쳐집니다. diff --git a/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md.meta b/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md.meta new file mode 100644 index 00000000..c931f913 --- /dev/null +++ b/Assets/02_Scripts/Communication/Dialog/GraphTool/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1fa58401314123a4b90fa0fda5240a18 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/Character/Cat1.asset b/Assets/07_Data/Character/Cat1.asset new file mode 100644 index 00000000..2eaf1777 --- /dev/null +++ b/Assets/07_Data/Character/Cat1.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbde8c3ec44964fbe983fce292694d5850162b9d2811485a51e6cd875f386ec1 +size 545 diff --git a/Assets/07_Data/Character/Cat1.asset.meta b/Assets/07_Data/Character/Cat1.asset.meta new file mode 100644 index 00000000..d708918a --- /dev/null +++ b/Assets/07_Data/Character/Cat1.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47faf39975a3c214c923a3cbca747c5a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/Communication/DialogGraph.meta b/Assets/07_Data/Communication/DialogGraph.meta new file mode 100644 index 00000000..1cdc313d --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ced28b8b2b5c7b74a94cc6a2ffaf712e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg b/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg new file mode 100644 index 00000000..d783ef23 --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg @@ -0,0 +1,248 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 790b4d75d92f4b0984310a268dbd952f, type: 3} + m_Name: DialogGraph_Test + m_EditorClassIdentifier: Unity.GraphToolkit.Editor::Unity.GraphToolkit.Editor.Implementation.GraphObjectImp + m_GraphModel: + rid: 6595524284503556355 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } + - rid: 6595524284503556355 + type: {class: GraphModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 17823081222403471471 + m_Value1: 14080304629836554331 + m_HashGuid: + serializedVersion: 2 + Hash: 6f384be8f04d58f75b20b2ce754a67c3 + m_Name: DialogGraph_Test + m_GraphNodeModels: + - rid: 6595524284503556358 + - rid: 6595524284503556360 + m_GraphWireModels: + - rid: 6595524284503556361 + m_GraphStickyNoteModels: [] + m_GraphPlacematModels: [] + m_GraphVariableModels: [] + m_GraphPortalModels: [] + m_SectionModels: + - rid: 6595524284503556356 + m_LocalSubgraphs: [] + m_LastKnownBounds: + serializedVersion: 2 + x: -3669 + y: -991 + width: 548 + height: 289 + m_GraphElementMetaData: + - m_Guid: + m_Value0: 7662814040680088363 + m_Value1: 10703116509857382770 + m_HashGuid: + serializedVersion: 2 + Hash: 2bc39c4ac5c8576a7201ab17c21b8994 + m_Category: 0 + m_Index: 0 + - m_Guid: + m_Value0: 13710833896111860515 + m_Value1: 8096942652718749771 + m_HashGuid: + serializedVersion: 2 + Hash: 23eb2110c9aa46be4b980276711e5e70 + m_Category: 0 + m_Index: 1 + - m_Guid: + m_Value0: 17559303413946147345 + m_Value1: 4055256088163418491 + m_HashGuid: + serializedVersion: 2 + Hash: 116290fb6b2daff37b813830ed294738 + m_Category: 2 + m_Index: 0 + m_EntryPoint: + rid: 6595524284503556358 + m_Graph: + rid: 6595524284503556357 + - rid: 6595524284503556356 + type: {class: SectionModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 8982649834989131076 + m_Value1: 15056671361968375959 + m_HashGuid: + serializedVersion: 2 + Hash: 44953fcc6ec8a87c9708ec7dd40af4d0 + m_Version: 2 + m_Items: [] + m_Title: + - rid: 6595524284503556357 + type: {class: DialogGraph, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524284503556358 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 7662814040680088363 + m_Value1: 10703116509857382770 + m_HashGuid: + serializedVersion: 2 + Hash: 2bc39c4ac5c8576a7201ab17c21b8994 + m_Version: 2 + m_Position: {x: -3668.628, y: -968.29016} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: [] + m_ValueList: [] + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524284503556359 + - rid: 6595524284503556359 + type: {class: DialogStartNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524284503556360 + type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} + data: + m_Guid: + m_Value0: 13710833896111860515 + m_Value1: 8096942652718749771 + m_HashGuid: + serializedVersion: 2 + Hash: 23eb2110c9aa46be4b980276711e5e70 + m_Version: 2 + m_Position: {x: -3454.8481, y: -991.3574} + m_Title: + m_Tooltip: + m_NodePreviewModel: + rid: -2 + m_State: 0 + m_InputConstantsById: + m_KeyList: + - __option_ChoiceCount + - Speaker + - TalkText + - Gesture + - Expression + - Voice + - LineDuration + - LookAtPlayer + m_ValueList: + - rid: 6595524284503556362 + - rid: 6595524284503556363 + - rid: 6595524284503556371 + - rid: 6595524284503556365 + - rid: 6595524284503556366 + - rid: 6595524284503556367 + - rid: 6595524284503556368 + - rid: 6595524284503556369 + m_InputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_OutputPortInfos: + expandedPortsById: + m_KeyList: [] + m_ValueList: + m_Collapsed: 0 + m_CurrentModeIndex: 0 + m_ElementColor: + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_HasUserColor: 0 + m_Node: + rid: 6595524284503556370 + - rid: 6595524284503556361 + type: {class: WireModel, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Guid: + m_Value0: 17559303413946147345 + m_Value1: 4055256088163418491 + m_HashGuid: + serializedVersion: 2 + Hash: 116290fb6b2daff37b813830ed294738 + m_Version: 2 + m_FromPortReference: + m_NodeModelGuid: + m_Value0: 7662814040680088363 + m_Value1: 10703116509857382770 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 2bc39c4ac5c8576a7201ab17c21b8994 + m_UniqueId: Out + m_PortDirection: 2 + m_PortOrientation: 0 + m_Title: + m_ToPortReference: + m_NodeModelGuid: + m_Value0: 13710833896111860515 + m_Value1: 8096942652718749771 + m_NodeModelHashGuid: + serializedVersion: 2 + Hash: 23eb2110c9aa46be4b980276711e5e70 + m_UniqueId: In + m_PortDirection: 1 + m_PortOrientation: 0 + m_Title: + - rid: 6595524284503556362 + type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524284503556363 + type: {class: 'Constant`1[[CharacterData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524284503556365 + type: {class: 'Constant`1[[GestureData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524284503556366 + type: {class: 'Constant`1[[ExpressionData, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524284503556367 + type: {class: 'Constant`1[[VoiceClip, Assembly-CSharp]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: {fileID: 0} + - rid: 6595524284503556368 + type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524284503556369 + type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: 0 + - rid: 6595524284503556370 + type: {class: DialogLineNode, ns: WhaleAdventure.Dialog.GraphTool.Editor, asm: Assembly-CSharp-Editor} + data: + - rid: 6595524284503556371 + type: {class: 'Constant`1[[WhaleAdventure.Dialog.GraphTool.Editor.DialogText, Assembly-CSharp-Editor]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} + data: + m_Value: + Value: diff --git a/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg.meta b/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg.meta new file mode 100644 index 00000000..689608ae --- /dev/null +++ b/Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 70d017bff29e23d40a4f4eaaf044a4bf +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 2ae5ca89bbed445479d9023586f0c041, type: 3} diff --git a/Assets/My project/Dialogue Scripts/Dialogue Datas/Gepetto_Intro.asset b/Assets/My project/Dialogue Scripts/Dialogue Datas/Gepetto_Intro.asset index 2864f159..f282a6cf 100644 --- a/Assets/My project/Dialogue Scripts/Dialogue Datas/Gepetto_Intro.asset +++ b/Assets/My project/Dialogue Scripts/Dialogue Datas/Gepetto_Intro.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1da27f66d5a9984ed76855ccefa5cea3a1494b8f948b106ba383df463d6be513 -size 706 +oid sha256:399fb9e9e799fa1ba990fbedf620126594ad4064301cc6b8aec09765a94987eb +size 908