3D 잔재 제거

This commit is contained in:
2026-07-24 21:11:32 +09:00
parent 0b02bacb3d
commit 53bad30836
55 changed files with 4269 additions and 292 deletions

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Unity.GraphToolkit.Editor;
using UnityEditor;
@@ -8,6 +9,9 @@ namespace DinoLove.Dialog.GraphTool.Editor
// 기존 Communication/Dialog 시스템(DialogGroup / DialogNode / DialogChoice)을
// 노드 그래프로 저작하기 위한 에디터 전용 그래프 타입이다.
// 임포트 시 DialogGraphImporter가 이 그래프를 DialogGroup 에셋으로 변환한다.
// [Serializable]: GraphToolkit이 이 그래프 인스턴스를 .dlg 안에 [SerializeReference]로 저장한다.
// 없으면 "missing the [Serializable] attribute" 경고가 뜨고, 필드를 추가해도 직렬화되지 않는다.
[Serializable]
[Graph(AssetExtension)]
internal class DialogGraph : Graph
{

View File

@@ -10,7 +10,7 @@ namespace DinoLove.Dialog.GraphTool.Editor
// .dlg 그래프 에셋을 기존 런타임 타입(DialogGroup / DialogNode / DialogChoice)으로 변환한다.
// 생성된 DialogNode들은 서브에셋으로, DialogGroup이 메인 에셋으로 등록된다.
// 따라서 DialogPlayer는 수정 없이 임포트된 .dlg 에셋(= DialogGroup)을 그대로 사용한다.
[ScriptedImporter(15, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨
[ScriptedImporter(16, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨
internal class DialogGraphImporter : ScriptedImporter
{
public override void OnImportAsset(AssetImportContext ctx)
@@ -53,6 +53,7 @@ public override void OnImportAsset(AssetImportContext ctx)
var dn = ScriptableObject.CreateInstance<DialogNode>();
dn.Choices = new List<DialogChoice>();
dn.HiddenBranches = new List<HiddenBranch>();
map[gn] = dn;
order.Add(gn);
@@ -144,21 +145,19 @@ public override void OnImportAsset(AssetImportContext ctx)
}
}
// 히든 분기 (켜져 있으면) — 목적지 노드 + 제스처 키 + 기록 코드
bool hasHidden = false;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_BRANCH)?.TryGetValue(out hasHidden);
if (hasHidden)
// 히든 분기 — 분기마다 [여는 키 + 목적지 노드 + 기록 코드]
int hiddenCount = 0;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_COUNT)?.TryGetValue(out hiddenCount);
for (int i = 0; i < hiddenCount; i++)
{
var hiddenDest = GetConnectedNode(gn, DialogLineNode.PORT_HIDDEN_OUT);
dn.HiddenBranch = hiddenDest != null && map.TryGetValue(hiddenDest, out var hiddenDn) ? hiddenDn : null;
string gestureKey = null;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_GESTURE)?.TryGetValue(out gestureKey);
dn.HiddenGestureKey = gestureKey;
string hiddenCode = null;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_CODE)?.TryGetValue(out hiddenCode);
dn.HiddenCode = hiddenCode;
var hiddenDest = GetConnectedNode(gn, DialogLineNode.HiddenOutPort(i));
dn.HiddenBranches.Add(new HiddenBranch
{
Key = GetInputPortValue<string>(gn.GetInputPortByName(DialogLineNode.HiddenKeyPort(i))),
Code = GetInputPortValue<string>(gn.GetInputPortByName(DialogLineNode.HiddenCodePort(i))),
Destination = hiddenDest != null && map.TryGetValue(hiddenDest, out var hiddenDn) ? hiddenDn : null
});
}
}
@@ -193,10 +192,11 @@ static IEnumerable<INode> GetSuccessors(INode node)
}
// 히든 분기 후속도 도달 가능해야 DialogNode로 생성된다
bool hasHidden = false;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_BRANCH)?.TryGetValue(out hasHidden);
if (hasHidden)
yield return GetConnectedNode(node, DialogLineNode.PORT_HIDDEN_OUT);
int hiddenCount = 0;
line.GetNodeOptionByName(DialogLineNode.OPTION_HIDDEN_COUNT)?.TryGetValue(out hiddenCount);
for (int i = 0; i < hiddenCount; i++)
yield return GetConnectedNode(node, DialogLineNode.HiddenOutPort(i));
}
// 출력 실행 포트에 연결된 노드 (없으면 null)

View File

@@ -24,18 +24,20 @@ internal class DialogLineNode : DialogGraphNode
public const string PORT_AFFECTION = "Affection";
public const string PORT_PROGRESS = "Progress";
public const string PORT_QUESTION = "ChoiceQuestion";
public const string PORT_HIDDEN_OUT = "HiddenOut";
public const string OPTION_CHOICE_COUNT = "ChoiceCount";
public const string OPTION_HIDDEN_BRANCH = "HasHiddenBranch";
public const string OPTION_HIDDEN_GESTURE = "HiddenGestureKey";
public const string OPTION_HIDDEN_CODE = "HiddenCode";
public const string OPTION_CHOICE_COUNT = "ChoiceCount";
public const string OPTION_HIDDEN_COUNT = "HiddenBranchCount";
// 선택지별 포트 이름 규칙 (임포터와 공유)
public static string ChoiceTextPort(int i) => $"Choice{i}Text";
public static string ChoiceCodePort(int i) => $"Choice{i}Code";
public static string ChoiceOutPort(int i) => $"Choice{i}Out";
// 히든 분기별 포트 이름 규칙 (임포터와 공유)
public static string HiddenKeyPort(int i) => $"Hidden{i}Key";
public static string HiddenCodePort(int i) => $"Hidden{i}Code";
public static string HiddenOutPort(int i) => $"Hidden{i}Out";
protected override void OnDefineOptions(IOptionDefinitionContext context)
{
context.AddOption<int>(OPTION_CHOICE_COUNT)
@@ -44,20 +46,12 @@ protected override void OnDefineOptions(IOptionDefinitionContext context)
.WithDefaultValue(0)
.Delayed();
context.AddOption<bool>(OPTION_HIDDEN_BRANCH)
.WithDisplayName("Hidden Branch")
.WithTooltip("켜면 맨 아래에 'Hidden →' 출력 포트가 생긴다. 이 대사 재생 중 특정 제스처(싸대기/터치)를 하면 " +
"Next/선택지 대신 그 포트로 연결된 노드로 몰래 분기한다")
.WithDefaultValue(false);
context.AddOption<string>(OPTION_HIDDEN_GESTURE)
.WithDisplayName("Hidden Gesture Key")
.WithTooltip("히든 분기를 여는 제스처 키 (DialogGestureZone의 Gesture Key와 일치). 비우면 아무 제스처 존이나 발동")
.Delayed();
context.AddOption<string>(OPTION_HIDDEN_CODE)
.WithDisplayName("Hidden Code")
.WithTooltip("히든 분기를 탔을 때 기록할 선택지 Code. 비우면 기록 안 함")
context.AddOption<int>(OPTION_HIDDEN_COUNT)
.WithDisplayName("Hidden Branch Count")
.WithTooltip("이 대사에 달 히든 분기 개수. 0이면 히든 분기 없음. " +
"1 이상이면 분기마다 [Key · Code 입력 포트]와 'Hidden N →' 출력 포트가 생긴다. " +
"재생 중 그 Key로 발동하면 Next/선택지 대신 해당 포트로 몰래 분기한다")
.WithDefaultValue(0)
.Delayed();
}
@@ -106,12 +100,24 @@ protected override void OnDefinePorts(IPortDefinitionContext context)
}
}
// 히든 분기 출력 — 켜져 있으면 선형/선택지 관계없이 맨 아래에 붙는다.
// 포트에 연결된 노드가 DialogNode.HiddenBranch 된다.
bool hasHidden = false;
GetNodeOptionByName(OPTION_HIDDEN_BRANCH)?.TryGetValue(out hasHidden);
if (hasHidden)
AddExecOutput(context, PORT_HIDDEN_OUT, "Hidden →");
// 히든 분기 선형/선택지 관계없이 맨 아래에 붙는다.
// 포트에 연결된 노드가 DialogNode.HiddenBranches[i].Destination이 된다.
int hiddenCount = 0;
GetNodeOptionByName(OPTION_HIDDEN_COUNT)?.TryGetValue(out hiddenCount);
for (int i = 0; i < hiddenCount; i++)
{
context.AddInputPort<string>(HiddenKeyPort(i))
.WithDisplayName($"Hidden {i + 1} Key")
.WithTooltip("이 분기를 여는 키 (터치 존·증거품 등 발동시키는 쪽의 키와 일치). " +
"비우면 catch-all — 다른 키에 안 걸린 발동을 전부 받는다(오답 처리용)")
.Build();
context.AddInputPort<string>(HiddenCodePort(i))
.WithDisplayName($"Hidden {i + 1} Code")
.WithTooltip("이 분기를 탔을 때 기록할 코드. 비우면 기록 안 함")
.Build();
AddExecOutput(context, HiddenOutPort(i), $"Hidden {i + 1} →");
}
}
}
}