using UnityEngine; public class DialogueManager : MonoBehaviour { [Header("References")] [SerializeField] private DialogueUI ui; [SerializeField] private DialogueBillboard billboard; [Header("Starting Dialogue")] [SerializeField] private DialogueData startingDialogue; [SerializeField] private Transform startingTarget; [SerializeField] private bool startOnAwake = false; [Header("Settings")] [SerializeField] private bool allowRestartWhileOpen = true; [SerializeField] private bool showDebugLog = true; private DialogueData currentDialogue; private int currentNodeIndex = -1; private Transform currentNpcTarget; public bool IsDialogueActive { get; private set; } private void Awake() { AutoFindReferences(); ValidateReferences(); } private void Start() { if (ui != null) { ui.SetVisible(false); ui.ClearChoices(); } if (startOnAwake && startingDialogue != null) StartDialogue(startingDialogue, startingTarget); } private void AutoFindReferences() { if (ui == null) ui = FindFirstObjectByType(); if (billboard == null) billboard = FindFirstObjectByType(); } private void ValidateReferences() { if (ui == null) Debug.LogWarning("[DialogueManager] DialogueUI가 연결되지 않았습니다."); if (billboard == null) Debug.LogWarning("[DialogueManager] DialogueBillboard가 연결되지 않았습니다. VR 위치 추적 기능은 동작하지 않습니다."); } public void StartDialogue(DialogueData dialogue) { StartDialogue(dialogue, currentNpcTarget); } public void StartDialogue(DialogueData dialogue, Transform npc) { if (dialogue == null) { Debug.LogWarning("[DialogueManager] 시작할 DialogueData가 null입니다."); return; } if (!allowRestartWhileOpen && IsDialogueActive) return; if (!dialogue.HasNodes) { Debug.LogWarning($"[DialogueManager] DialogueData '{dialogue.name}'에 노드가 없습니다."); EndDialogue(); return; } currentDialogue = dialogue; currentNodeIndex = 0; currentNpcTarget = npc; IsDialogueActive = true; if (ui != null) ui.SetVisible(true); if (billboard != null) billboard.SetTarget(npc); ShowCurrentNode(); if (showDebugLog) Debug.Log($"[DialogueManager] Dialogue Start: {dialogue.name}"); } public void StartDialogueFromNPC(DialogueData dialogue, Transform npc) { StartDialogue(dialogue, npc); } public void NextDialogue() { if (!IsDialogueActive) return; GoToNodeIndex(currentNodeIndex + 1); } public void GoToNodeIndex(int index) { if (currentDialogue == null) { EndDialogue(); return; } if (!currentDialogue.IsValidIndex(index)) { EndDialogue(); return; } currentNodeIndex = index; ShowCurrentNode(); } public void GoToNodeId(string nodeId) { if (currentDialogue == null) { EndDialogue(); return; } int index = currentDialogue.GetNodeIndexById(nodeId); if (index < 0) { Debug.LogWarning($"[DialogueManager] nodeId '{nodeId}'를 찾을 수 없습니다."); EndDialogue(); return; } GoToNodeIndex(index); } private void ShowCurrentNode() { if (currentDialogue == null) { EndDialogue(); return; } if (!currentDialogue.TryGetNode(currentNodeIndex, out DialogueNode node)) { Debug.LogWarning("[DialogueManager] 현재 노드를 가져올 수 없습니다."); EndDialogue(); return; } if (ui == null) return; ui.SetSpeaker(node.speaker); ui.SetDialogueText(node.dialogue); if (HasChoices(node)) { ui.SetNextButtonVisible(false); ui.CreateChoices(node.choices, OnChoiceClicked); } else { ui.ClearChoices(); ui.SetNextButtonVisible(true); ui.SetupNextButton(NextDialogue); } } private bool HasChoices(DialogueNode node) { return node != null && node.choices != null && node.choices.Length > 0; } private void OnChoiceClicked(int choiceIndex) { if (!IsDialogueActive) return; if (currentDialogue == null) return; if (!currentDialogue.TryGetNode(currentNodeIndex, out DialogueNode node)) return; if (node.choices == null || choiceIndex < 0 || choiceIndex >= node.choices.Length) return; ChoiceData choice = node.choices[choiceIndex]; if (choice == null) return; HandleChoice(choice); } private void HandleChoice(ChoiceData choice) { choice.onSelected?.Invoke(); if (choice.endDialogue) { EndDialogue(); return; } if (choice.nextDialogue != null) { StartDialogue(choice.nextDialogue, currentNpcTarget); return; } if (!string.IsNullOrWhiteSpace(choice.nextNodeId)) { GoToNodeId(choice.nextNodeId); return; } if (choice.nextNodeIndex >= 0) { GoToNodeIndex(choice.nextNodeIndex); return; } NextDialogue(); } public void EndDialogue() { if (ui != null) { ui.ClearChoices(); ui.SetNextButtonVisible(false); ui.SetVisible(false); } if (billboard != null) billboard.ClearTarget(); currentDialogue = null; currentNodeIndex = -1; currentNpcTarget = null; IsDialogueActive = false; if (showDebugLog) Debug.Log("[DialogueManager] Dialogue End"); } public void CloseDialogue() { EndDialogue(); } }