2026-07-10 대화 끼어들기 구현

This commit is contained in:
2026-07-10 12:55:02 +09:00
parent a368a27465
commit 173f3823b6
13 changed files with 316 additions and 10 deletions

View File

@@ -11,6 +11,10 @@ public class DialogNode : ScriptableObject
[Tooltip("비우면 Speaker의 이름 그대로, 채우면 이 이름으로 표시 (예: ???)")]
public string SpeakerNameOverride;
[Tooltip("대사창을 띄울 기준 캐릭터. 비우면 Speaker 옆에 띄움 " +
"(다른 NPC가 끼어드는 대사를 원래 대화 상대 옆에 표시하고 싶을 때 지정)")]
public CharacterData HudAnchor;
[Header("Content")]
[TextArea(2,5)] public string TalkText;
public GestureData Gesture;

View File

@@ -362,7 +362,8 @@ private async Awaitable PlayNode(DialogNode node)
if (node.StagingOnly)
DialogHud.Instance.Hide();
else
DialogHud.Instance.Show(node.Speaker, node.TalkText, node.SpeakerNameOverride);
DialogHud.Instance.Show(node.Speaker, node.TalkText, node.SpeakerNameOverride,
node.HudAnchor); // HudAnchor 지정 시 창은 그 캐릭터 옆에 (비우면 화자 옆)
}
RaiseNodeEvent(node.EventKey); // EventKey 있으면 매칭 이벤트 호출

View File

@@ -10,7 +10,7 @@ namespace DinoLove.Dialog.GraphTool.Editor
// .dlg 그래프 에셋을 기존 런타임 타입(DialogGroup / DialogNode / DialogChoice)으로 변환한다.
// 생성된 DialogNode들은 서브에셋으로, DialogGroup이 메인 에셋으로 등록된다.
// 따라서 DialogPlayer는 수정 없이 임포트된 .dlg 에셋(= DialogGroup)을 그대로 사용한다.
[ScriptedImporter(6, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨
[ScriptedImporter(8, DialogGraph.AssetExtension)] // 버전 올리면 기존 .dlg 에셋이 재임포트됨
internal class DialogGraphImporter : ScriptedImporter
{
public override void OnImportAsset(AssetImportContext ctx)
@@ -86,6 +86,7 @@ public override void OnImportAsset(AssetImportContext ctx)
dn.Speaker = GetInputPortValue<CharacterData>(gn.GetInputPortByName(DialogLineNode.PORT_SPEAKER));
dn.SpeakerNameOverride = GetInputPortValue<DialogShortText>(gn.GetInputPortByName(DialogLineNode.PORT_NAME_OVERRIDE)).Value;
dn.HudAnchor = GetInputPortValue<CharacterData>(gn.GetInputPortByName(DialogLineNode.PORT_HUD_ANCHOR));
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));

View File

@@ -16,6 +16,7 @@ internal class DialogLineNode : DialogGraphNode
{
public const string PORT_SPEAKER = "Speaker";
public const string PORT_NAME_OVERRIDE = "SpeakerNameOverride";
public const string PORT_HUD_ANCHOR = "HudAnchor";
public const string PORT_TALK = "TalkText";
public const string PORT_GESTURE = "Gesture";
public const string PORT_EXPRESSION = "Expression";
@@ -58,6 +59,8 @@ protected override void OnDefinePorts(IPortDefinitionContext context)
context.AddInputPort<CharacterData>(PORT_SPEAKER).WithDisplayName("Speaker").Build();
context.AddInputPort<DialogShortText>(PORT_NAME_OVERRIDE).WithDisplayName("Name Override")
.WithTooltip("비우면 Speaker 이름 그대로, 채우면 이 이름으로 표시 (예: ???)").Build();
context.AddInputPort<CharacterData>(PORT_HUD_ANCHOR).WithDisplayName("Hud Anchor")
.WithTooltip("대사창을 띄울 기준 캐릭터. 비우면 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();

View File

@@ -70,9 +70,12 @@ private void OnDestroy()
// - 배치는 화자 오브젝트의 DialogHudPlacement 값 사용, 없으면 이 컴포넌트의 기본값 폴백
// - speakerNameOverride가 비어있지 않으면 CharacterData.Name 대신 그 이름을 표시한다 (예: "???")
// - 화자의 DialogHudPlacement에 후보 목록이 있으면 벽/가림 검사를 통과하는 첫 후보 위치로 자동 배치
public void Show(CharacterData speaker, string text, string speakerNameOverride = null)
// - anchorOverride가 있으면 창의 위치/배치만 그 캐릭터 기준으로 하고 이름/대사는 speaker 것을 쓴다
// (다른 NPC가 끼어드는 대사지만 창은 대화 주인 옆에 두고 싶을 때)
public void Show(CharacterData speaker, string text, string speakerNameOverride = null, CharacterData anchorOverride = null)
{
var voiceObj = speaker != null ? CharacterVoiceObject.Find(speaker) : null;
var anchorData = anchorOverride != null ? anchorOverride : speaker;
var voiceObj = anchorData != null ? CharacterVoiceObject.Find(anchorData) : null;
_speakerTransform = voiceObj != null ? voiceObj.transform : null;
DialogHudPlacement placement = null;