diff --git a/Assets/Scripts/MyUI/SkillCardSlot.cs b/Assets/Scripts/MyUI/SkillCardSlot.cs new file mode 100644 index 0000000..335e980 --- /dev/null +++ b/Assets/Scripts/MyUI/SkillCardSlot.cs @@ -0,0 +1,104 @@ +using UnityEngine; +using UnityEngine.UI; +using TMPro; +using EerieVillage.Progression; + +namespace EerieVillage.MyUI +{ + /// + /// BT12-MVP-A 영역 단일 카드 슬롯 — PD 첨부 예시 ("기술 선택" 화면) 정합. + /// SkillSelectionUI 영역 3개 자식. + /// + /// PD 예시 카드 구성: + /// 1. 상단 색상 배너 (등급 — 청록 Common · 노랑 Rare · 빨강 Max) + /// 2. 카드 이름 (한글) + /// 3. 원형 아이콘 + 동심원 빛 효과 + /// 4. "레벨 N" 또는 "최대" (빨강 강조) + /// 5. 효과 설명 3~4 라인 + /// + public class SkillCardSlot : MonoBehaviour + { + [Header("PD 예시 정합 — 카드 구성")] + [SerializeField] Image _topBanner; // 1. 상단 색상 배너 + [SerializeField] TMP_Text _nameText; // 2. 카드 이름 (한글) + [SerializeField] Image _icon; // 3. 원형 아이콘 + [SerializeField] Image _glowEffect; // 3. 동심원 빛 효과 + [SerializeField] TMP_Text _levelText; // 4. 레벨 N / 최대 + [SerializeField] TMP_Text _descriptionText; // 5. 효과 설명 + + [Header("인터랙션")] + [SerializeField] Button _clickArea; // 카드 전체 클릭 영역 + [SerializeField] GameObject _highlightFrame; // 선택 시 활성 + + [Header("등급별 색상 (PD 예시 정합)")] + [SerializeField] Color _commonColor = new Color(0.3f, 0.7f, 0.7f, 1f); // 청록 + [SerializeField] Color _rareColor = new Color(0.95f, 0.7f, 0.25f, 1f); // 노랑 + [SerializeField] Color _maxColor = new Color(0.9f, 0.3f, 0.3f, 1f); // 빨강 + + public SkillCardPlaceholder Card { get; private set; } + + public void Bind(SkillCardPlaceholder card, System.Action onClick) + { + Card = card; + + if (card == null) + { + gameObject.SetActive(false); + return; + } + gameObject.SetActive(true); + + // 2. 카드 이름 + if (_nameText != null) _nameText.text = card.displayName; + + // 3. 아이콘 (sprite null 시 영역 image enable=false 또는 placeholder 영역 잔존) + if (_icon != null) + { + _icon.sprite = card.icon; + _icon.enabled = card.icon != null; + } + + // 5. 효과 설명 + if (_descriptionText != null) _descriptionText.text = card.description; + + // 1. 상단 색상 배너 + 4. 레벨 텍스트 — 등급별 영역 + Color bannerColor = card.rarity switch + { + CardRarity.Common => _commonColor, + CardRarity.Rare => _rareColor, + CardRarity.Max => _maxColor, + _ => _commonColor + }; + if (_topBanner != null) _topBanner.color = bannerColor; + + // 4. 레벨 N / 최대 + if (_levelText != null) + { + if (card.IsMaxLevel) + { + _levelText.text = "최대"; + _levelText.color = _maxColor; + } + else + { + _levelText.text = $"레벨 {card.currentLevel}"; + _levelText.color = Color.white; + } + } + + // 클릭 영역 + if (_clickArea != null) + { + _clickArea.onClick.RemoveAllListeners(); + _clickArea.onClick.AddListener(() => onClick?.Invoke()); + } + + SetHighlight(false); + } + + public void SetHighlight(bool active) + { + if (_highlightFrame != null) _highlightFrame.SetActive(active); + } + } +} diff --git a/Assets/Scripts/MyUI/SkillSelectionUI.cs b/Assets/Scripts/MyUI/SkillSelectionUI.cs new file mode 100644 index 0000000..871ef87 --- /dev/null +++ b/Assets/Scripts/MyUI/SkillSelectionUI.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using TMPro; +using EerieVillage.Progression; + +namespace EerieVillage.MyUI +{ + /// + /// BT12-MVP-A 영역 스킬 선택 UI — PD 첨부 예시 ("기술 선택" 화면) 정합. + /// LevelUpManager 영역 호출 — Show(cards, level, onConfirm). + /// + /// 화면 구조: + /// [Header] "기술 선택" 타이틀 + X 닫기 버튼 + /// [Body] 카드 3장 가로 배치 (SkillCardSlot ×3) + /// [Footer] "남은 포인트: N" + "확인" 버튼 + /// + /// 인터랙션: + /// - 카드 클릭 → 선택 (highlight) + /// - 확인 버튼 → onConfirm 콜백 + UI 닫기 + /// - X 버튼 → 첫 카드 자동 선택 + 콜백 (취소 영역 placeholder) + /// + public class SkillSelectionUI : MonoBehaviour + { + [Header("Root")] + [SerializeField] GameObject _rootPanel; // 일시정지 시 활성·복귀 시 비활성 + + [Header("Header")] + [SerializeField] TMP_Text _titleText; // "기술 선택" + [SerializeField] Button _closeButton; // X 버튼 (우측 상단) + + [Header("Card Slots (3개 가로 배치)")] + [SerializeField] SkillCardSlot _slot1; + [SerializeField] SkillCardSlot _slot2; + [SerializeField] SkillCardSlot _slot3; + + [Header("Footer")] + [SerializeField] TMP_Text _pointText; // "남은 포인트: N" + [SerializeField] Button _confirmButton; // "확인" + + SkillCardPlaceholder _selected; + System.Action _onConfirm; + + void Awake() + { + // unscaledDeltaTime 정합 영역 — Time.timeScale=0 시 UI 인터랙션 보장 (Animator·EventSystem 영역 PD 추후 검증) + if (_rootPanel != null) _rootPanel.SetActive(false); + } + + /// 레벨업 시점 호출 — 카드 3장 표시 + 사용자 선택 대기. + public void Show(List cards, int level, System.Action onConfirm) + { + _onConfirm = onConfirm; + _selected = null; + + if (_rootPanel != null) _rootPanel.SetActive(true); + if (_titleText != null) _titleText.text = "기술 선택"; + if (_pointText != null) _pointText.text = "남은 포인트: 1"; + if (_confirmButton != null) _confirmButton.interactable = false; + + // 카드 3장 영역 바인딩 (부족 영역 = 슬롯 hide) + BindSlot(_slot1, cards.Count > 0 ? cards[0] : null); + BindSlot(_slot2, cards.Count > 1 ? cards[1] : null); + BindSlot(_slot3, cards.Count > 2 ? cards[2] : null); + + // 버튼 영역 리스너 + if (_confirmButton != null) + { + _confirmButton.onClick.RemoveAllListeners(); + _confirmButton.onClick.AddListener(OnConfirmClicked); + } + if (_closeButton != null) + { + _closeButton.onClick.RemoveAllListeners(); + _closeButton.onClick.AddListener(OnCloseClicked); + } + } + + public void Hide() + { + if (_rootPanel != null) _rootPanel.SetActive(false); + } + + void BindSlot(SkillCardSlot slot, SkillCardPlaceholder card) + { + if (slot == null) return; + slot.Bind(card, () => OnCardSelected(slot, card)); + } + + void OnCardSelected(SkillCardSlot clickedSlot, SkillCardPlaceholder card) + { + if (card == null) return; + _selected = card; + if (_confirmButton != null) _confirmButton.interactable = true; + + // 다른 슬롯 영역 highlight 해제 + 선택 슬롯만 활성 + if (_slot1 != null) _slot1.SetHighlight(_slot1 == clickedSlot); + if (_slot2 != null) _slot2.SetHighlight(_slot2 == clickedSlot); + if (_slot3 != null) _slot3.SetHighlight(_slot3 == clickedSlot); + } + + void OnConfirmClicked() + { + if (_selected == null) return; + var sel = _selected; + _onConfirm?.Invoke(sel); + } + + void OnCloseClicked() + { + // 취소 영역 = 첫 카드 자동 확정 (BT12-MVP-A 영역 placeholder · 차기 영역 PD 결정 영역) + var fallback = _slot1 != null ? _slot1.Card : null; + _onConfirm?.Invoke(fallback); + } + } +} diff --git a/Assets/Scripts/Progression/LevelUpManager.cs b/Assets/Scripts/Progression/LevelUpManager.cs index eb05b35..2edf975 100644 --- a/Assets/Scripts/Progression/LevelUpManager.cs +++ b/Assets/Scripts/Progression/LevelUpManager.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UnityEngine; using Platformer.Mechanics; +using EerieVillage.MyUI; namespace EerieVillage.Progression { @@ -8,17 +9,14 @@ namespace EerieVillage.Progression /// 레벨업 발화 시 일시정지 + UI 호출 + 카드 선택 결과 수령. /// PlayerProgression.OnLevelUp 구독. /// - /// Phase 2-A 영역 — UI 호출 placeholder (Debug.Log). - /// Phase 2-B 영역 — SkillSelectionUI 영역 통합. + /// Phase 2-B 영역 (2026-05-08) — SkillSelectionUI 정식 통합. /// public class LevelUpManager : MonoBehaviour { public static LevelUpManager Instance { get; private set; } [SerializeField] SkillCardPlaceholderPool _pool; - - // Phase 2-B 영역 — SkillSelectionUI 영역 부착 - // [SerializeField] SkillSelectionUI _ui; + [SerializeField] SkillSelectionUI _ui; PlayerController _player; PlayerProgression _progression; @@ -73,25 +71,29 @@ namespace EerieVillage.Progression ? _pool.Draw3Random() : new List(); - // Phase 2-A 영역 placeholder — UI 호출 영역 Phase 2-B 영역 통합 - Debug.Log($"[LevelUpManager] LevelUp Lv.{newLevel} — 카드 {cards.Count}장 영역 (UI placeholder·Phase 2-B 통합 예정)"); - for (int i = 0; i < cards.Count; i++) + // Phase 2-B 영역 — SkillSelectionUI 정식 호출 + if (_ui != null) { - var c = cards[i]; - Debug.Log($" [{i + 1}] {c.displayName} ({c.rarity}·Lv.{c.currentLevel}{(c.IsMaxLevel ? "·최대" : "")})"); + _ui.Show(cards, newLevel, HandleCardConfirmed); + } + else + { + // UI 영역 부재 fallback (placeholder asset 5장 미등록 영역 등) + Debug.LogWarning($"[LevelUpManager] SkillSelectionUI 부재 — Lv.{newLevel} 카드 {cards.Count}장 영역 자동 확정"); + HandleCardConfirmed(cards.Count > 0 ? cards[0] : null); } - - // Phase 2-A 영역 임시 — 즉시 첫 카드 자동 확인 + 게임 재개 - // Phase 2-B 영역 = SkillSelectionUI.Show + 사용자 클릭·확인 후 콜백 - HandleCardConfirmed(cards.Count > 0 ? cards[0] : null); } void HandleCardConfirmed(SkillCardPlaceholder selected) { + // UI 닫기 + if (_ui != null) _ui.Hide(); + // 차기 BT12-Dev 영역 = PlayerSkillInventory.AddSkillByCardId(selected.id) // BT12-MVP-A 영역 = UI 닫기·게임 재개만 - Debug.Log($"[LevelUpManager] 카드 확정 영역 — {(selected != null ? selected.displayName : "NONE")} (효과 적용 X·BT12-Dev 본격 영역)"); + Debug.Log($"[LevelUpManager] 카드 확정 — {(selected != null ? selected.displayName : "NONE")} (효과 적용 X·BT12-Dev 본격 영역)"); + // 일시정지 해제 + 입력 복원 Time.timeScale = 1f; if (_player != null) _player.controlEnabled = true; _isLevelUpActive = false;