Projext/Assets/02_Scripts/Player/Upgrade/UI/CardUI.cs
2026-03-01 12:22:29 +09:00

175 lines
10 KiB
C#

using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
using TMPro; // 텍스트메쉬프로를 사용할거에요 -> TMPro를
using UnityEngine.UI; // UI 기능을 사용할거에요 -> UnityEngine.UI를
using System.Collections.Generic; // 리스트를 사용할거에요 -> System.Collections.Generic을
public class CardUI : MonoBehaviour // 클래스를 선언할거에요 -> 카드 UI를
{
[SerializeField] private TextMeshProUGUI effectText; // 변수를 선언할거에요 -> 효과 텍스트를
[SerializeField] private Image iconImage; // 변수를 선언할거에요 -> 아이콘 이미지를
[SerializeField] private Outline selectionOutline; // 변수를 선언할거에요 -> 선택 테두리를 (기존 Outline, 유지 또는 제거 가능)
[Header("--- 파티클 이펙트 ---")] // 인스펙터 제목을 달거에요 -> 파티클 이펙트 설정을
[SerializeField] private CardParticleEffect particleEffect; // 변수를 선언할거에요 -> 파티클 이펙트 컴포넌트를 (자동 캐싱되므로 미할당 가능)
private CardData cardData; // 변수를 선언할거에요 -> 카드 데이터를
private LevelUpUIManager uiManager; // 변수를 선언할거에요 -> UI 매니저를
// [FIX] 3개 능력치 저장용 리스트
private List<StatType> rolledStats = new List<StatType>(); // 리스트를 선언할거에요 -> 뽑힌 스탯 타입들을
private List<int> rolledValues = new List<int>(); // 리스트를 선언할거에요 -> 뽑힌 수치들을
private bool isRandomCard = false; // 변수를 초기화할거에요 -> 랜덤 카드 여부를
public void Setup(CardData data, LevelUpUIManager manager, StatType mainStat = StatType.Health) // 함수를 선언할거에요 -> 카드를 설정하는 Setup을 (mainStat = 이 카드의 메인 버프 스탯)
{
cardData = data; // 값을 저장할거에요 -> 카드 데이터를
uiManager = manager; // 값을 저장할거에요 -> 매니저를
if (selectionOutline != null) selectionOutline.enabled = false; // 꺼줄거에요 -> 테두리를
// ===== [추가] 파티클 이펙트 자동 캐싱 =====
if (particleEffect == null) // 조건이 맞으면 실행할거에요 -> Inspector에서 안 넣었다면
particleEffect = GetComponent<CardParticleEffect>(); // 캐싱할거에요 -> 같은 오브젝트에서 찾아서
// ===== [추가] 파티클 이펙트 초기화 =====
if (particleEffect != null) // 조건이 맞으면 실행할거에요 -> 이펙트 컴포넌트가 있다면
particleEffect.ResetEffect(); // 초기화할거에요 -> 이전 선택 파티클을 깨끗하게
// 아이콘 설정
if (iconImage != null && cardData.icon != null) // 조건이 맞으면 실행할거에요 -> 아이콘이 있다면
iconImage.sprite = cardData.icon; // 설정할거에요 -> 아이콘을
RandomStatCardData randomData = cardData as RandomStatCardData; // 형변환할거에요 -> 랜덤 카드 데이터로
if (randomData != null) // 조건이 맞으면 실행할거에요 -> 랜덤 카드라면
{
isRandomCard = true; // 상태를 바꿀거에요 -> 랜덤 카드로
RollAllStats(randomData, mainStat); // 실행할거에요 -> 메인 스탯 지정해서 3개 능력치 뽑기를
}
else // 조건이 틀리면 실행할거에요 -> 일반 카드라면
{
effectText.text = cardData.GetText(); // 텍스트를 설정할거에요 -> 기본 텍스트를
}
}
/// <summary>
/// [FIX] possibleStats의 모든 스탯을 중복 없이 뽑아서 표시
/// 첫 번째 스탯은 양수(버프), 나머지는 음수(디버프)
/// </summary>
private void RollAllStats(RandomStatCardData randomData, StatType mainStat) // 함수를 선언할거에요 -> 메인 스탯 지정해서 3개 능력치를 뽑는 RollAllStats를
{
rolledStats.Clear(); // 비울거에요 -> 이전 데이터를
rolledValues.Clear(); // 비울거에요 -> 이전 데이터를
// ① 메인 스탯 (버프) 먼저 고정
int mainValue = Random.Range( // 뽑을거에요 -> 양수 버프 수치를
Mathf.Max(1, randomData.minValue), // 최솟값을 1 이상으로 보장
Mathf.Max(2, randomData.maxValue) + 1 // 최댓값 + 1 (상한 미포함)
);
rolledStats.Add(mainStat); // 추가할거에요 -> 메인 스탯을 첫 번째로
rolledValues.Add(mainValue); // 추가할거에요 -> 버프 수치를
// ② 나머지 스탯들 (디버프) — mainStat 제외하고 추가
List<StatType> otherStats = new List<StatType>(); // 리스트를 만들거에요 -> 나머지 스탯을
foreach (StatType s in System.Enum.GetValues(typeof(StatType))) // 반복할거에요 -> 모든 StatType을
{
if (s != mainStat) otherStats.Add(s); // 추가할거에요 -> 메인 스탯이 아닌 것만
}
ShuffleList(otherStats); // 섞을거에요 -> 디버프 순서를 랜덤으로
foreach (StatType s in otherStats) // 반복할거에요 -> 나머지 스탯들을
{
int debuffValue = Random.Range( // 뽑을거에요 -> 음수 디버프 수치를
Mathf.Min(-1, -randomData.maxValue), // 최솟값 (음수)
0 // 상한 0 (미포함 → 항상 음수)
);
rolledStats.Add(s); // 추가할거에요 -> 디버프 스탯을
rolledValues.Add(debuffValue); // 추가할거에요 -> 디버프 수치를
}
// ③ 텍스트 조합 — 3줄 표시
string display = ""; // 초기화할거에요 -> 표시 텍스트를
for (int i = 0; i < rolledStats.Count; i++) // 반복할거에요 -> 모든 스탯에 대해
{
int val = rolledValues[i]; // 가져올거에요 -> 수치를
string sign = val >= 0 ? "+" : ""; // 결정할거에요 -> 부호를
string colorTag = val >= 0 ? "<color=#4AFF4A>" : "<color=#FF4A4A>"; // 결정할거에요 -> 버프=초록, 디버프=빨강
string statName = GetStatDisplayName(rolledStats[i]); // 가져올거에요 -> 스탯 표시명을
display += $"{colorTag}{statName} {sign}{val}</color>"; // 추가할거에요 -> 한 줄을
if (i < rolledStats.Count - 1) display += "\n"; // 추가할거에요 -> 줄바꿈을 (마지막 제외)
}
effectText.text = display; // 설정할거에요 -> 완성된 텍스트를
}
/// <summary>
/// 스탯 타입을 표시용 이름으로 변환
/// </summary>
private string GetStatDisplayName(StatType stat) // 함수를 선언할거에요 -> 스탯 표시명을 반환하는
{
switch (stat) // 분기할거에요 -> 스탯 타입에 따라
{
case StatType.Health: return "Health"; // 반환할거에요 -> Health를
case StatType.Speed: return "Speed"; // 반환할거에요 -> Speed를
case StatType.Damage: return "Damage"; // 반환할거에요 -> Damage를
default: return stat.ToString(); // 반환할거에요 -> 기본 이름을
}
}
/// <summary>
/// 리스트를 랜덤으로 섞기 (Fisher-Yates 셔플)
/// </summary>
private void ShuffleList<T>(List<T> list) // 함수를 선언할거에요 -> 리스트를 셔플하는
{
for (int i = list.Count - 1; i > 0; i--) // 반복할거에요 -> 뒤에서부터
{
int j = Random.Range(0, i + 1); // 뽑을거에요 -> 랜덤 인덱스를
T temp = list[i]; // 임시 저장할거에요 -> 현재 값을
list[i] = list[j]; // 교체할거에요 -> i와 j를
list[j] = temp; // 교체할거에요 -> j에 임시값을
}
}
// 카드 클릭 시 매니저에게 알림
public void OnClick() // 함수를 선언할거에요 -> 클릭 처리를
{
uiManager.OnCardClick(this); // 알릴거에요 -> 매니저에게
}
// ===== [수정] 선택 시 기존 Outline + 파티클 이펙트 동시 제어 =====
public void SetSelected(bool isSelected) // 함수를 선언할거에요 -> 선택 상태를
{
// --- 기존 Outline 처리 (있으면 동작, 없어도 안전) ---
if (selectionOutline != null) selectionOutline.enabled = isSelected; // 설정할거에요 -> 테두리를
// --- [수정] 파티클 이펙트 처리 ---
if (particleEffect != null) // 조건이 맞으면 실행할거에요 -> 파티클 이펙트가 있다면
{
if (isSelected) // 조건이 맞으면 실행할거에요 -> 선택 상태라면
particleEffect.Select(); // 실행할거에요 -> 파티클 Play + 스케일 확대를
else // 조건이 틀리면 실행할거에요 -> 해제 상태라면
particleEffect.Deselect(); // 실행할거에요 -> 파티클 Stop + 스케일 원복을
}
else // 조건이 틀리면 실행할거에요 -> 이펙트 없으면 기존 방식 유지
{
transform.localScale = isSelected ? Vector3.one * 1.05f : Vector3.one; // 크기를 조절할거에요 -> 선택되면 키우기
}
}
// [FIX] APPLY 버튼 누를 때 3개 능력치 전부 적용
public void ApplyCurrentEffect() // 함수를 선언할거에요 -> 효과를 적용하는 ApplyCurrentEffect를
{
if (isRandomCard) // 조건이 맞으면 실행할거에요 -> 랜덤 카드라면
{
RandomStatCardData rd = cardData as RandomStatCardData; // 형변환할거에요 -> 랜덤 카드로
for (int i = 0; i < rolledStats.Count; i++) // 반복할거에요 -> 모든 뽑힌 스탯에 대해
{
rd.ApplyToPlayer(rolledStats[i], rolledValues[i]); // 적용할거에요 -> 각 스탯과 수치를
}
}
else // 조건이 틀리면 실행할거에요 -> 일반 카드라면
{
cardData.Execute(); // 실행할거에요 -> 기본 효과를
}
FindObjectOfType<PlayerHealth>()?.RefreshHealthUI(); // 실행할거에요 -> 체력 UI 갱신을
}
}