2026-03-26 인벤토리
This commit is contained in:
8
Assets/02_Scripts/UI/Inventory.meta
Normal file
8
Assets/02_Scripts/UI/Inventory.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92a7e920733b3e840935254a21a578d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Assets/02_Scripts/UI/Inventory/InventoryItemControl.cs
Normal file
84
Assets/02_Scripts/UI/Inventory/InventoryItemControl.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class InventoryItemControl : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[HideInInspector] public InventorySlot ParentSlot;
|
||||
|
||||
private Transform _dragTransform;
|
||||
private CanvasGroup _canvasGroup;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_dragTransform = GameManager.Instance.InGameUI.DragCanvas;
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
// 시작할 때 부모 슬롯을 아예 스크립트 통째로 저장!
|
||||
ParentSlot = GetComponentInParent<InventorySlot>();
|
||||
|
||||
if (ParentSlot == null || ParentSlot.currentItem == null)
|
||||
{
|
||||
// eventData.pointerDrag를 null로 만들면 이후 OnDrag, OnEndDrag도 호출안됨
|
||||
eventData.pointerDrag = null;
|
||||
return;
|
||||
}
|
||||
|
||||
transform.SetParent(_dragTransform);
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
transform.position = eventData.position; // 마우스 위치 추적
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
|
||||
// 마우스 아래에 슬롯이 없어서 제자리로 복귀해야 하는 상황인지 판별
|
||||
if (transform.parent == _dragTransform)
|
||||
{
|
||||
// eventData.pointerEnter는 드래그가 끝난 지점에 마우스가 가리키고 있는 오브젝트 (이전까지 _canvasGroup.blocksRaycasts = false; 였으므로 자신 제외)
|
||||
// 가르키는 오브젝트가 없거나, UI 레이어가 아니라면 버린것으로 간주
|
||||
bool isDroppedOutsideUI = eventData.pointerEnter == null || eventData.pointerEnter.layer != LayerMask.NameToLayer("UI");
|
||||
|
||||
if (isDroppedOutsideUI)
|
||||
{
|
||||
// 먼저 원래 부모 슬롯으로 복귀시킴 (드래그 전용 캔버스에 남는것을 방지)
|
||||
transform.SetParent(ParentSlot.transform);
|
||||
transform.localPosition = Vector3.zero;
|
||||
|
||||
GameManager.Instance.Inventory.DropItemFromSlot(ParentSlot.SlotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// UI 위이긴 한데 (예: 인벤토리 빈 여백, 다른 창) 슬롯이 아니라서 드롭 처리가 안 된 경우 -> 제자리 복귀
|
||||
transform.SetParent(ParentSlot.transform);
|
||||
transform.localPosition = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
//드래그를 취소했을때 툴팁이 다시 나와야됨
|
||||
GameObject overObj = eventData.pointerCurrentRaycast.gameObject;
|
||||
if (overObj != null)
|
||||
{
|
||||
// 만약 마우스 아래에 슬롯이 있다면 그 슬롯의 아이템정보로 툴팁 띄우기
|
||||
InventorySlot slot = overObj.GetComponentInParent<InventorySlot>();
|
||||
if (slot != null)
|
||||
{
|
||||
if (slot.currentItem != null && slot.currentItem.Data != null)
|
||||
{
|
||||
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(slot.currentItem, slot.GetComponent<RectTransform>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f41f1b66f585124db9f2823ebb15524
|
||||
179
Assets/02_Scripts/UI/Inventory/InventorySlot.cs
Normal file
179
Assets/02_Scripts/UI/Inventory/InventorySlot.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InventorySlot : MonoBehaviour, IDropHandler, IPointerEnterHandler, IPointerExitHandler, IPointerMoveHandler
|
||||
{
|
||||
public int SlotIndex; // 슬롯의 고유 번호
|
||||
private RectTransform _rectTransform; //슬롯박스 사각형
|
||||
|
||||
[Header("Slot References")]
|
||||
[SerializeField] private Image _iconImage;
|
||||
[SerializeField] private TextMeshProUGUI _stackText;
|
||||
[SerializeField] private Image _rarityImage; // 등급 배경 등
|
||||
[SerializeField] private GameObject _highlightBox;
|
||||
|
||||
[Header("Slot Settings")]
|
||||
[SerializeField] private Sprite _rarity_None_Sprite;
|
||||
[SerializeField] private Sprite _rarity_1_Sprite;
|
||||
[SerializeField] private Sprite _rarity_2_Sprite;
|
||||
[SerializeField] private Sprite _rarity_3_Sprite;
|
||||
[SerializeField] private Sprite _rarity_4_Sprite;
|
||||
|
||||
[Header("Current ItemInstance")]
|
||||
public ItemInstance currentItem; // 이 슬롯에 담긴 아이템 정보
|
||||
|
||||
//private Image SlotBg; //기본 슬롯 배경
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
//SlotBg = GetComponent<Image>();
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
ClearSlot();
|
||||
}
|
||||
|
||||
// 아이템 데이터 설정 및 UI 갱신
|
||||
public void SetItem(ItemInstance newItem)
|
||||
{
|
||||
currentItem = newItem;
|
||||
|
||||
UpdateSlotUI();
|
||||
}
|
||||
|
||||
public void OnDrop(PointerEventData eventData)
|
||||
{
|
||||
GameObject dropped = eventData.pointerDrag;
|
||||
if (dropped != null)
|
||||
{
|
||||
InventoryItemControl dragItem = dropped.GetComponent<InventoryItemControl>();
|
||||
if (dragItem != null)
|
||||
{
|
||||
InventorySlot originalSlot = dragItem.ParentSlot;
|
||||
|
||||
if (originalSlot != null && originalSlot != this)
|
||||
{
|
||||
// 스택 가능한 아이템이고 개수가 1개보다 많을 때만 분할 창 띄우기
|
||||
if (originalSlot.currentItem.Data.IsStackable && originalSlot.currentItem.CurrentStack > 1)
|
||||
{
|
||||
// 팝업 오픈 (Action으로 분할 로직 전달)
|
||||
GameManager.Instance.InGameUI.GetSplitWindowUI().Open(originalSlot.currentItem.CurrentStack, (splitCount) =>
|
||||
{
|
||||
GameManager.Instance.Inventory.ExecuteSplit(originalSlot.SlotIndex, this.SlotIndex, splitCount);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"{originalSlot.SlotIndex}번 슬롯에서 {this.SlotIndex}번 슬롯으로 이동 시도");
|
||||
GameManager.Instance.Inventory.MoveItem(originalSlot.SlotIndex, this.SlotIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (currentItem != null && currentItem.Data != null)
|
||||
{
|
||||
// 드래그 중에는 툴팁 안띄움
|
||||
if (eventData.dragging)
|
||||
{
|
||||
_highlightBox.gameObject.SetActive(false);
|
||||
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
|
||||
return;
|
||||
}
|
||||
|
||||
_highlightBox.gameObject.SetActive(true);
|
||||
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(currentItem, GetComponent<RectTransform>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnPointerMove(PointerEventData eventData)
|
||||
{
|
||||
if (currentItem != null && currentItem.Data != null)
|
||||
{
|
||||
// 드래그 중이거나, 현재 마우스 위치를 로컬 좌표로 변환해서 사각형 안에 있지 않으면
|
||||
if (eventData.dragging || !RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, eventData.position, eventData.pressEventCamera))
|
||||
{
|
||||
_highlightBox.gameObject.SetActive(false);
|
||||
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
|
||||
return;
|
||||
}
|
||||
|
||||
_highlightBox.gameObject.SetActive(true);
|
||||
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(currentItem, GetComponent<RectTransform>());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
_highlightBox.gameObject.SetActive(false);
|
||||
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
|
||||
}
|
||||
|
||||
public void ClearSlot()
|
||||
{
|
||||
_iconImage.sprite = null;
|
||||
_iconImage.enabled = false;
|
||||
|
||||
_stackText.text = "";
|
||||
_stackText.enabled = false;
|
||||
|
||||
_rarityImage.sprite = _rarity_None_Sprite;
|
||||
//SlotBg.sprite = RarityImage.sprite;
|
||||
}
|
||||
|
||||
public void UpdateSlotUI()
|
||||
{
|
||||
if (currentItem == null)
|
||||
{
|
||||
ClearSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentItem.Data.Icon != null)
|
||||
{
|
||||
_iconImage.sprite = currentItem.Data.Icon;
|
||||
_iconImage.enabled = true;
|
||||
}
|
||||
|
||||
if (currentItem.Data.IsStackable)
|
||||
_stackText.text = currentItem.CurrentStack.ToString();
|
||||
else
|
||||
_stackText.text = "";
|
||||
|
||||
_stackText.enabled = true;
|
||||
|
||||
switch (currentItem.Data.Rarity)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
_rarityImage.sprite = _rarity_1_Sprite;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
_rarityImage.sprite = _rarity_2_Sprite;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
_rarityImage.sprite = _rarity_3_Sprite;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
_rarityImage.sprite = _rarity_4_Sprite;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
_rarityImage.sprite = _rarity_None_Sprite;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
//SlotBg.sprite = _rarityImage.sprite;
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/Inventory/InventorySlot.cs.meta
Normal file
2
Assets/02_Scripts/UI/Inventory/InventorySlot.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6168315e6b8be1443b23324f00913e8a
|
||||
74
Assets/02_Scripts/UI/Inventory/TooltipUI.cs
Normal file
74
Assets/02_Scripts/UI/Inventory/TooltipUI.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class TooltipUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private GameObject _tooltipWindow;
|
||||
[SerializeField] private TextMeshProUGUI _titleText;
|
||||
[SerializeField] private TextMeshProUGUI _descriptionText;
|
||||
[SerializeField] private TextMeshProUGUI _infoText; // 강화 수치나 타입 등
|
||||
[SerializeField] private Image _icon;
|
||||
|
||||
private RectTransform rectTransform;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rectTransform = _tooltipWindow.GetComponent<RectTransform>();
|
||||
HideTooltip();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void ShowTooltip(ItemInstance item, RectTransform slotRect)
|
||||
{
|
||||
_icon.sprite = item.Data.Icon;
|
||||
_titleText.text = item.Data.ItemName;
|
||||
_descriptionText.text = item.Data.Description;
|
||||
|
||||
//무기일 경우
|
||||
if (item.Data.ItemType == ItemType.EQUIPMENT)
|
||||
{
|
||||
_infoText.text =
|
||||
$"공격력 : 00\n방어력 : 00";
|
||||
}
|
||||
else if (item.Data.ItemType == ItemType.CONSUMABLE)
|
||||
{
|
||||
_infoText.text =
|
||||
$"회복량 : 00";
|
||||
}
|
||||
|
||||
|
||||
_tooltipWindow.SetActive(true);
|
||||
//UpdateToolTip(item); // 텍스트 갱신
|
||||
|
||||
// 레이아웃 강제 갱신 (텍스트 길이에 따라 변한 툴팁 크기를 즉시 반영)
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
|
||||
|
||||
// 기본 위치 설정 (슬롯의 오른쪽)
|
||||
Vector3[] slotCorners = new Vector3[4];
|
||||
slotRect.GetWorldCorners(slotCorners);
|
||||
|
||||
Vector3 targetPos = slotCorners[2];
|
||||
rectTransform.pivot = new Vector2(0f, 1f); // 툴팁 피벗을 좌상단으로
|
||||
|
||||
// 화면 잘림 검사
|
||||
float tooltipWidth = rectTransform.rect.width;
|
||||
// 툴팁의 우측 끝 좌표가 화면 너비보다 크면 왼쪽으로 뒤집기
|
||||
if (targetPos.x + tooltipWidth > Screen.width)
|
||||
{
|
||||
targetPos = slotCorners[1]; // 슬롯의 좌측 상단 모서리
|
||||
rectTransform.pivot = new Vector2(1f, 1f); // 툴팁 피벗을 우상단으로 변경
|
||||
}
|
||||
|
||||
rectTransform.position = targetPos;
|
||||
}
|
||||
|
||||
public void HideTooltip()
|
||||
{
|
||||
_tooltipWindow.SetActive(false);
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/Inventory/TooltipUI.cs.meta
Normal file
2
Assets/02_Scripts/UI/Inventory/TooltipUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ce182b987390a44192add2739f07f27
|
||||
37
Assets/02_Scripts/UI/_Shared/SplitWindowUI.cs
Normal file
37
Assets/02_Scripts/UI/_Shared/SplitWindowUI.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using System;
|
||||
|
||||
public class SplitWindowUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_InputField _inputField;
|
||||
private int _maxAmount;
|
||||
private Action<int> _onConfirm;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public void Open(int max, Action<int> onConfirm)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
_maxAmount = max;
|
||||
_onConfirm = onConfirm;
|
||||
_inputField.text = "1"; // 기본값
|
||||
_inputField.ActivateInputField();
|
||||
}
|
||||
|
||||
public void OnClickConfirm()
|
||||
{
|
||||
if (int.TryParse(_inputField.text, out int amount))
|
||||
{
|
||||
// 1보다 작으면 1로, 최대치보다 크면 최대치로 보정
|
||||
amount = Mathf.Clamp(amount, 1, _maxAmount);
|
||||
_onConfirm?.Invoke(amount);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Close() => gameObject.SetActive(false);
|
||||
}
|
||||
2
Assets/02_Scripts/UI/_Shared/SplitWindowUI.cs.meta
Normal file
2
Assets/02_Scripts/UI/_Shared/SplitWindowUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88c61d650f9274141b4c26634a0d55cf
|
||||
Reference in New Issue
Block a user