338 lines
11 KiB
C#
338 lines
11 KiB
C#
using System.Collections;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
[DisallowMultipleComponent]
|
|
public class MemoryProgressUI : MonoBehaviour
|
|
{
|
|
[Header("Reference")]
|
|
[SerializeField] private MemoryProgressManager memoryProgressManager;
|
|
[SerializeField] private bool autoFindManager = true;
|
|
[SerializeField] private bool registerOnEnable = true;
|
|
[Tooltip("매니저를 찾지 못했을 때만 0/max로 초기 표시합니다.")]
|
|
[SerializeField] private bool initializeEmptyWhenNoManager = true;
|
|
|
|
[Header("Text")]
|
|
[SerializeField] private TMP_Text titleText;
|
|
[SerializeField] private TMP_Text countText;
|
|
|
|
[Header("Piece Icons - Detail Panel")]
|
|
[Tooltip("상세 패널용 획득 조각 아이콘입니다. PieceSlot_01~05 안의 FilledIcon을 순서대로 넣으세요.")]
|
|
[SerializeField] private GameObject[] filledIcons;
|
|
|
|
[Tooltip("선택 사항입니다. 획득 후 계속 켜둘 정적 Glow 이미지입니다. 사용하지 않으려면 비워두세요.")]
|
|
[SerializeField] private GameObject[] glowIcons;
|
|
|
|
[Header("Fill Flash - One Shot")]
|
|
[Tooltip("조각이 새로 채워지는 순간 잠깐 켰다가 자동으로 끄는 효과입니다. PieceSlot_01~05 안의 FillFlashIcon을 순서대로 넣으세요.")]
|
|
[SerializeField] private GameObject[] fillFlashIcons;
|
|
|
|
[Tooltip("조각이 새로 채워질 때 FillFlashIcon을 잠깐 표시합니다.")]
|
|
[SerializeField] private bool useFillFlash = true;
|
|
|
|
[Tooltip("FillFlashIcon이 켜져 있는 시간입니다. 애니메이션 없이 SetActive만 사용합니다.")]
|
|
[SerializeField] private float fillFlashShowTime = 0.5f;
|
|
|
|
[Tooltip("SetProgress가 처음 호출될 때는 기존 진행도를 복구하는 상황일 수 있으므로 Flash를 재생하지 않습니다.")]
|
|
[SerializeField] private bool skipFlashOnFirstUpdate = true;
|
|
|
|
[Header("Single Icon State - Mini Button")]
|
|
[Tooltip("미니 버튼처럼 아이콘 1개만 상태 제어할 때 켭니다. 상세 패널에서는 꺼두세요.")]
|
|
[SerializeField] private bool useSingleIconState = false;
|
|
[Tooltip("0개일 때 켤 아이콘입니다. 예: 어두운 기억 조각. 소켓을 항상 보이게 둘 거면 비워둬도 됩니다.")]
|
|
[SerializeField] private GameObject emptyStateIcon;
|
|
[Tooltip("1개 이상일 때 켤 아이콘입니다. 예: MiniCrystalIcon.")]
|
|
[SerializeField] private GameObject activeStateIcon;
|
|
[Tooltip("진행도 상태에 따라 켤 정적 Glow 이미지입니다. 예: MiniGlowIcon.")]
|
|
[SerializeField] private GameObject singleStateGlowIcon;
|
|
[Tooltip("체크하면 완료 시에만 미니 Glow가 켜집니다. 끄면 1개 이상 획득 시 켜집니다.")]
|
|
[SerializeField] private bool singleStateGlowOnlyWhenCompleted = true;
|
|
|
|
[Header("Optional Objects")]
|
|
[Tooltip("모든 조각을 모았을 때 켤 정적 완료 표시 오브젝트입니다. 없어도 됩니다.")]
|
|
[SerializeField] private GameObject completedEffect;
|
|
|
|
[Header("Settings")]
|
|
[SerializeField] private int maxPieces = 5;
|
|
[SerializeField] private string title = "기억의 조각";
|
|
[SerializeField] private bool showCountText = true;
|
|
[Tooltip("상세 패널용 Glow Icons에만 적용됩니다.")]
|
|
[SerializeField] private bool showGlowOnlyWhenCompleted = true;
|
|
|
|
private bool registered;
|
|
private bool hasProgressSnapshot;
|
|
private int lastCurrentPieces;
|
|
private Coroutine[] flashRoutines;
|
|
|
|
private void Awake()
|
|
{
|
|
SetTitle();
|
|
InitializeFlashIcons(false);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (!registerOnEnable)
|
|
{
|
|
RefreshFromManager();
|
|
return;
|
|
}
|
|
|
|
TryRegisterToManager();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (registered && memoryProgressManager != null)
|
|
memoryProgressManager.UnregisterUI(this);
|
|
|
|
registered = false;
|
|
|
|
StopAllFlashRoutines();
|
|
InitializeFlashIcons(false);
|
|
}
|
|
|
|
public void SetProgress(int current, int max)
|
|
{
|
|
maxPieces = Mathf.Max(1, max);
|
|
current = Mathf.Clamp(current, 0, maxPieces);
|
|
|
|
int previous = lastCurrentPieces;
|
|
bool shouldPlayFlash = useFillFlash
|
|
&& hasProgressSnapshot
|
|
&& current > previous
|
|
&& (!skipFlashOnFirstUpdate || hasProgressSnapshot);
|
|
|
|
SetTitle();
|
|
|
|
if (countText != null)
|
|
{
|
|
countText.gameObject.SetActive(showCountText);
|
|
countText.text = $"{current} / {maxPieces}";
|
|
}
|
|
|
|
UpdatePieceIcons(current);
|
|
UpdateSingleStateIcons(current);
|
|
UpdateCompletedEffect(current >= maxPieces);
|
|
|
|
if (shouldPlayFlash)
|
|
PlayFillFlash(previous, current);
|
|
|
|
lastCurrentPieces = current;
|
|
hasProgressSnapshot = true;
|
|
}
|
|
|
|
public void RefreshFromManager()
|
|
{
|
|
if (memoryProgressManager == null && autoFindManager)
|
|
memoryProgressManager = FindFirstObjectByType<MemoryProgressManager>();
|
|
|
|
if (memoryProgressManager != null)
|
|
SetProgress(memoryProgressManager.CurrentPieces, memoryProgressManager.RequiredPieces);
|
|
else if (initializeEmptyWhenNoManager)
|
|
SetProgress(0, maxPieces);
|
|
}
|
|
|
|
public void SetMemoryProgressManager(MemoryProgressManager manager)
|
|
{
|
|
if (registered && memoryProgressManager != null)
|
|
memoryProgressManager.UnregisterUI(this);
|
|
|
|
memoryProgressManager = manager;
|
|
registered = false;
|
|
hasProgressSnapshot = false;
|
|
|
|
if (isActiveAndEnabled && registerOnEnable)
|
|
TryRegisterToManager();
|
|
else
|
|
RefreshFromManager();
|
|
}
|
|
|
|
public void ResetProgressSnapshot()
|
|
{
|
|
hasProgressSnapshot = false;
|
|
lastCurrentPieces = 0;
|
|
StopAllFlashRoutines();
|
|
InitializeFlashIcons(false);
|
|
}
|
|
|
|
private void TryRegisterToManager()
|
|
{
|
|
if (memoryProgressManager == null && autoFindManager)
|
|
memoryProgressManager = FindFirstObjectByType<MemoryProgressManager>();
|
|
|
|
if (memoryProgressManager != null)
|
|
{
|
|
memoryProgressManager.RegisterUI(this);
|
|
registered = true;
|
|
}
|
|
else if (initializeEmptyWhenNoManager)
|
|
{
|
|
SetProgress(0, maxPieces);
|
|
}
|
|
}
|
|
|
|
private void SetTitle()
|
|
{
|
|
if (titleText != null)
|
|
titleText.text = title;
|
|
}
|
|
|
|
private void UpdatePieceIcons(int current)
|
|
{
|
|
if (filledIcons != null)
|
|
{
|
|
for (int i = 0; i < filledIcons.Length; i++)
|
|
{
|
|
if (filledIcons[i] != null)
|
|
filledIcons[i].SetActive(i < current);
|
|
}
|
|
}
|
|
|
|
if (glowIcons == null)
|
|
return;
|
|
|
|
bool completed = current >= maxPieces;
|
|
|
|
for (int i = 0; i < glowIcons.Length; i++)
|
|
{
|
|
if (glowIcons[i] == null)
|
|
continue;
|
|
|
|
if (showGlowOnlyWhenCompleted)
|
|
glowIcons[i].SetActive(completed);
|
|
else
|
|
glowIcons[i].SetActive(i < current);
|
|
}
|
|
}
|
|
|
|
private void UpdateSingleStateIcons(int current)
|
|
{
|
|
if (!useSingleIconState)
|
|
return;
|
|
|
|
bool hasAnyPiece = current > 0;
|
|
bool completed = current >= maxPieces;
|
|
|
|
if (emptyStateIcon != null)
|
|
emptyStateIcon.SetActive(!hasAnyPiece);
|
|
|
|
if (activeStateIcon != null)
|
|
activeStateIcon.SetActive(hasAnyPiece);
|
|
|
|
if (singleStateGlowIcon != null)
|
|
{
|
|
bool showGlow = singleStateGlowOnlyWhenCompleted ? completed : hasAnyPiece;
|
|
singleStateGlowIcon.SetActive(showGlow);
|
|
}
|
|
}
|
|
|
|
private void UpdateCompletedEffect(bool completed)
|
|
{
|
|
if (completedEffect != null)
|
|
completedEffect.SetActive(completed);
|
|
}
|
|
|
|
private void PlayFillFlash(int previousCurrent, int newCurrent)
|
|
{
|
|
if (fillFlashIcons == null || fillFlashIcons.Length == 0)
|
|
return;
|
|
|
|
EnsureFlashRoutineArray();
|
|
|
|
int start = Mathf.Clamp(previousCurrent, 0, fillFlashIcons.Length);
|
|
int end = Mathf.Clamp(newCurrent, 0, fillFlashIcons.Length);
|
|
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
if (fillFlashIcons[i] == null)
|
|
continue;
|
|
|
|
if (flashRoutines[i] != null)
|
|
StopCoroutine(flashRoutines[i]);
|
|
|
|
flashRoutines[i] = StartCoroutine(FlashRoutine(i));
|
|
}
|
|
}
|
|
|
|
private IEnumerator FlashRoutine(int index)
|
|
{
|
|
GameObject target = fillFlashIcons[index];
|
|
|
|
if (target == null)
|
|
yield break;
|
|
|
|
target.SetActive(true);
|
|
|
|
if (fillFlashShowTime > 0f)
|
|
yield return new WaitForSeconds(fillFlashShowTime);
|
|
|
|
if (target != null)
|
|
target.SetActive(false);
|
|
|
|
if (flashRoutines != null && index >= 0 && index < flashRoutines.Length)
|
|
flashRoutines[index] = null;
|
|
}
|
|
|
|
private void EnsureFlashRoutineArray()
|
|
{
|
|
int length = fillFlashIcons == null ? 0 : fillFlashIcons.Length;
|
|
|
|
if (flashRoutines == null || flashRoutines.Length != length)
|
|
flashRoutines = new Coroutine[length];
|
|
}
|
|
|
|
private void StopAllFlashRoutines()
|
|
{
|
|
if (flashRoutines == null)
|
|
return;
|
|
|
|
for (int i = 0; i < flashRoutines.Length; i++)
|
|
{
|
|
if (flashRoutines[i] != null)
|
|
{
|
|
StopCoroutine(flashRoutines[i]);
|
|
flashRoutines[i] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeFlashIcons(bool active)
|
|
{
|
|
if (fillFlashIcons == null)
|
|
return;
|
|
|
|
for (int i = 0; i < fillFlashIcons.Length; i++)
|
|
{
|
|
if (fillFlashIcons[i] != null)
|
|
fillFlashIcons[i].SetActive(active);
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnValidate()
|
|
{
|
|
maxPieces = Mathf.Max(1, maxPieces);
|
|
fillFlashShowTime = Mathf.Max(0f, fillFlashShowTime);
|
|
|
|
if (filledIcons != null && filledIcons.Length > 0 && filledIcons.Length != maxPieces)
|
|
{
|
|
Debug.LogWarning(
|
|
$"MemoryProgressUI: Filled Icons 개수({filledIcons.Length})와 Max Pieces({maxPieces})가 다릅니다. 미니 버튼 아이콘 1개 제어는 Filled Icons가 아니라 Single Icon State를 사용하세요.",
|
|
this
|
|
);
|
|
}
|
|
|
|
if (glowIcons != null && glowIcons.Length > 0 && filledIcons != null && filledIcons.Length > 0 && glowIcons.Length != filledIcons.Length)
|
|
{
|
|
Debug.LogWarning("MemoryProgressUI: Glow Icons 개수와 Filled Icons 개수가 다릅니다.", this);
|
|
}
|
|
|
|
if (fillFlashIcons != null && fillFlashIcons.Length > 0 && filledIcons != null && filledIcons.Length > 0 && fillFlashIcons.Length != filledIcons.Length)
|
|
{
|
|
Debug.LogWarning("MemoryProgressUI: Fill Flash Icons 개수와 Filled Icons 개수가 다릅니다.", this);
|
|
}
|
|
}
|
|
#endif
|
|
}
|