2026-06-19 다이얼로그 그래프툴
This commit is contained in:
BIN
Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity
LFS
Normal file
BIN
Assets/01_Scenes/WhaleAdventure_VR/Dialog/CatsRoomDialogTest.unity
LFS
Normal file
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22ed8e3dbc3ea934f8aac5f96aa1ba2f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/02_Scripts/Communication/Dialog/GraphTool.meta
Normal file
8
Assets/02_Scripts/Communication/Dialog/GraphTool.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb0dad36a50363647a4b9cd61d078b20
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bad231f70f838c84288d07f754f51ed7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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<DialogGraph>(k_DefaultName);
|
||||
}
|
||||
|
||||
// 그래프가 바뀔 때마다 호출되어 에러/경고를 보고한다.
|
||||
public override void OnGraphChanged(GraphLogger infos)
|
||||
{
|
||||
base.OnGraphChanged(infos);
|
||||
|
||||
var startNodes = GetNodes().OfType<DialogStartNode>().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f25a0745082bd7c439679495337d3598
|
||||
@@ -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<DialogGraph>(ctx.assetPath);
|
||||
if (graph == null)
|
||||
{
|
||||
Debug.LogError($"[DialogGraphImporter] 그래프 로드 실패: {ctx.assetPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 메인 에셋: DialogGroup (이름은 파일명 기준 — DialogPlayer가 이름으로 조회)
|
||||
var groupName = Path.GetFileNameWithoutExtension(ctx.assetPath);
|
||||
var group = ScriptableObject.CreateInstance<DialogGroup>();
|
||||
group.name = groupName;
|
||||
group.DialogGroupName = groupName;
|
||||
ctx.AddObjectToAsset("Group", group);
|
||||
ctx.SetMainObject(group);
|
||||
|
||||
var startNode = graph.GetNodes().OfType<DialogStartNode>().FirstOrDefault();
|
||||
if (startNode == null)
|
||||
return; // OnGraphChanged에서 에러 로깅됨
|
||||
|
||||
var firstGraphNode = GetConnectedNode(startNode, DialogGraphNode.EXEC_OUT);
|
||||
if (firstGraphNode == null)
|
||||
return; // Start만 있고 연결 없음
|
||||
|
||||
// 1패스: 도달 가능한 모든 라인 노드 → DialogNode 인스턴스 생성 (중복 제거)
|
||||
var map = new Dictionary<INode, DialogNode>();
|
||||
var order = new List<INode>();
|
||||
var queue = new Queue<INode>();
|
||||
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<DialogNode>();
|
||||
dn.Choices = new List<DialogChoice>();
|
||||
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<CharacterData>(gn.GetInputPortByName(DialogLineNode.PORT_SPEAKER));
|
||||
dn.TalkText = GetInputPortValue<DialogText>(gn.GetInputPortByName(DialogLineNode.PORT_TALK)).Value;
|
||||
dn.Gesture = GetInputPortValue<GestureData>(gn.GetInputPortByName(DialogLineNode.PORT_GESTURE));
|
||||
dn.Expression = GetInputPortValue<ExpressionData>(gn.GetInputPortByName(DialogLineNode.PORT_EXPRESSION));
|
||||
dn.Voice = GetInputPortValue<VoiceClip>(gn.GetInputPortByName(DialogLineNode.PORT_VOICE));
|
||||
dn.LineDuration = GetInputPortValue<float>(gn.GetInputPortByName(DialogLineNode.PORT_DURATION));
|
||||
dn.LookAtPlayer = GetInputPortValue<bool>(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<string>(gn.GetInputPortByName(DialogLineNode.PORT_QUESTION));
|
||||
for (int i = 0; i < choiceCount; i++)
|
||||
{
|
||||
var choiceText = GetInputPortValue<string>(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<INode> 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<T>(IPort port)
|
||||
{
|
||||
T value = default;
|
||||
if (port == null)
|
||||
return value;
|
||||
|
||||
if (port.isConnected)
|
||||
{
|
||||
switch (port.firstConnectedPort.GetNode())
|
||||
{
|
||||
case IVariableNode variableNode:
|
||||
variableNode.variable.TryGetDefaultValue<T>(out value);
|
||||
return value;
|
||||
case IConstantNode constantNode:
|
||||
constantNode.TryGetValue<T>(out value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port.TryGetValue(out value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ae5ca89bbed445479d9023586f0c041
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be386f8d9f138f8449c9e84034f5e1ef
|
||||
@@ -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<int>(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<CharacterData>(PORT_SPEAKER).WithDisplayName("Speaker").Build();
|
||||
context.AddInputPort<DialogText>(PORT_TALK).WithDisplayName("Talk Text").Build();
|
||||
context.AddInputPort<GestureData>(PORT_GESTURE).WithDisplayName("Gesture").Build();
|
||||
context.AddInputPort<ExpressionData>(PORT_EXPRESSION).WithDisplayName("Expression").Build();
|
||||
context.AddInputPort<VoiceClip>(PORT_VOICE).WithDisplayName("Voice").Build();
|
||||
context.AddInputPort<float>(PORT_DURATION).WithDisplayName("Line Duration").Build();
|
||||
context.AddInputPort<bool>(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<string>(PORT_QUESTION).WithDisplayName("Choice Question").Build();
|
||||
|
||||
for (int i = 0; i < choiceCount; i++)
|
||||
{
|
||||
context.AddInputPort<string>(ChoiceTextPort(i))
|
||||
.WithDisplayName($"Choice {i + 1} Text")
|
||||
.Build();
|
||||
AddExecOutput(context, ChoiceOutPort(i), $"Choice {i + 1} →");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be6c5a15779e30f40910985e5d7cfbd9
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f76763c19a33214ca518048c8a89799
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ad54cf039f672845a54666166b5021c
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d31004aee38951a4faaff37d886d3a65
|
||||
47
Assets/02_Scripts/Communication/Dialog/GraphTool/README.md
Normal file
47
Assets/02_Scripts/Communication/Dialog/GraphTool/README.md
Normal file
@@ -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`로 합쳐집니다.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fa58401314123a4b90fa0fda5240a18
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/07_Data/Character/Cat1.asset
LFS
Normal file
BIN
Assets/07_Data/Character/Cat1.asset
LFS
Normal file
Binary file not shown.
8
Assets/07_Data/Character/Cat1.asset.meta
Normal file
8
Assets/07_Data/Character/Cat1.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47faf39975a3c214c923a3cbca747c5a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/07_Data/Communication/DialogGraph.meta
Normal file
8
Assets/07_Data/Communication/DialogGraph.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ced28b8b2b5c7b74a94cc6a2ffaf712e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
248
Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg
Normal file
248
Assets/07_Data/Communication/DialogGraph/DialogGraph_Test.wdg
Normal file
@@ -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:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70d017bff29e23d40a4f4eaaf044a4bf
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 2ae5ca89bbed445479d9023586f0c041, type: 3}
|
||||
Binary file not shown.
Reference in New Issue
Block a user