BT12-MVP-A Phase 2-B (코드): SkillSelectionUI + SkillCardSlot + LevelUpManager UI 통합
PD 직접 지시 2026-05-08 — Phase 2-A 검증 우선 + Phase 2-B 진행. 본 응답 = 코드 영역. 신규 영역 (2 파일): - Assets/Scripts/MyUI/SkillCardSlot.cs — 단일 카드 슬롯 (PD 예시 정합) · 1. 상단 색상 배너 (등급별: 청록 Common · 노랑 Rare · 빨강 Max) · 2. 카드 이름 (한글) · 3. 원형 아이콘 + 동심원 빛 효과 · 4. 레벨 N / 최대 (빨강 강조) · 5. 효과 설명 3~4 라인 · 클릭 영역 + 선택 highlight - Assets/Scripts/MyUI/SkillSelectionUI.cs — 스킬 선택 화면 (PD 예시 정합) · 헤더: '기술 선택' 타이틀 + X 닫기 버튼 · 본체: 카드 3장 가로 배치 (SkillCardSlot ×3) · 푸터: '남은 포인트: N' + '확인' 버튼 · 인터랙션: 카드 클릭 → 선택·highlight / 확인 → onConfirm 콜백 / X → 첫 카드 자동 fallback 기존 수정 (1 파일): - Assets/Scripts/Progression/LevelUpManager.cs · _ui 필드 (SkillSelectionUI) 추가 · HandleLevelUp → _ui.Show(cards, level, HandleCardConfirmed) 정식 호출 · UI 부재 fallback 영역 (placeholder asset 미등록 시) · HandleCardConfirmed → _ui.Hide() + 게임 재개 TMPro 영역: Unity 6 com.unity.ugui 2.0.0 통합 — 별도 패키지 X. Phase 2-B 다음 세그먼트 (별도): placeholder asset 5장 + Canvas Prefab + Scene [LevelUpManager] 통합.
This commit is contained in:
parent
047661cc49
commit
5b2b753784
|
|
@ -0,0 +1,104 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using EerieVillage.Progression;
|
||||
|
||||
namespace EerieVillage.MyUI
|
||||
{
|
||||
/// <summary>
|
||||
/// BT12-MVP-A 영역 단일 카드 슬롯 — PD 첨부 예시 ("기술 선택" 화면) 정합.
|
||||
/// SkillSelectionUI 영역 3개 자식.
|
||||
///
|
||||
/// PD 예시 카드 구성:
|
||||
/// 1. 상단 색상 배너 (등급 — 청록 Common · 노랑 Rare · 빨강 Max)
|
||||
/// 2. 카드 이름 (한글)
|
||||
/// 3. 원형 아이콘 + 동심원 빛 효과
|
||||
/// 4. "레벨 N" 또는 "최대" (빨강 강조)
|
||||
/// 5. 효과 설명 3~4 라인
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using EerieVillage.Progression;
|
||||
|
||||
namespace EerieVillage.MyUI
|
||||
{
|
||||
/// <summary>
|
||||
/// BT12-MVP-A 영역 스킬 선택 UI — PD 첨부 예시 ("기술 선택" 화면) 정합.
|
||||
/// LevelUpManager 영역 호출 — Show(cards, level, onConfirm).
|
||||
///
|
||||
/// 화면 구조:
|
||||
/// [Header] "기술 선택" 타이틀 + X 닫기 버튼
|
||||
/// [Body] 카드 3장 가로 배치 (SkillCardSlot ×3)
|
||||
/// [Footer] "남은 포인트: N" + "확인" 버튼
|
||||
///
|
||||
/// 인터랙션:
|
||||
/// - 카드 클릭 → 선택 (highlight)
|
||||
/// - 확인 버튼 → onConfirm 콜백 + UI 닫기
|
||||
/// - X 버튼 → 첫 카드 자동 선택 + 콜백 (취소 영역 placeholder)
|
||||
/// </summary>
|
||||
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<SkillCardPlaceholder> _onConfirm;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// unscaledDeltaTime 정합 영역 — Time.timeScale=0 시 UI 인터랙션 보장 (Animator·EventSystem 영역 PD 추후 검증)
|
||||
if (_rootPanel != null) _rootPanel.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>레벨업 시점 호출 — 카드 3장 표시 + 사용자 선택 대기.</summary>
|
||||
public void Show(List<SkillCardPlaceholder> cards, int level, System.Action<SkillCardPlaceholder> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 정식 통합.
|
||||
/// </summary>
|
||||
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<SkillCardPlaceholder>();
|
||||
|
||||
// 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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue