2026-04-24 NPC 음성 다이얼로그 시스템
This commit is contained in:
@@ -1,24 +1,99 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(CharacterVoiceObject))]
|
||||
public class DialogPlayer : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private List<DialogGroup> _dialogGroups;
|
||||
private Dictionary<string, DialogGroup> _dialogGroupMap;
|
||||
private Animator _animator;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_dialogGroupMap = new Dictionary<string, DialogGroup>();
|
||||
foreach (var g in _dialogGroups)
|
||||
{
|
||||
_dialogGroupMap[g.DialogGroupName] = g;
|
||||
}
|
||||
|
||||
_animator = GetComponentInChildren<Animator>();
|
||||
}
|
||||
|
||||
public void VoicePlay(DialogNode node)
|
||||
// 임시 테스트용
|
||||
private void Update()
|
||||
{
|
||||
|
||||
//CharacterVoiceObject speakerVoiceObject = FindSpeakerVoiceObject(node.Speaker);
|
||||
//speakerVoiceObject.Play(node.Voice.Clip);
|
||||
if (Keyboard.current != null && Keyboard.current.tKey.wasPressedThisFrame)
|
||||
_ = Play("BlackDialogGroup1");
|
||||
}
|
||||
|
||||
public async Awaitable Play(string groupName)
|
||||
{
|
||||
if (!_dialogGroupMap.TryGetValue(groupName, out var group))
|
||||
{
|
||||
Debug.LogWarning($"[DialogPlayer] 그룹 없음: {groupName}");
|
||||
return;
|
||||
}
|
||||
|
||||
var node = group.StartNode;
|
||||
while (node != null)
|
||||
{
|
||||
await PlayNode(node);
|
||||
|
||||
if (node.Choices != null && node.Choices.Count > 0)
|
||||
{
|
||||
int picked = await WaitForChoice(node);
|
||||
node = node.Choices[picked].DestinationNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("[DialogPlayer] 대화 종료");
|
||||
}
|
||||
|
||||
private async Awaitable PlayNode(DialogNode node)
|
||||
{
|
||||
var speakerName = node.Speaker != null ? node.Speaker.Name : "(누구?)";
|
||||
Debug.Log($"[{speakerName}] {node.TalkText}");
|
||||
|
||||
// 보이스 재생
|
||||
if (node.Voice != null && node.Speaker != null)
|
||||
{
|
||||
var voiceObj = CharacterVoiceObject.Find(node.Speaker);
|
||||
if (voiceObj != null && node.Voice.Clip != null)
|
||||
voiceObj.Play(node.Voice.Clip);
|
||||
}
|
||||
|
||||
if (node.Gesture != null)
|
||||
_animator.CrossFade(node.Gesture.StateName, node.Gesture.CrossFadeDuration, node.Gesture.AnimationLayer);
|
||||
if (node.Expression != null)
|
||||
_animator.CrossFade(node.Expression.StateName, node.Expression.CrossFadeDuration, node.Expression.AnimationLayer);
|
||||
|
||||
// 대기 시간 결정
|
||||
float wait = 0f;
|
||||
if (node.Voice != null && node.Voice.Clip != null)
|
||||
wait = node.Voice.Clip.length;
|
||||
else
|
||||
wait = node.LineDuration;
|
||||
|
||||
if (wait > 0f)
|
||||
await Awaitable.WaitForSecondsAsync(wait);
|
||||
else
|
||||
await WaitForAdvanceInput(); // 수동 진행
|
||||
}
|
||||
|
||||
private async Awaitable<int> WaitForChoice(DialogNode node)
|
||||
{
|
||||
// TODO: 선택지 UI 띄우기. 일단은 첫 번째 선택지 자동 선택 (테스트용)
|
||||
Debug.Log("[DialogPlayer] 선택지 대기 — 일단 0번 자동 선택");
|
||||
await Awaitable.WaitForSecondsAsync(0.5f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Awaitable WaitForAdvanceInput()
|
||||
{
|
||||
// TODO: VR 컨트롤러 버튼 입력 대기. 일단은 1초 대기
|
||||
await Awaitable.WaitForSecondsAsync(1f);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user