diff --git a/Assets/01_Scenes/WhaleAdventure_VR/Dialog/DialogTest.unity b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/DialogTest.unity index 17e01b3f..d550f66e 100644 --- a/Assets/01_Scenes/WhaleAdventure_VR/Dialog/DialogTest.unity +++ b/Assets/01_Scenes/WhaleAdventure_VR/Dialog/DialogTest.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5aa4aaa513a0738a9c05e1d48ab31d6f5c764d91d08be39d0b67487515b9cbc4 -size 13217 +oid sha256:12282a225cd3a1838baf13dfd5b4370872b7415a386aafb6839af5312931ca2c +size 34621 diff --git a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs index 333a711a..33af3dc5 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogPlayer.cs @@ -176,16 +176,14 @@ private async Awaitable PlayNode(DialogNode node) 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() @@ -193,4 +191,19 @@ private async Awaitable WaitForAdvanceInput() // TODO: VR 컨트롤러 버튼 입력 대기. 일단은 1초 대기 await Awaitable.WaitForSecondsAsync(1f); } + + //테스트용 + private void Update() + { + if (Mouse.current == null) return; + if (!Mouse.current.leftButton.wasPressedThisFrame) return; + if (Camera.main == null) return; + + var ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()); + if (Physics.Raycast(ray, out var hit) && hit.transform.IsChildOf(transform)) + { + Debug.Log("캐릭터 클릭"); + Play(); + } + } } diff --git a/Assets/12_Font/dumy.txt.meta b/Assets/02_Scripts/UI.meta similarity index 57% rename from Assets/12_Font/dumy.txt.meta rename to Assets/02_Scripts/UI.meta index 87a7afab..21aec122 100644 --- a/Assets/12_Font/dumy.txt.meta +++ b/Assets/02_Scripts/UI.meta @@ -1,6 +1,7 @@ fileFormatVersion: 2 -guid: f954c56664544ab45987cd3f22c833bc -TextScriptImporter: +guid: 540f24a679513b344b55eda149fcb29c +folderAsset: yes +DefaultImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/02_Scripts/UI/ChoiceHud.cs b/Assets/02_Scripts/UI/ChoiceHud.cs new file mode 100644 index 00000000..d32b883f --- /dev/null +++ b/Assets/02_Scripts/UI/ChoiceHud.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using TMPro; +using UnityEngine; + +// 화자 몸통 앞에 떠 있는 World-space 선택지 UI 싱글턴 +// DialogPlayer가 Show()를 await해서 선택된 인덱스를 받아감 +public class ChoiceHud : MonoBehaviour +{ + public static ChoiceHud Instance { get; private set; } + + [Header("Refs")] + [SerializeField] private GameObject _root; + [SerializeField] private Transform _rowContainer; + [SerializeField] private DialogChoiceRow _rowPrefab; + [SerializeField] private TMP_Text ChoiceQuestion; + + [Header("Placement")] + [SerializeField] private float _chestHeight = 1.2f; // 화자 발 기준 가슴 높이 + [SerializeField] private float _forwardOffset = 0.5f; // 화자→플레이어 방향으로 띄울 거리 + + private Transform _speakerTransform; + private readonly List _rows = new(); + private AwaitableCompletionSource _completion; + + private void Awake() + { + if (Instance != null && Instance != this) { Destroy(gameObject); return; } + Instance = this; + if (_root == null) _root = gameObject; + Hide(); + } + + private void OnDestroy() + { + if (Instance == this) Instance = null; + } + + private void OnDisable() + { + // 진행 중 대기 정리 (씬 전환 등으로 비활성화될 때) + _completion?.TrySetCanceled(); + _completion = null; + } + + public async Awaitable Show(CharacterData speaker,string choiceQuestion, List choices) + { + if (choices == null || choices.Count == 0) return 0; + + _speakerTransform = speaker != null ? CharacterVoiceObject.Find(speaker)?.transform : null; + + ChoiceQuestion.text = choiceQuestion; + ClearRows(); + for (int i = 0; i < choices.Count; i++) + { + var row = Instantiate(_rowPrefab, _rowContainer); + row.Bind(i, choices[i].ChoiceText); + row.OnClicked += HandleClicked; + _rows.Add(row); + } + + _root.SetActive(true); + _completion = new AwaitableCompletionSource(); + + int result; + try + { + result = await _completion.Awaitable; + } + finally + { + _completion = null; + Hide(); + } + return result; + } + + private void HandleClicked(int index) + { + _completion?.TrySetResult(index); + } + + private void Hide() + { + ChoiceQuestion.text = ""; + ClearRows(); + if (_root != null) _root.SetActive(false); + _speakerTransform = null; + } + + private void ClearRows() + { + foreach (var row in _rows) + { + if (row == null) continue; + row.OnClicked -= HandleClicked; + Destroy(row.gameObject); + } + _rows.Clear(); + } + + private void LateUpdate() + { + if (_root == null || !_root.activeSelf) return; + if (_speakerTransform == null || Camera.main == null) return; + + var camTr = Camera.main.transform; + + // 화자에서 플레이어 카메라로 향하는 수평 방향 (yaw만) + Vector3 toCam = camTr.position - _speakerTransform.position; + toCam.y = 0f; + if (toCam.sqrMagnitude < 0.0001f) return; + Vector3 dir = toCam.normalized; + + Vector3 chestWorld = _speakerTransform.position + Vector3.up * _chestHeight; + _root.transform.position = chestWorld + dir * _forwardOffset; + + // 빌보드 — 캔버스의 -Z(읽는 면)가 카메라를 향하도록 +Z를 카메라 반대로 + _root.transform.rotation = Quaternion.LookRotation(-dir); + } +} diff --git a/Assets/02_Scripts/UI/ChoiceHud.cs.meta b/Assets/02_Scripts/UI/ChoiceHud.cs.meta new file mode 100644 index 00000000..6b564c30 --- /dev/null +++ b/Assets/02_Scripts/UI/ChoiceHud.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 66c54b72d69da6b4e9c30ef648aade40 \ No newline at end of file diff --git a/Assets/02_Scripts/UI/DialogChoiceRow.cs b/Assets/02_Scripts/UI/DialogChoiceRow.cs new file mode 100644 index 00000000..37f1f4e6 --- /dev/null +++ b/Assets/02_Scripts/UI/DialogChoiceRow.cs @@ -0,0 +1,26 @@ +using System; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +public class DialogChoiceRow : MonoBehaviour +{ + [SerializeField] private TMP_Text _text; + [SerializeField] private Button _button; + + public event Action OnClicked; + + private int _index; + + private void Awake() + { + if (_button != null) + _button.onClick.AddListener(() => OnClicked?.Invoke(_index)); + } + + public void Bind(int index, string text) + { + _index = index; + if (_text != null) _text.text = text; + } +} diff --git a/Assets/02_Scripts/UI/DialogChoiceRow.cs.meta b/Assets/02_Scripts/UI/DialogChoiceRow.cs.meta new file mode 100644 index 00000000..cca61284 --- /dev/null +++ b/Assets/02_Scripts/UI/DialogChoiceRow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 63cb7682c2757f74cad43848f593838f \ No newline at end of file diff --git a/Assets/04_Models/Choice.meta b/Assets/04_Models/Choice.meta new file mode 100644 index 00000000..7ba77579 --- /dev/null +++ b/Assets/04_Models/Choice.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c038b57f026442c44b4173b9cbc00906 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/04_Models/Choice/Row.meta b/Assets/04_Models/Choice/Row.meta new file mode 100644 index 00000000..f6a0268b --- /dev/null +++ b/Assets/04_Models/Choice/Row.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d8ed44e79da1eed4d89009c073e6e325 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/04_Models/Choice/Row/ChoiceRow.prefab b/Assets/04_Models/Choice/Row/ChoiceRow.prefab new file mode 100644 index 00000000..68e35102 --- /dev/null +++ b/Assets/04_Models/Choice/Row/ChoiceRow.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:970f0e0b82e8a90ae14fb69b743adab27f4d6acd5b38499eef88b06cbbc952cb +size 6090 diff --git a/Assets/04_Models/dumy.txt.meta b/Assets/04_Models/Choice/Row/ChoiceRow.prefab.meta similarity index 62% rename from Assets/04_Models/dumy.txt.meta rename to Assets/04_Models/Choice/Row/ChoiceRow.prefab.meta index 5a108d9a..7e3886fa 100644 --- a/Assets/04_Models/dumy.txt.meta +++ b/Assets/04_Models/Choice/Row/ChoiceRow.prefab.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 79e9e3274eb3e724490ac16b6706b6ae -TextScriptImporter: +guid: a4c299230cdcd9442bde6e02b8823135 +PrefabImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/04_Models/dumy.txt b/Assets/04_Models/dumy.txt deleted file mode 100644 index 34b4a496..00000000 --- a/Assets/04_Models/dumy.txt +++ /dev/null @@ -1 +0,0 @@ -지울것 \ No newline at end of file diff --git a/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_310.asset b/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_310.asset index ce5c3949..6250f9f8 100644 --- a/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_310.asset +++ b/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_310.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f3c1e87208e0b8b4d6291c4a8e1a9f34589c2aa8a61794dbe8a6f50ff2ff1f6 +oid sha256:096de3f9c350aed0007f17029cf9a11a66725b33b4ea720dbedd9e3640ecec05 size 749 diff --git a/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_410.asset b/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_410.asset index cb4365ff..3d898b5c 100644 --- a/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_410.asset +++ b/Assets/07_Data/Communication/DialogNodes/DialogNode_Fish1_1_410.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da2e901585c6d11f7a937ff91d169b8cbfdb4df0633a34655cb740d44f56f12e +oid sha256:5b14ea171168670f81c6e96c7fe6003e6ba76fed4b33fdd1f4bc1ffe936f31b7 size 754 diff --git a/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset b/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset new file mode 100644 index 00000000..51c2495f --- /dev/null +++ b/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d6663d59c8c343f172088f225287fa4d9ba42c61405b43089c468c8bb22f291 +size 139125144 diff --git a/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset.meta b/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset.meta new file mode 100644 index 00000000..14086930 --- /dev/null +++ b/Assets/12_Font/Hakgyoansim_TteokbokkiB SDF.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81f0fb85a86d25b4db2a69347ec056fd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf b/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf new file mode 100644 index 00000000..5f82b513 --- /dev/null +++ b/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f1eba3e4d45e8bf2bd4ae54f0572a4d54ad8e75aa635b8005c2720ea3b71be7 +size 1520868 diff --git a/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf.meta b/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf.meta new file mode 100644 index 00000000..b5fa2418 --- /dev/null +++ b/Assets/12_Font/Hakgyoansim_TteokbokkiB.ttf.meta @@ -0,0 +1,21 @@ +fileFormatVersion: 2 +guid: 61af039048c279940929bb3263a04712 +TrueTypeFontImporter: + externalObjects: {} + serializedVersion: 4 + fontSize: 16 + forceTextureCase: -2 + characterSpacing: 0 + characterPadding: 1 + includeFontData: 1 + fontNames: + - Hakgyoansim TTeokbokki B + fallbackFontReferences: [] + customCharacters: + fontRenderingMode: 0 + ascentCalculationMode: 1 + useLegacyBoundsCalculation: 0 + shouldRoundAdvanceValue: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/12_Font/dumy.txt b/Assets/12_Font/dumy.txt deleted file mode 100644 index 34b4a496..00000000 --- a/Assets/12_Font/dumy.txt +++ /dev/null @@ -1 +0,0 @@ -지울것 \ No newline at end of file