using System.Collections; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public enum InventorySlotClickMode { None, ShowDetail, RequestUse } /// /// 인벤토리 슬롯 하나를 담당하는 UI 스크립트입니다. /// VR 포인터 Hover/Select, NEW 배지, 0개 흐림 표시, 중요 아이템 강조, 상세보기/사용 클릭을 지원합니다. /// [DisallowMultipleComponent] public class InventorySlotUI : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, ISelectHandler, IDeselectHandler, ISubmitHandler { [Header("Item")] [SerializeField] private InventoryItemType itemType; [Header("UI References")] [SerializeField] private Image slotBackground; [SerializeField] private Image itemIcon; [SerializeField] private TMP_Text countText; [SerializeField] private GameObject newBadge; [SerializeField] private CanvasGroup canvasGroup; [SerializeField] private GameObject importantGlowObject; [SerializeField] private GameObject filteredOutOverlay; [Header("Tooltip / Parent UI")] [SerializeField] private InventoryTooltipUI tooltipUI; [SerializeField] private InventoryUI inventoryUI; [Header("Click")] [SerializeField] private InventorySlotClickMode clickMode = InventorySlotClickMode.ShowDetail; [Header("Settings")] [Tooltip("VR에서는 false 권장. 슬롯이 사라지면 Ray 조작 위치가 바뀌어 불편할 수 있습니다.")] [SerializeField] private bool hideWhenZero = false; [SerializeField] private bool dimWhenZero = true; [SerializeField] private float zeroAlpha = 0.35f; [SerializeField] private float ownedAlpha = 1f; [SerializeField] private float filteredAlpha = 0.15f; [SerializeField] private bool showCountWhenZero = true; [SerializeField] private bool showMaxCount = false; [SerializeField] private float newBadgeShowTime = 1.2f; [Header("Hover Effect")] [SerializeField] private bool useHoverScale = true; [SerializeField] private float hoverScale = 1.08f; [SerializeField] private bool bringToFrontOnHover = true; private int currentCount; private int currentMaxCount; private bool filteredOut; private Coroutine newBadgeRoutine; private Vector3 originalScale; private InventoryItemDefinition definition; public InventoryItemType ItemType => itemType; public int CurrentCount => currentCount; public InventoryItemCategory Category => definition != null ? definition.category : InventoryItemCategory.Other; private void Awake() { originalScale = transform.localScale; if (canvasGroup == null) canvasGroup = GetComponent(); if (slotBackground == null) slotBackground = GetComponent(); if (inventoryUI == null) inventoryUI = GetComponentInParent(); if (tooltipUI == null) tooltipUI = GetComponentInParent(); if (newBadge != null) newBadge.SetActive(false); UpdateVisualState(); } private void OnDisable() { HideNewBadge(); HideTooltip(); ResetHoverVisual(); } public void SetInventoryUI(InventoryUI ui) { inventoryUI = ui; } public void SetItemType(InventoryItemType newItemType) { itemType = newItemType; } public void SetDefinition(InventoryItemDefinition newDefinition) { definition = newDefinition; if (definition == null) { UpdateVisualState(); return; } if (itemIcon != null) { itemIcon.sprite = definition.icon; itemIcon.enabled = definition.icon != null; } if (slotBackground != null && definition.slotBackground != null) { slotBackground.sprite = definition.slotBackground; slotBackground.enabled = true; } if (definition.overrideSlotDisplaySettings) { hideWhenZero = definition.hideWhenZero; dimWhenZero = definition.dimWhenZero; zeroAlpha = definition.zeroAlpha; ownedAlpha = definition.ownedAlpha; } if (importantGlowObject != null) importantGlowObject.SetActive(definition.importantItem); UpdateVisualState(); } public void SetCount(int count, bool showNewBadge) { SetCount(count, showNewBadge, currentMaxCount); } public void SetCount(int count, bool showNewBadge, int maxCount) { int previousCount = currentCount; currentCount = Mathf.Max(0, count); currentMaxCount = Mathf.Max(0, maxCount); UpdateVisualState(); if (showNewBadge && currentCount > previousCount) ShowNewBadge(); } public void SetIcon(Sprite sprite) { if (itemIcon == null) return; itemIcon.sprite = sprite; itemIcon.enabled = sprite != null; } public void SetSlotBackground(Sprite sprite) { if (slotBackground == null) return; slotBackground.sprite = sprite; slotBackground.enabled = sprite != null; } public void SetFilteredOut(bool value) { filteredOut = value; if (filteredOutOverlay != null) filteredOutOverlay.SetActive(value); UpdateVisualState(); } public void SetHoverSettings(bool useScale, float scale, bool bringToFront) { useHoverScale = useScale; hoverScale = Mathf.Max(1f, scale); bringToFrontOnHover = bringToFront; } public void ShowNewBadge() { if (newBadge == null) return; if (newBadgeRoutine != null) StopCoroutine(newBadgeRoutine); newBadgeRoutine = StartCoroutine(NewBadgeRoutine()); } public void HideNewBadge() { if (newBadgeRoutine != null) { StopCoroutine(newBadgeRoutine); newBadgeRoutine = null; } if (newBadge != null) newBadge.SetActive(false); } private IEnumerator NewBadgeRoutine() { newBadge.SetActive(true); yield return new WaitForSecondsRealtime(newBadgeShowTime); newBadge.SetActive(false); newBadgeRoutine = null; } private void UpdateVisualState() { bool hasItem = currentCount > 0; if (countText != null) { if (!showCountWhenZero && currentCount <= 0) countText.text = string.Empty; else if (showMaxCount && currentMaxCount > 0) countText.text = $"x{currentCount}/{currentMaxCount}"; else countText.text = $"x{currentCount}"; } if (hideWhenZero) gameObject.SetActive(hasItem); if (canvasGroup != null && dimWhenZero) { float targetAlpha = hasItem ? ownedAlpha : zeroAlpha; if (filteredOut) targetAlpha = Mathf.Min(targetAlpha, filteredAlpha); canvasGroup.alpha = targetAlpha; canvasGroup.interactable = !filteredOut; canvasGroup.blocksRaycasts = !filteredOut; } } public void OnPointerEnter(PointerEventData eventData) { ApplyHoverVisual(); ShowTooltip(); } public void OnPointerExit(PointerEventData eventData) { ResetHoverVisual(); HideTooltip(); } public void OnPointerClick(PointerEventData eventData) { ActivateSlot(); } public void OnSelect(BaseEventData eventData) { ApplyHoverVisual(); ShowTooltip(); } public void OnDeselect(BaseEventData eventData) { ResetHoverVisual(); HideTooltip(); } public void OnSubmit(BaseEventData eventData) { ActivateSlot(); } private void ActivateSlot() { if (filteredOut) return; if (inventoryUI == null) inventoryUI = GetComponentInParent(); if (inventoryUI == null) return; switch (clickMode) { case InventorySlotClickMode.ShowDetail: inventoryUI.ShowItemDetail(itemType); break; case InventorySlotClickMode.RequestUse: inventoryUI.RequestUseItem(itemType, 1); break; } } private void ApplyHoverVisual() { if (bringToFrontOnHover) transform.SetAsLastSibling(); if (useHoverScale) transform.localScale = originalScale * hoverScale; } private void ResetHoverVisual() { if (useHoverScale) transform.localScale = originalScale; } public void ShowTooltip() { if (tooltipUI == null) tooltipUI = GetComponentInParent(); if (tooltipUI != null) tooltipUI.ShowTooltip(itemType, currentCount, currentMaxCount); } public void HideTooltip() { if (tooltipUI != null) tooltipUI.HideTooltip(); } }