| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | 
| 12 | 13 | 14 | 15 | 16 | 17 | 18 | 
| 19 | 20 | 21 | 22 | 23 | 24 | 25 | 
| 26 | 27 | 28 | 29 | 30 | 31 | 
- 알고리즘수업-너비우선탐색2
- 크래프톤 정글 4기
- 추상클래스와인터페이스
- 크래프톤정글4기
- 핀토스
- 파이썬
- 이벤트 함수 실행 순서
- 백준
- 유니티
- C
- kraftonjungle
- anonymous page
- 알고리즘
- 전쟁-전투
- 연결리스트
- 4기
- 크래프톤정글
- Unity
- BFS
- project3
- 다익스트라
- TiL
- KRAFTON JUNGLE
- 오블완
- 네트워크
- 티스토리챌린지
- pintos
- c#
- User Stack
- 크래프톤 정글
- Today
- Total
말감로그
[Unity] Valley - 인벤토리 시스템 본문
인벤토리 시스템
인벤토리 클래스를 정의해서 인벤토리 시스템을 관리할 것이다.
//Inventory.cs
[System.Serializable]
public class Inventory
{
    public List<Slot> slots = new List<Slot>();
    [System.Serializable]
    public class Slot
    {
        public CollectableType type;
        public Sprite icon;
        public int count;
        public int maxAllowed;
        public Slot()
        {
            type = CollectableType.NONE;
            count = 0;
            maxAllowed = 99;
        }
    }
    public Inventory(int numSlots)
    {
        for (int i = 0; i < numSlots; i++)
        {
            Slot slot = new Slot();
            slots.Add(slot);
        }
    }
List<Slot> slots
Slot 객체들의 리스트로, 인벤토리에 들어갈 각 슬롯을 나타낸다.
Slot 클래스
인벤토리의 각 슬롯을 정의하는 내부 클래스로, 각 슬롯이 어떤 아이템을 담고 있는지, 아이템의 갯수, 아이콘 등을 저장한다.
Slot() 생성자
Slot 객체가 생성될 때 기본적으로 저 값으로 초기화된다.
Inventory(int numSlots) 생성자
인벤토리의 슬롯 수를 받아서 Slot 객체들을 해당 개수만큼 생성하여 slots 리스트에 추가한다.
인벤토리를 초기화할 때 이 생성자를 사용하여 빈 슬롯들이 채워진 인벤토리가 생성된다.
// Player.cs
void Awake()
{
    inventory = new Inventory(24);
}
실제 Player.cs에서 24개의 슬롯들이 채워진 인벤토리를 생성한다.
기본적인 설정을 마쳤으니 이제 아이템을 추가하고 삭제하는 일을 해야 한다.
슬롯에 아이템을 추가하고 삭제하는 역할을 하는 메서드들이다.
public class Slot
{
        :
    public bool CanAddItem()
    {
        if (count < maxAllowed)
        {
            return true;
        }
        return false;
    }
    public void AddItem(Collectable item)
    {
        this.type = item.type;
        this.icon = item.icon;
        count++;
    }
    public void RemoveItem()
    {
        if(count > 0)
        {
            count--;
            if(count == 0)
            {
                icon = null;
                type = CollectableType.NONE;
            }
        }
    }
}
인벤토리에 아이템을 추가하고 삭제하는 메서드들
		:
    public void Add(Collectable item)
    {
        // 슬롯의 타입이 추가하려는 아이템의 타입과 같고 maxAllowed보다 적은 count이면
        foreach (Slot slot in slots)
        {
            if (slot.type == item.type && slot.CanAddItem())
            {
                slot.AddItem(item);
                return;
            }
        }
		
        // 같은 타입의 슬롯이 없거나 슬롯이 꽉차서 아이템으 추가할 수 없는 경우
        foreach (Slot slot in slots)
        {
            // 빈 슬롯이면
            if (slot.type == CollectableType.NONE)
            {
                slot.AddItem(item);
                return;
            }
        }
    }
    public void Remove(int index)
    {
        slots[index].RemoveItem();
    }
    public void Remove(int index, int numToRemove)
    {
        if(slots[index].count >= numToRemove)
        {
            for(int i = 0; i < numToRemove; i++)
            {
                Remove(index);
            }
        }
    }
슬롯에도 해당 아이템의 아이콘과 개수를 나타내기 위한 스크립트를 작성해서 붙여준다.

// Slot_UI.cs
using UnityEngine;
using UnityEngine.UI;
public class Slot_UI : MonoBehaviour
{
    public int slotID;
    public Image itemIcon;
    public Text quantityText;
    public void SetItem(Inventory.Slot slot)
    {
        if (slot != null)
        {
            itemIcon.sprite = slot.icon;
            itemIcon.color = new Color(1, 1, 1, 1);
            quantityText.text = slot.count.ToString();
        }
    }
    public void EmptyItem()
    {
        itemIcon.sprite = null;
        itemIcon.color = new Color(1, 1, 1, 0);
        quantityText.text = "";
    }
}
수집 가능한 아이템을 나타내는 스크립트를 작성해서 오브젝트의 타입을 정의하고, 플레이어에 닿으면 해당 오브젝트를 인벤토리에 추가하도록 한다.

// Collectable.cs
using UnityEngine;
public enum CollectableType { NONE, RICE_SEED }
public class Collectable : MonoBehaviour
{
    public CollectableType type;
    public Sprite icon;
    public Rigidbody2D rigid;
    void Start()
    {
        rigid = GetComponent<Rigidbody2D>();
    }
    void OnTriggerEnter2D(Collider2D other)
    {
        Player player = other.GetComponent<Player>();
        if (player)
        {
            player.inventory.Add(this);
            Destroy(this.gameObject);
        }
    }
}
인벤토리를 Tab 키로 껐다 키고 킬때마다 갱신을 시켜줘야 하기 때문에 Refresh() 함수를 통해서 인벤토리의 슬롯을 갱신해준다.
public class Inventory_UI : MonoBehaviour
{
    public Player player;
    public GameObject inventory;
    public List<Slot_UI> slots = new List<Slot_UI>();
    void Awake()
    {
        canvas = FindObjectOfType<Canvas>();
        inventory.SetActive(false);
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            ToggleInventory();
        }
    }
    public void ToggleInventory()
    {
        if (!inventory.activeSelf)
        {
            inventory.SetActive(true);
            Refresh();
        }
        else
        {
            inventory.SetActive(false);
        }
    }
    // 인벤토리 갱신
    void Refresh()
    {
        if (slots.Count == player.inventory.slots.Count)
        {
            for (int i = 0; i < slots.Count; i++)
            {
                if (player.inventory.slots[i].type != CollectableType.NONE)
                {
                    slots[i].SetItem(player.inventory.slots[i]);
                }
                else
                {
                    slots[i].EmptyItem();
                }
            }
        }
    }
}

여기까지 하면 플레이어가 아이템을 먹으면 인벤토리에 갯수와 함께 잘 들어가 있는 모습을 볼 수 있다.
이제 드래그앤 드랍으로 슬롯을 끌어다가 밖에 놓으면 플레이어 주변으로 아이템이 떨어져있는 모습을 구현할 것이다.
그러기 위해선 Slot 프리팹에 Event Trigger 컴포넌트를 추가해줘야 한다.
Event Trigger는 다양한 UI 이벤트를 감지하고 이에 대응할 수 있게 도와준다.

1. SlotBeginDrag(Slot_UI slot)
슬롯 드래그 시작시 호출되며, 드래그할 슬롯과 아이콘을 준비한다.
public void SlotBeginDrag(Slot_UI slot)
{
    draggedSlot = slot;	 //드래그할 슬롯 저장
    draggedIcon = Instantiate(draggedSlot.itemIcon);   // 슬롯의 아이콘 복사
    draggedIcon.transform.SetParent(canvas.transform);  // 캔버스의 자식으로 설정
    draggedIcon.raycastTarget = false;  // 아이콘이 레이캐스트를 받지 않도록 설정
    draggedIcon.rectTransform.sizeDelta = new Vector2(100, 100);  // 아이콘 크기
    MoveToMousePosition(draggedIcon.gameObject);  // 아이콘을 마우스 위치로 이동
    Debug.Log("Start Drag:" + draggedSlot.name);
}
2. SlotDrag()
드래그 중 아이콘의 위치를 업데이트한다.
public void SlotDrag()
{
    MoveToMousePosition(draggedIcon.gameObject); // 드래그 중 아이콘 위치 업데이트
    Debug.Log("Dragging:" + draggedSlot.name); // 드래그 중인 슬롯 로그 출력
}
3. SlotEndDrag()
드래그가 끝났을 때 호출되며, 드래그된 아이콘을 정리한다.
public void SlotEndDrag()
{
    Destroy(draggedIcon.gameObject); // 드래그가 끝나면 아이콘 삭제
    draggedIcon = null; // 드래그된 아이콘 변수 초기화
}
4.SlotDrop(Slot_UI Slot)
아이콘이 다른 슬롯에 드롭될 때 호출된다.
아직 이건 구현 X
public void SlotDrop(Slot_UI slot)
{
    Debug.Log("Dropped :" + draggedSlot.name + " on " + slot.name); // 드롭된 슬롯 로그 출력
}
5. MoveToMousePosition(GameObject toMove)
주어진 게임 오브젝트를 마우스 위치로 이동한다.
public void MoveToMousePosition(GameObject toMove)
{
    if (canvas != null)
    {
        Vector2 position;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, null, out position); // 마우스 위치를 로컬 좌표로 변환
        toMove.transform.position = canvas.transform.TransformPoint(position); // 변환된 위치로 이동
    }
}
RectTransformUtility.ScreenPointToLocalPointInRectangle
스크린상의 마우스 위치를 캔버스의 로컬 좌표로 변환한다. 이 방법은 UI 요소를 정확한 위치에 배치하는 데 필요하다.
TransformPoint
변환된 로컬 위치를 다시 세계 좌표로 변환
캔버스에서 아이템 제거 패널 오브젝트를 만들고 캔버스에 가득차게 설정한다.

UI 배경이 보이게끔 알파값은 0으로 설정하고,
Event Trigger - Drop - Inventory_UI.Remove() 함수를 추가한다.

RemoveItem_Panel에 드랍한 아이템을 플레이어 주변으로 떨어트리고 인벤토리에서 제거시킨다.
public void Remove()
{
    Collectable itemToDrop = GameManager.instance.itemManager.GetItemByType(player.inventory.slots[draggedSlot.slotID].type);
    if (itemToDrop != null)
    {
        if (dragSingle)
        {
            player.DropItem(itemToDrop);
            player.inventory.Remove(draggedSlot.slotID);
        }
        else
        {
            player.DropItem(itemToDrop, player.inventory.slots[draggedSlot.slotID].count);
            player.inventory.Remove(draggedSlot.slotID, player.inventory.slots[draggedSlot.slotID].count);
        }
        Refresh();
    }
    draggedSlot = null;
}
플레이어 주변에서 점차 멀어지게끔 떨어지는 효과를 주기 위한
// Player.cs
public void DropItem(Collectable item)
{
    Vector3 spawnLocation = transform.position;
    Vector3 spawnOffset = Random.insideUnitCircle * 2f;
    Collectable droppedItem = Instantiate(item, spawnLocation + spawnOffset, Quaternion.identity);
    droppedItem.rigid.AddForce(spawnOffset * 0.3f, ForceMode2D.Impulse);
}
'TIL' 카테고리의 다른 글
| [Unity] Valley - DayCycle & 식물 성장 시스템 (1) | 2024.10.24 | 
|---|---|
| [Unity] Valley - 아이템 리팩토링 / 인벤토리 & Toolbar 간 슬롯 이동 (1) | 2024.10.18 | 
| [Unity] Valley - 플레이어 이동 & 애니메이션(Blend Tree) (1) | 2024.10.14 | 
| 2024.10.04 Unity - 수류탄 효과, 몬스터 AI (NavMeshAgent) (0) | 2024.10.04 | 
| 24.10.02 Unity - 물리 충돌 수정, Raycast (0) | 2024.10.02 | 
 
                   
                   
                  