using System.Collections.Generic; using UnityEngine.InputSystem; using UnityEngine; [RequireComponent(typeof(CharacterVoiceObject))] public class DialogPlayer : MonoBehaviour { [SerializeField] private List _dialogGroups; private Dictionary _dialogGroupMap; private Animator _animator; private int _initialGestureHash; private int _initialExpressionHash; private bool _hasInitialExpression; private readonly Dictionary _originalRotations = new(); public bool IsPlaying { get; private set; } private void Awake() { _dialogGroupMap = new Dictionary(); foreach (var g in _dialogGroups) _dialogGroupMap[g.DialogGroupName] = g; _animator = GetComponentInChildren(); if (_animator != null) { _initialGestureHash = _animator.GetCurrentAnimatorStateInfo(0).fullPathHash; if (_animator.layerCount > 1) { _initialExpressionHash = _animator.GetCurrentAnimatorStateInfo(1).fullPathHash; _hasInitialExpression = true; } } } public void Play() { if(_dialogGroups.Count > 0) _ = Play(_dialogGroups[0].DialogGroupName); } public async Awaitable Play(string groupName) { if (IsPlaying) return; if (!_dialogGroupMap.TryGetValue(groupName, out var group)) { Debug.LogWarning($"[DialogPlayer] 그룹 없음: {groupName}"); return; } IsPlaying = true; try { 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; } } } finally { IsPlaying = false; RestoreDefaultAnimations(); RestoreRotations(); } Debug.Log("[DialogPlayer] 대화 종료"); } private void RestoreDefaultAnimations() { if (_animator == null) return; _animator.CrossFade(_initialGestureHash, 0.3f, 0, normalizedTimeOffset: 0f); if (_hasInitialExpression) _animator.CrossFade(_initialExpressionHash, 0.3f, 1, normalizedTimeOffset: 0f); } private async Awaitable RotateTowardPlayer(Transform target) { if (Camera.main == null) return; var playerCam = Camera.main.transform; float duration = 0.5f; float elapsed = 0f; while (elapsed < duration) { Vector3 dir = playerCam.position - target.position; dir.y = 0f; if (dir.sqrMagnitude > 0.0001f) { var targetRot = Quaternion.LookRotation(dir); target.rotation = Quaternion.Slerp(target.rotation, targetRot, 10f * Time.deltaTime); } elapsed += Time.deltaTime; await Awaitable.NextFrameAsync(); } } private void RestoreRotations() { foreach (var kvp in _originalRotations) { if (kvp.Key != null) _ = RotateToRotation(kvp.Key, kvp.Value); } _originalRotations.Clear(); } private async Awaitable RotateToRotation(Transform target, Quaternion targetRotation) { float duration = 0.5f; float elapsed = 0f; while (elapsed < duration) { if (target == null) return; target.rotation = Quaternion.Slerp(target.rotation, targetRotation, 10f * Time.deltaTime); elapsed += Time.deltaTime; await Awaitable.NextFrameAsync(); } } 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.LookAtPlayer && node.Speaker != null) { var voiceObj = CharacterVoiceObject.Find(node.Speaker); if (voiceObj != null) { _originalRotations.TryAdd(voiceObj.transform, voiceObj.transform.rotation); _ = RotateTowardPlayer(voiceObj.transform); } } 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 WaitForChoice(DialogNode node) { //선택을 기다리는 함수 수정해서 사용할것 /* if (ChoiceHud.Instance == null) { Debug.LogWarning("[DialogPlayer] ChoiceHud 없음 — 0번 자동 선택"); return 0; } return await ChoiceHud.Instance.Show(node.Speaker, node.ChoiceQuestion ,node.Choices); */ return 0; } private async Awaitable WaitForAdvanceInput() { // TODO: VR 컨트롤러 버튼 입력 대기. 일단은 1초 대기 await Awaitable.WaitForSecondsAsync(1f); } }