From 4dc22eb32b62d415ae4967a1042101fda03d9fc6 Mon Sep 17 00:00:00 2001 From: nakjun Date: Fri, 31 Jul 2026 11:51:50 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=B8=EB=AC=BC=EC=A0=95=EB=B3=B4,=20?= =?UTF-8?q?=EC=86=8C=EC=A7=80=ED=92=88=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/01_Scenes/TestScene.unity | 4 +- .../Communication/Dialog/DialogTouchZone.cs | 2 +- Assets/02_Scripts/Managers/StoryManager.cs | 51 +++--- Assets/02_Scripts/Story/StoryState.cs | 10 +- .../_Data/{Item.meta => Evidence.meta} | 2 +- .../02_Scripts/_Data/Evidence/EvidenceData.cs | 27 +++ .../EvidenceData.cs.meta} | 0 .../_Data/Evidence/EvidenceDatabase.cs | 66 ++++++++ .../EvidenceDatabase.cs.meta} | 0 Assets/02_Scripts/_Data/Item/ItemData.cs | 22 --- Assets/02_Scripts/_Data/Item/ItemDatabase.cs | 25 --- Assets/02_Scripts/_Debug/DialogUITester.cs | 4 +- .../_UI/Communication/DialogEnterHud.cs | 2 +- .../_UI/{Item.meta => Evidence.meta} | 2 +- .../ItemHud.cs => Evidence/EvidenceHud.cs} | 99 +++++++---- .../EvidenceHud.cs.meta} | 0 Assets/05_Textures/Icon/PersonInfo.meta | 8 + .../Icon/PersonInfo/PersonInfoEx1.png | 3 + .../Icon/PersonInfo/PersonInfoEx1.png.meta | 156 ++++++++++++++++++ .../Icon/PersonInfo/PersonInfoEx2.png | 3 + .../Icon/PersonInfo/PersonInfoEx2.png.meta | 156 ++++++++++++++++++ Assets/07_Data/Evidences.meta | 8 + .../07_Data/Evidences/EvidenceDataBase.asset | 3 + .../EvidenceDataBase.asset.meta} | 0 Assets/07_Data/{ => Evidences}/Items.meta | 0 Assets/07_Data/Evidences/Items/TestItem.asset | 3 + .../{ => Evidences}/Items/TestItem.asset.meta | 0 .../07_Data/Evidences/Items/TestItem2.asset | 3 + .../Items/TestItem2.asset.meta | 0 Assets/07_Data/Evidences/PersonInfos.meta | 8 + .../PersonInfos/TestPersonInfo1.asset | 3 + .../PersonInfos/TestPersonInfo1.asset.meta | 8 + .../PersonInfos/TestPersonInfo2.asset | 3 + .../PersonInfos/TestPersonInfo2.asset.meta | 8 + Assets/08_UI/ItemListUI.uxml | 25 +-- Assets/12_Font/Limgul24 SDF.asset | 4 +- 36 files changed, 598 insertions(+), 120 deletions(-) rename Assets/02_Scripts/_Data/{Item.meta => Evidence.meta} (77%) create mode 100644 Assets/02_Scripts/_Data/Evidence/EvidenceData.cs rename Assets/02_Scripts/_Data/{Item/ItemData.cs.meta => Evidence/EvidenceData.cs.meta} (100%) create mode 100644 Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs rename Assets/02_Scripts/_Data/{Item/ItemDatabase.cs.meta => Evidence/EvidenceDatabase.cs.meta} (100%) delete mode 100644 Assets/02_Scripts/_Data/Item/ItemData.cs delete mode 100644 Assets/02_Scripts/_Data/Item/ItemDatabase.cs rename Assets/02_Scripts/_UI/{Item.meta => Evidence.meta} (77%) rename Assets/02_Scripts/_UI/{Item/ItemHud.cs => Evidence/EvidenceHud.cs} (67%) rename Assets/02_Scripts/_UI/{Item/ItemHud.cs.meta => Evidence/EvidenceHud.cs.meta} (100%) create mode 100644 Assets/05_Textures/Icon/PersonInfo.meta create mode 100644 Assets/05_Textures/Icon/PersonInfo/PersonInfoEx1.png create mode 100644 Assets/05_Textures/Icon/PersonInfo/PersonInfoEx1.png.meta create mode 100644 Assets/05_Textures/Icon/PersonInfo/PersonInfoEx2.png create mode 100644 Assets/05_Textures/Icon/PersonInfo/PersonInfoEx2.png.meta create mode 100644 Assets/07_Data/Evidences.meta create mode 100644 Assets/07_Data/Evidences/EvidenceDataBase.asset rename Assets/07_Data/{Items/ItemDataBase.asset.meta => Evidences/EvidenceDataBase.asset.meta} (100%) rename Assets/07_Data/{ => Evidences}/Items.meta (100%) create mode 100644 Assets/07_Data/Evidences/Items/TestItem.asset rename Assets/07_Data/{ => Evidences}/Items/TestItem.asset.meta (100%) create mode 100644 Assets/07_Data/Evidences/Items/TestItem2.asset rename Assets/07_Data/{ => Evidences}/Items/TestItem2.asset.meta (100%) create mode 100644 Assets/07_Data/Evidences/PersonInfos.meta create mode 100644 Assets/07_Data/Evidences/PersonInfos/TestPersonInfo1.asset create mode 100644 Assets/07_Data/Evidences/PersonInfos/TestPersonInfo1.asset.meta create mode 100644 Assets/07_Data/Evidences/PersonInfos/TestPersonInfo2.asset create mode 100644 Assets/07_Data/Evidences/PersonInfos/TestPersonInfo2.asset.meta diff --git a/Assets/01_Scenes/TestScene.unity b/Assets/01_Scenes/TestScene.unity index c5f4569..be7818d 100644 --- a/Assets/01_Scenes/TestScene.unity +++ b/Assets/01_Scenes/TestScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d5b8efdb03418fb6f6e97810e8d305e9415246c7a46064cb27689d6e9f9ad26 -size 28648 +oid sha256:7321a8de3ea8ffc32c5ddd7e97b1e91caffb09a0626aeab38109782b411f37d7 +size 30772 diff --git a/Assets/02_Scripts/Communication/Dialog/DialogTouchZone.cs b/Assets/02_Scripts/Communication/Dialog/DialogTouchZone.cs index 87f5d2e..6fa1b10 100644 --- a/Assets/02_Scripts/Communication/Dialog/DialogTouchZone.cs +++ b/Assets/02_Scripts/Communication/Dialog/DialogTouchZone.cs @@ -33,7 +33,7 @@ private void Update() // 증거품창이 떠 있으면 월드 클릭을 받지 않는다 — 창이 전체 화면 오버레이라서, // 창 위 클릭이 이 존까지 뚫고 들어와 엉뚱한 분기를 여는 것을 막는다 - if (ItemHud.Instance != null && ItemHud.Instance.IsOpen) return; + if (EvidenceHud.Instance != null && EvidenceHud.Instance.IsOpen) return; if (Mouse.current == null || !Mouse.current.leftButton.wasPressedThisFrame) return; diff --git a/Assets/02_Scripts/Managers/StoryManager.cs b/Assets/02_Scripts/Managers/StoryManager.cs index f8c3dbc..0efe289 100644 --- a/Assets/02_Scripts/Managers/StoryManager.cs +++ b/Assets/02_Scripts/Managers/StoryManager.cs @@ -25,10 +25,15 @@ private struct InitialAffection "진행도 초기값과 마찬가지로 플레이를 시작한 씬의 값만 적용되고, 씬 전환으로 넘어온 경우엔 무시된다")] [SerializeField] private List _initialAffections = new(); - [Tooltip("플레이 시작 시 소지 아이템 — 이 씬만 직접 열어 테스트할 때 앞 과정 없이 증거품을 들고 시작하게. " + + // 아래 두 목록은 찾기 쉽게 나눠 둔 것일 뿐, 항목의 실제 분류는 EvidenceDatabase의 + // 어느 목록에 등록했는지가 정한다. 여기서 자리를 바꿔도 창에 뜨는 분류는 달라지지 않는다. + [Tooltip("플레이 시작 시 들고 있을 소지품 — 이 씬만 직접 열어 테스트할 때 앞 과정 없이 시작하게. " + "다른 초기값과 마찬가지로 플레이를 시작한 씬의 값만 적용되고, 씬 전환으로 넘어온 경우엔 무시된다. " + - "실제 게임에서의 획득은 AddItem(터치존·버튼 UnityEvent 등)으로")] - [SerializeField] private List _initialItems = new(); + "실제 게임에서의 획득은 AddEvidence(터치존·버튼 UnityEvent 등)으로")] + [SerializeField] private List _initialItems = new(); + + [Tooltip("플레이 시작 시 열려 있을 인물 정보 — 위 소지품과 동작은 같다")] + [SerializeField] private List _initialProfiles = new(); private StoryState _state = new(); @@ -47,10 +52,9 @@ private void Awake() if (init.Character != null) _state.Affection[IdOf(init.Character)] = init.Amount; - // 테스트용 초기 소지 아이템 - foreach (var item in _initialItems) - if (item != null && !_state.Items.Contains(ItemIdOf(item))) - _state.Items.Add(ItemIdOf(item)); + // 테스트용 초기 소지 증거품 항목 (두 목록 모두 같은 획득 이력으로 들어간다) + AddInitialEvidence(_initialItems); + AddInitialEvidence(_initialProfiles); } else { @@ -58,6 +62,13 @@ private void Awake() } } + private void AddInitialEvidence(List list) + { + foreach (var entry in list) + if (entry != null && !_state.Evidence.Contains(EvidenceIdOf(entry))) + _state.Evidence.Add(EvidenceIdOf(entry)); + } + // ── 메인 진행도 ────────────────────────────────────────────── public int MainProgress { @@ -110,29 +121,29 @@ public void SetTrigger(string id) Changed?.Invoke(); } - // ── 아이템(증거품) ─────────────────────────────────────────── - public IReadOnlyList ItemIds => _state.Items; + // ── 증거품 (소지품 · 인물) ─────────────────────────────────── + public IReadOnlyList EvidenceIds => _state.Evidence; - public bool HasItem(ItemData item) => item != null && _state.Items.Contains(ItemIdOf(item)); + public bool HasEvidence(EvidenceData entry) => entry != null && _state.Evidence.Contains(EvidenceIdOf(entry)); - // UnityEvent 연결용 (대화 터치존·월드 버튼 등에서 증거품 획득). 중복 획득은 무시된다. - public void AddItem(ItemData item) + // UnityEvent 연결용 (대화 터치존·월드 버튼 등에서 증거품 항목 획득). 중복 획득은 무시된다. + public void AddEvidence(EvidenceData entry) { - if (item == null) return; - string id = ItemIdOf(item); - if (_state.Items.Contains(id)) return; - _state.Items.Add(id); + if (entry == null) return; + string id = EvidenceIdOf(entry); + if (_state.Evidence.Contains(id)) return; + _state.Evidence.Add(id); Changed?.Invoke(); } - public void RemoveItem(ItemData item) + public void RemoveEvidence(EvidenceData entry) { - if (item == null || !_state.Items.Remove(ItemIdOf(item))) return; + if (entry == null || !_state.Evidence.Remove(EvidenceIdOf(entry))) return; Changed?.Invoke(); } - // ItemData의 Id를 키로 사용 (비어있으면 에셋 이름) — 캐릭터 IdOf와 같은 규칙 - private static string ItemIdOf(ItemData i) => string.IsNullOrEmpty(i.Id) ? i.name : i.Id; + // EvidenceData의 Id를 키로 사용 (비어있으면 에셋 이름) — 캐릭터 IdOf와 같은 규칙 + private static string EvidenceIdOf(EvidenceData e) => string.IsNullOrEmpty(e.Id) ? e.name : e.Id; // ── 선택 이력 ──────────────────────────────────────────────── public bool HasChosen(string code) => _state.ChosenCodes.Contains(code); diff --git a/Assets/02_Scripts/Story/StoryState.cs b/Assets/02_Scripts/Story/StoryState.cs index d02cb8e..65de5a4 100644 --- a/Assets/02_Scripts/Story/StoryState.cs +++ b/Assets/02_Scripts/Story/StoryState.cs @@ -11,7 +11,7 @@ public class StoryState public readonly HashSet CompletedDialogs = new(); // 완료한 DialogGroup 이름 public readonly HashSet ChosenCodes = new(); // 골랐던 선택지 Code public readonly HashSet Triggers = new(); // 켜진 트리거 Id (대화 조건 검사용) - public readonly List Items = new(); // 가진 아이템 Id (획득 순서 유지 — 증거품창 표시 순서) + public readonly List Evidence = new(); // 가진 증거품 항목 Id (획득 순서 유지 = 표시 순서) public void Clear() { @@ -20,7 +20,7 @@ public void Clear() CompletedDialogs.Clear(); ChosenCodes.Clear(); Triggers.Clear(); - Items.Clear(); + Evidence.Clear(); } // ── JSON 변환 ──────────────────────────────────────────────── @@ -34,7 +34,7 @@ private class JsonData public List CompletedDialogs = new(); public List ChosenCodes = new(); public List Triggers = new(); - public List Items = new(); + public List Evidence = new(); } public string ToJson() @@ -48,7 +48,7 @@ public string ToJson() data.CompletedDialogs.AddRange(CompletedDialogs); data.ChosenCodes.AddRange(ChosenCodes); data.Triggers.AddRange(Triggers); - data.Items.AddRange(Items); + data.Evidence.AddRange(Evidence); return JsonUtility.ToJson(data, prettyPrint: true); } @@ -64,7 +64,7 @@ public static StoryState FromJson(string json) state.CompletedDialogs.UnionWith(data.CompletedDialogs); state.ChosenCodes.UnionWith(data.ChosenCodes); state.Triggers.UnionWith(data.Triggers ?? new List()); - state.Items.AddRange(data.Items ?? new List()); + state.Evidence.AddRange(data.Evidence ?? new List()); return state; } } diff --git a/Assets/02_Scripts/_Data/Item.meta b/Assets/02_Scripts/_Data/Evidence.meta similarity index 77% rename from Assets/02_Scripts/_Data/Item.meta rename to Assets/02_Scripts/_Data/Evidence.meta index c95e297..a57a93f 100644 --- a/Assets/02_Scripts/_Data/Item.meta +++ b/Assets/02_Scripts/_Data/Evidence.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d08e6466f285165448cc7a8751239a65 +guid: 862d041642c0cda48a942dfe6fbe5057 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/02_Scripts/_Data/Evidence/EvidenceData.cs b/Assets/02_Scripts/_Data/Evidence/EvidenceData.cs new file mode 100644 index 0000000..d8d6664 --- /dev/null +++ b/Assets/02_Scripts/_Data/Evidence/EvidenceData.cs @@ -0,0 +1,27 @@ +using UnityEngine; + +// 증거품 항목 하나 — 소지품이거나 인물이거나. +// +// 분류(소지품/인물 정보) 필드가 여기 없는 것은 의도된 것이다. +// 분류는 EvidenceDatabase의 어느 목록에 넣었는지로 결정된다 — 항목에도 분류 필드를 두면 +// "목록 위치"와 "필드 값"이 어긋날 수 있으므로, 아예 어긋날 수 없게 한쪽만 남겼다. +// 두 분류가 이름·그림·설명이라는 같은 모양을 쓰므로 타입도 나누지 않는다. +// +// Id가 곧 "제시 키"다 — 대화 노드의 HiddenBranch.Key와 같은 값을 쓰면, +// 그 노드가 재생되는 동안 이 항목을 제시했을 때 해당 히든 분기가 열린다. +// (DialogTouchZone의 부위 키와 완전히 같은 통로를 쓴다) +[CreateAssetMenu(menuName = "Evidence/Evidence Data")] +public class EvidenceData : ScriptableObject +{ + [Tooltip("고유 Id — 저장 파일과 히든 분기 Key에 쓰인다. 비우면 에셋 이름")] + public string Id; + + [Tooltip("증거품창에 표시할 이름")] + public string Name; + + [Tooltip("증거품창에 표시할 그림 (소지품 사진 / 인물 초상)")] + public Sprite Icon; + + [Tooltip("증거품창에 표시할 설명")] + [TextArea(2, 5)] public string Description; +} diff --git a/Assets/02_Scripts/_Data/Item/ItemData.cs.meta b/Assets/02_Scripts/_Data/Evidence/EvidenceData.cs.meta similarity index 100% rename from Assets/02_Scripts/_Data/Item/ItemData.cs.meta rename to Assets/02_Scripts/_Data/Evidence/EvidenceData.cs.meta diff --git a/Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs b/Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs new file mode 100644 index 0000000..01ac325 --- /dev/null +++ b/Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using UnityEngine; + +// 증거품의 하위 분류. 어느 목록에 들어 있는지가 곧 분류다 (EvidenceDatabase 참고). +public enum EvidenceCategory +{ + [InspectorName("소지품")] Item, + [InspectorName("인물 정보")] Profile, +} + +// 게임에 존재하는 모든 증거품 항목의 목록 (StoryDatabase와 같은 "단일 원본" 역할). +// +// 분류마다 목록을 따로 둔다. 데이터를 넣는 사람은 드롭다운을 고를 필요 없이 +// 해당 목록에 끌어다 놓기만 하면 되고, 분류가 두 곳에 적히지 않으니 어긋날 수도 없다. +// +// 저장 파일에는 Id 문자열만 남으므로, Id → EvidenceData 복원은 이 에셋이 담당한다. +// 게임에 하나만 만들어 EvidenceHud에 꽂아 쓴다. +[CreateAssetMenu(menuName = "Evidence/Evidence Database")] +public class EvidenceDatabase : ScriptableObject +{ + [Tooltip("소지품 — 물건 증거품")] + [SerializeField] private List _items = new(); + + [Tooltip("인물 정보 — 사건 관계자")] + [SerializeField] private List _profiles = new(); + + public IReadOnlyList GetEntries(EvidenceCategory category) + => category == EvidenceCategory.Item ? _items : _profiles; + + // Id로 항목을 찾고 그 분류까지 함께 알려준다. 어느 목록에도 없으면 null. + public EvidenceData FindById(string id, out EvidenceCategory category) + { + category = EvidenceCategory.Item; + if (string.IsNullOrEmpty(id)) return null; + + var found = FindIn(_items, id); + if (found != null) return found; + + category = EvidenceCategory.Profile; + return FindIn(_profiles, id); + } + + public EvidenceData FindById(string id) => FindById(id, out _); + + private static EvidenceData FindIn(List list, string id) + { + foreach (var entry in list) + { + if (entry == null) continue; + string entryId = string.IsNullOrEmpty(entry.Id) ? entry.name : entry.Id; + if (entryId == id) return entry; + } + return null; + } + +#if UNITY_EDITOR + // 같은 항목을 두 목록에 넣으면 분류가 모호해지므로(먼저 찾은 쪽이 이긴다) 넣는 즉시 알려준다. + private void OnValidate() + { + foreach (var entry in _items) + if (entry != null && _profiles.Contains(entry)) + Debug.LogWarning($"[EvidenceDatabase] '{entry.name}'이(가) 소지품과 인물 정보 " + + "양쪽에 있습니다 — 한쪽에서 빼주세요", this); + } +#endif +} diff --git a/Assets/02_Scripts/_Data/Item/ItemDatabase.cs.meta b/Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs.meta similarity index 100% rename from Assets/02_Scripts/_Data/Item/ItemDatabase.cs.meta rename to Assets/02_Scripts/_Data/Evidence/EvidenceDatabase.cs.meta diff --git a/Assets/02_Scripts/_Data/Item/ItemData.cs b/Assets/02_Scripts/_Data/Item/ItemData.cs deleted file mode 100644 index 17aa2e4..0000000 --- a/Assets/02_Scripts/_Data/Item/ItemData.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UnityEngine; - -// 증거품/아이템 하나 (역전재판의 법정기록 항목). -// -// Id가 곧 "제시 키"다 — 대화 노드의 HiddenBranch.Key와 같은 값을 쓰면, -// 그 노드가 재생되는 동안 증거품창에서 이 아이템을 클릭했을 때 해당 히든 분기가 열린다. -// (DialogTouchZone의 부위 키와 완전히 같은 통로를 쓴다) -[CreateAssetMenu(menuName = "Item/Item Data")] -public class ItemData : ScriptableObject -{ - [Tooltip("고유 Id — 저장 파일과 히든 분기 Key에 쓰인다. 비우면 에셋 이름")] - public string Id; - - [Tooltip("증거품창에 표시할 이름")] - public string Name; - - [Tooltip("증거품창에 표시할 그림")] - public Sprite Icon; - - [Tooltip("증거품창에 표시할 설명")] - [TextArea(2, 5)] public string Description; -} diff --git a/Assets/02_Scripts/_Data/Item/ItemDatabase.cs b/Assets/02_Scripts/_Data/Item/ItemDatabase.cs deleted file mode 100644 index 832be96..0000000 --- a/Assets/02_Scripts/_Data/Item/ItemDatabase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -// 게임에 존재하는 모든 아이템의 목록 (StoryDatabase와 같은 "단일 원본" 역할). -// 저장 파일에는 Id 문자열만 남으므로, Id → ItemData 복원은 이 에셋이 담당한다. -// 게임에 하나만 만들어 ItemHud에 꽂아 쓴다. -[CreateAssetMenu(menuName = "Item/Item Database")] -public class ItemDatabase : ScriptableObject -{ - [SerializeField] private List _items = new(); - - public IReadOnlyList Items => _items; - - public ItemData FindById(string id) - { - if (string.IsNullOrEmpty(id)) return null; - foreach (var item in _items) - { - if (item == null) continue; - string itemId = string.IsNullOrEmpty(item.Id) ? item.name : item.Id; - if (itemId == id) return item; - } - return null; - } -} diff --git a/Assets/02_Scripts/_Debug/DialogUITester.cs b/Assets/02_Scripts/_Debug/DialogUITester.cs index e6ce10d..31b40ff 100644 --- a/Assets/02_Scripts/_Debug/DialogUITester.cs +++ b/Assets/02_Scripts/_Debug/DialogUITester.cs @@ -29,8 +29,8 @@ private void Update() _index++; } - // Tab → 대화 선택 메뉴 표시 - if (kb.tabKey.wasPressedThisFrame && DialogEnterHud.Instance != null) + // T → 대화 선택 메뉴 표시 + if (kb.tKey.wasPressedThisFrame && DialogEnterHud.Instance != null) _ = ShowMenu(); } diff --git a/Assets/02_Scripts/_UI/Communication/DialogEnterHud.cs b/Assets/02_Scripts/_UI/Communication/DialogEnterHud.cs index 974fc0e..71d9ad9 100644 --- a/Assets/02_Scripts/_UI/Communication/DialogEnterHud.cs +++ b/Assets/02_Scripts/_UI/Communication/DialogEnterHud.cs @@ -2,7 +2,7 @@ using UnityEngine; using UnityEngine.UIElements; -// "어떤 대화를 걸지" 고르는 메뉴 (역전재판식 "대화하기" — 후보가 여럿이면 골라서 시작). +// "어떤 대화를 걸지" 고르는 메뉴 — 한 캐릭터에게 걸 수 있는 대화가 여럿이면 골라서 시작한다. // DialogEnterUI.uxml의 ListView로 후보를 띄우고, 행을 클릭하면 그 인덱스를 반환한다. // 대사 도중의 분기 선택지(ChoiceHud)와는 별개다 — 이건 대화를 시작하기 전 단계의 UI. // DialogPlayer.SelectBeat가 재생 가능한 비트가 여럿일 때 사용한다. diff --git a/Assets/02_Scripts/_UI/Item.meta b/Assets/02_Scripts/_UI/Evidence.meta similarity index 77% rename from Assets/02_Scripts/_UI/Item.meta rename to Assets/02_Scripts/_UI/Evidence.meta index d5d5ffa..9a17b1e 100644 --- a/Assets/02_Scripts/_UI/Item.meta +++ b/Assets/02_Scripts/_UI/Evidence.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0c3122efb3a7a304ca03b2bfaa0fad18 +guid: 026166c0bdc41234c8ea34d015e7ce9f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/02_Scripts/_UI/Item/ItemHud.cs b/Assets/02_Scripts/_UI/Evidence/EvidenceHud.cs similarity index 67% rename from Assets/02_Scripts/_UI/Item/ItemHud.cs rename to Assets/02_Scripts/_UI/Evidence/EvidenceHud.cs index fca9f3d..554ec4b 100644 --- a/Assets/02_Scripts/_UI/Item/ItemHud.cs +++ b/Assets/02_Scripts/_UI/Evidence/EvidenceHud.cs @@ -4,24 +4,36 @@ using UnityEngine.Events; using UnityEngine.UIElements; -// 증거품창 (역전재판의 법정기록). ItemListUI.uxml을 띄우고 가진 아이템을 좌우 화살표로 넘겨 본다. +// 증거품창. 가진 항목을 좌우 화살표로 넘겨 본다. // -// 제시(들이대기): 대화 노드가 히든 분기로 무장돼 있는 동안 아이템 그림을 클릭하면 -// HiddenBranchResolver.Fire(아이템 Id)를 쏜다 — DialogTouchZone의 부위 키와 완전히 같은 통로라서, -// 노드에 Key = 아이템 Id 인 히든 분기를 달아두면 그 분기가 열린다. 발동하면 창은 자동으로 닫힌다. -// 무장돼 있지 않으면(평소) 클릭해도 아무 일도 없다 — 그냥 열람용 창이다. +// 하위 분류가 둘이고 Evidence 버튼 하나로 순환한다: 닫힘 → 소지품 → 인물 정보 → 닫힘. +// 두 분류는 같은 창·같은 레이아웃을 쓰고, 보고 있는 분류의 항목만 목록에 오른다. // -// 목록은 StoryManager의 아이템 이력(Id)을 ItemDatabase로 복원해 만든다. 획득 순서 = 표시 순서. +// 제시(들이대기): 대화 노드가 히든 분기로 무장돼 있는 동안 그림을 클릭하면 +// HiddenBranchResolver.Fire(항목 Id)를 쏜다 — DialogTouchZone의 부위 키와 완전히 같은 통로라서, +// 노드에 Key = 항목 Id 인 히든 분기를 달아두면 그 분기가 열린다. 발동하면 창은 자동으로 닫힌다. +// 소지품과 인물 모두 제시할 수 있다. 무장돼 있지 않으면(평소) 클릭해도 아무 일도 없다. +// +// 목록은 StoryManager의 획득 이력(Id)을 EvidenceDatabase로 복원해 만든다. 획득 순서 = 표시 순서. [RequireComponent(typeof(PanelRenderer))] -public class ItemHud : MonoBehaviour +public class EvidenceHud : MonoBehaviour { - public static ItemHud Instance { get; private set; } + public static EvidenceHud Instance { get; private set; } - [Tooltip("Id → ItemData 복원용 아이템 전체 목록 (게임에 하나)")] - [SerializeField] private ItemDatabase _database; + [Tooltip("Id → EvidenceData 복원용 전체 목록 (소지품·인물 모두 포함, 게임에 하나)")] + [SerializeField] private EvidenceDatabase _database; - [Tooltip("증거품이 하나도 없을 때 설명 칸에 표시할 문구")] - [SerializeField] private string _emptyText = "가지고 있는 증거품이 없습니다."; + [Tooltip("창 상단에 표시할 소지품 분류 이름")] + [SerializeField] private string _itemCategoryLabel = "소지품"; + + [Tooltip("창 상단에 표시할 인물 정보 분류 이름")] + [SerializeField] private string _profileCategoryLabel = "인물 정보"; + + [Tooltip("가진 소지품이 없을 때 설명 칸에 표시할 문구")] + [SerializeField] private string _emptyItemText = "가지고 있는 소지품이 없습니다."; + + [Tooltip("등록된 인물이 없을 때 설명 칸에 표시할 문구")] + [SerializeField] private string _emptyProfileText = "등록된 인물이 없습니다."; [Tooltip("제시가 실제로 발동했을 때 호출 (연출·사운드용). 발동하지 않은 클릭에는 불리지 않는다")] [SerializeField] private UnityEvent _onPresented; @@ -35,7 +47,8 @@ public class ItemHud : MonoBehaviour private PanelRenderer _panelRenderer; private VisualElement _root; // 전체 토글 대상(#Body) - private VisualElement _itemNode; // 슬라이드 대상 (.ItemNode — 그림+설명 묶음) + private VisualElement _itemNode; // 슬라이드 대상 (.EvidenceNode — 그림+설명 묶음) + private Label _categoryLabel; // 창 상단 분류 이름 (#EvidenceCategory) private Image _image; private Label _label; @@ -48,8 +61,9 @@ public class ItemHud : MonoBehaviour // 현재 표시 상태. 리로드로 요소가 새로 만들어져도 이 값으로 복원된다. private bool _visible; + private EvidenceCategory _category = EvidenceCategory.Item; // 지금 보고 있는 분류 private int _index; - private readonly List _owned = new(); + private readonly List _owned = new(); // 창이 떠 있는가 (DialogTouchZone이 창 뒤 월드 클릭을 무시하는 데 쓴다) public bool IsOpen => _visible; @@ -100,7 +114,8 @@ private void OnUIReload(PanelRenderer panelRenderer, VisualElement root, int ver _uiVersion = version; _root = root.Q("Body"); - _itemNode = root.Q(className: "ItemNode"); + _itemNode = root.Q(className: "EvidenceNode"); + _categoryLabel = root.Q