272 lines
6.4 KiB
C#
272 lines
6.4 KiB
C#
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<DialogueUI>();
|
|
|
|
if (billboard == null)
|
|
billboard = FindFirstObjectByType<DialogueBillboard>();
|
|
}
|
|
|
|
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();
|
|
}
|
|
} |