(출처 : https://rito15.github.io/posts/unity-study-rpg-inventory/)
위에 링크에 있는 내용을 바탕으로 제작했다.
private Inventory inventory;
[SerializeField] private ItemTooltipUI itemTooltipUI;
[SerializeField] private InventoryPopupUI popupUI;
private GraphicRaycaster gr;
private PointerEventData ped;
private List<RaycastResult> rrList;
private ItemSlotUIs pointerOverSlot;
private ItemSlotUIs beginDragSlot;
private Transform beginDragIconTransform;
private int leftClick = 0;
private int rightClick = 1;
[SerializeField] private bool showHighlight = true;
[SerializeField] private bool showRemovingPopup = true;
[SerializeField] private bool showTooltip = true;
private Vector3 beginDragIconPoint;
private Vector3 beginDragCursorPoint;
private int beginDragSlotSiblingIndex;
ped rrList는 GraphicRaycaster의 Raycast 함수를 활용하기 위해서 사용하는 매개 변수이다.
private void Awake()
{
Init();
}
private void Init()
{
TryGetComponent(out gr);
if (gr == null)
gr = gameObject.AddComponent<GraphicRaycaster>();
ped = new PointerEventData(EventSystem.current);
rrList = new List<RaycastResult>(10);
if (itemTooltipUI == null)
{
itemTooltipUI = GetComponent<ItemTooltipUI>();
}
}
null일 때를 방지해서 null인 경우 값을 넣어주기 위해서 Init 함수를 사용한다.
private void Update()
{
DragAndDropUpdate();
}
public void DragAndDropUpdate()
{
ped.position = Input.mousePosition;
OnPointerEnterAndExit();
if (showTooltip) ShowOrHideItemTooltip();
OnPointerDown();
OnPointerDrag();
OnPointerUp();
}
PointerEnterAndExit, Down, Drag, Up 등 Pointer Event를 직접 구현해서 사용한다.
private void OnPointerEnterAndExit()
{
// 이전 프레임의 슬롯
var prevSlot = pointerOverSlot;
// 현재 프레임의 슬롯
var curSlot = pointerOverSlot = RaycastAndGetFirstComponent<ItemSlotUIs>();
if (prevSlot == null)
{
//Enter
if (curSlot != null)
{
OnCurrentEnter();
}
}
else
{
//Exit
if (curSlot == null)
{
OnPrevExit();
}
//Change
else if (prevSlot != curSlot)
{
OnPrevExit();
OnCurrentEnter();
}
}
void OnCurrentEnter()
{
if (showHighlight)
curSlot.Highlight(true);
}
void OnPrevExit()
{
prevSlot.Highlight(false);
}
}
먼저 Enter And Exit 이다.
보는 것과 같이 이전 Slot과 현재 Slot을 비교하면서 Cursor가 현재 위치해 있는 SlotUI의 HighlightImage를 켜줄지 말지를 정해준다.
private void OnPointerDown()
{
if (Input.GetMouseButtonDown(leftClick))
{
beginDragSlot = RaycastAndGetFirstComponent<ItemSlotUIs>();
// 아이템을 갖고 있는 슬롯만
if (beginDragSlot != null && beginDragSlot.HasItem)
{
// 위치 기억, 참조 등록
beginDragIconTransform = beginDragSlot.IconRect.transform;
beginDragIconPoint = beginDragIconTransform.position;
beginDragCursorPoint = Input.mousePosition;
// 맨 위에 보이기
beginDragSlotSiblingIndex = beginDragSlot.transform.GetSiblingIndex();
beginDragSlot.transform.SetAsLastSibling();
// 해당 슬롯의 하이라이트 이미지를 아이콘보다 뒤에 위치시키기
beginDragSlot.SetHighlightOnTop(false);
}
else
{
beginDragSlot = null;
}
}
else if (Input.GetMouseButtonDown(rightClick))
{
ItemSlotUIs slot = RaycastAndGetFirstComponent<ItemSlotUIs>();
if (slot != null && slot.HasItem && slot.IsAccessible)
{
TryUseItem(slot.Index);
}
}
}
private void TryUseItem(int index)
{
inventory.Use(index);
}
클릭하는 경우는 Mouse 왼쪽과 오른쪽의 경우로 나누어진다.
왼쪽의 경우 보통 아이템을 옮기는 용도로 사용하기 때문에 Item이 있는 것과 아닌 것의 경우가 있다.
오른쪽의 경우 아이템을 사용하는 용도로 사용하기 때문에 Item이 있는지와, 접근 가능한 슬롯인지 확인 후 아이템을 사용한다.
private void OnPointerDrag()
{
if (beginDragSlot == null) return;
if (Input.GetMouseButton(0))
{
beginDragIconTransform.position = beginDragIconPoint + (Input.mousePosition - beginDragCursorPoint);
}
}
// 클릭을 뗄 경우
private void OnPointerUp()
{
if (Input.GetMouseButtonUp(0))
{
if (beginDragSlot != null)
{
beginDragIconTransform.position = beginDragIconPoint;
beginDragSlot.transform.SetSiblingIndex(beginDragSlotSiblingIndex);
EndDrag();
beginDragSlot = null;
beginDragIconTransform = null;
}
}
}
private void EndDrag()
{
ItemSlotUIs endDragSlot = RaycastAndGetFirstComponent<ItemSlotUIs>();
if (endDragSlot != null && endDragSlot.IsAccessible)
{
// 수량 나누기 조건
// 1) 마우스 클릭 떼는 수간 좌측 shift 키 유지
// 2) begin : 셀 수 있는 아이템 / end : 비어있는 슬롯
// 3) begin 아이템 수량 > 1
bool isSeparatable = (Input.GetKey(KeyCode.LeftShift) && inventory.IsCountableItem(beginDragSlot.Index) && !inventory.HasItem(endDragSlot.Index));
// true : 수량 나누기, false : 교환 또는 이동
bool isSeparation = false;
int currentAmount = 0;
if (isSeparatable)
{
currentAmount = inventory.GetCurrentAmount(beginDragSlot.Index);
if (currentAmount > 1)
{
isSeparation = true;
}
}
// 1. 개수 나누기
if (isSeparation)
TrySeparateAmount(beginDragSlot.Index, endDragSlot.Index, currentAmount);
// 교환 또는 이동
else
TrySwapItems(beginDragSlot, endDragSlot);
if (endDragSlot.Index < 33)
UpdateTooltipUI(endDragSlot);
return;
}
// 버리기
if (IsOverUI())
{
// 확인 팝업 띄우기
int index = beginDragSlot.Index;
string itemName = inventory.GetItemName(index);
int amount = inventory.GetCurrentAmount(index);
// 셀 수 있는 아이템의 경우, 수량 표시
if (amount > 1)
itemName += $"x{amount}";
if (showRemovingPopup)
{
popupUI.OpenConfirmationPopup(itemName, index);
}
else
TryRemoveItem(index);
}
// 슬롯이 아닌 다른 UI 위에 놓은 경우
else
{
}
}
private void TrySeparateAmount(int indexA, int indexB, int amount)
{
if (indexA == indexB) return;
string itemName = $"{inventory.GetItemName(indexA)} x{amount}";
popupUI.OpenAmountInputPopup(itemName, amount, indexA, indexB);
}
public void TryRemoveItem(int index)
{
inventory.Remove(index);
}
public void TryInventorySeparateAmount(int indexA, int indexB, int amt)
{
inventory.SeparateAmount(indexA, indexB, amt);
}
private void TrySwapItems(ItemSlotUIs from, ItemSlotUIs to)
{
if (from == to)
{
return;
}
from.SwapOrMoveIcon(to);
if (to.IsNotEqualEquipment || (!to.IsNotEqualEquipment && from.IsNotEqualEquipment && to.HasItem && from.HasItem)
|| (!to.IsNotEqualEquipment && !from.IsNotEqualEquipment))
inventory.Swap(from.Index, to.Index);
}
클릭을 때는 경우는 개수가 있는 아이템을 합치거나, 아이템의 슬롯 위치를 옮기거나, 개수가 있는 아이템을 나눌 때, 아이템을 버릴 때 사용된다.
그래서 각 조건의 맞는지 확인 후 진행한다. endDragSlot.Index < 33 으로 하드코딩 된 부분은 장비칸을 제외한 Inventory의 Slot 개수이다. Item 정보와 Inventory UI를 업데이트 할 때 장비칸의 Slot 개수도 맞춰줘야해서 우선 하드코딩으로 구현했다.
마지막에 TrySwapItems에서 if 문 안에 있는 조건들은 장비칸에 해당하는 장비들만 들어가게 하기 위한 조건들이다.
'내배켐 Unity TIL' 카테고리의 다른 글
Unity 68일차 TIL - Unity (Blend Tree) (0) | 2024.07.24 |
---|---|
Unity 67일차 TIL - Unity (Animator.Play()) (0) | 2024.07.24 |
Unity 63일차 TIL - Unity (2D ItemInventory3_ItemTooltipUI) (0) | 2024.07.10 |
Unity 60일차 TIL - Unity (Graphic RayCaster) (0) | 2024.07.05 |
Unity 59일차 TIL - Unity (2D ItemInventory2_InventoryUI) (0) | 2024.07.04 |