[Unity3D] 인벤토리 [Part 2] - Item Add

※ 주의 

이 글은 아마추어가 개인적으로 생각하여 작성하는 것으로, 이곳에 나오는 내용을 맹신하지 않는것을 당부드립니다.



Menu

1. 인벤토리 껍데기 만들기.

- 인벤토리의 Pivot 설정.

- 슬롯의 Pivot 설정.

- 슬롯 사이즈 설정.

- 슬롯 간 거리 설정.

- 슬롯의 가로, 세로 개수 설정.

- 인벤토리의 가로 사이즈, 세로 사이즈 설정.

- 슬롯 생성 및 부모설정.

- 모든 슬롯을 관리해줄 리스트를 생성.


2. 아이템 획득 시 검사조건 만들기 및 슬롯 기능 만들기.

- 아이템을 먹었을 때, 인벤토리 내의 슬롯을 모두 검사한다.

  *검사조건 : -> 슬롯 내에 같은 아이템이 존재하는지?

-> 슬롯내에 같은 아이템이 존재할 경우 겹칠수 있는지?

-> 슬롯내에 같은 아이템이 존재하지만, 겹칠수 없는경우 빈 슬롯이 존재 하는지?

- 슬롯을 스택으로 만들어 아이템 관리하기

  ->아이템이 슬롯에 들어갔을때 이미지 변경.  

  ->아이템을 겹칠경우 텍스트 갱신.

  ->아이템을 사용할 경우 텍스트 갱신.

  ->아이템을 모두 사용했을 때 이미지 변경.


3. 아이템 만들기 및 획득

 - 아이템 타입 설정.

 - 아이템의 이미지 설정.

 - 아이템 겹칠수 있는 최대치 설정.

 - 아이템과의 충돌로 아이템 획득하기.


4. 인벤토리내에 아이템 드래그 앤 드랍으로 아이템 옮기기 및 자리 바꾸기.

 - 처음 마우스 버튼을 누를 때

 - 누르고 이동할 때

 - 드래그가 끝났을 때

 - 누른 버튼을 땠을 때

의 4가지 상태로 나눠 드래그 앤 드랍 구현.


5. XML문서를 이용한 인벤토리 아이템 저장.

- Save

- Load




미리보기



2. 아이템 획득 시 검사조건 만들기 및 슬롯 기능 만들기.

를 진행하기 전에 3번부터 합시다.



3. 아이템 만들기 및 획득.


만약 아이템이 플레이어와 충돌한다면

아이템은 플레이어 인벤토리 스크립트에 존재하는 AddItem()함수를 호출한다.


※ 아이템쪽에서 플레이어와의 충돌을 감지한다.

※ 왜 하필 인벤토리 스크립트에서 AddItem()을 호출하는가?

슬롯에 직접 아이템을 넣으면 안되는가?


아이템을 먹었다고 해서 인벤토리에 아이템이 무조건 들어가는것이 아니다.

인벤토리가 꽉 차 있는경우 아이템 획득에 실패할수 있다.

또한 인벤토리가 꽉 차 있지만 인벤토리 내에 똑같은 아이템이 존재하여

먹을수 있는 경우도 있다.

이러한 경우에 수를 다 시도해 보기 위해 모든 슬롯을 관리하는 인벤토리 스크립트에

AddItem()을 호출해줄 필요가 있다.



'Item' Script

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
32
33
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Item : MonoBehaviour
{
    public enum TYPE { HP, MP }
 
    public TYPE type;           // 아이템의 타입.
    public Sprite DefaultImg;   // 기본 이미지.
    public int MaxCount;        // 겹칠수 있는 최대 숫자.
 
 
    void AddItem()
    {
        // 싱글톤을 이용해서 인벤토리 스크립트를 가져온다.
        Inventory iv = ObjManager.Call().IV;
 
        // 아이템 획득에 실패할 경우.
        if (!iv.AddItem(this))
            Debug.Log("아이템이 가득 찼습니다.");
        else // 아이템 획득에 성공할 경우.
            gameObject.SetActive(false); // 아이템을 비활성화 시켜준다.
    }
 
    // 충돌체크
    void OnTriggerEnter(Collider _col)
    {
        // 플레이어와 충돌하면.
        if (_col.gameObject.layer == 10)
            AddItem();
    }
}
cs



싱글톤이 없을 경우. 'Item' Script



아이템을 먹었을 때 엉뚱한 아이템이 인벤토리에 들어가면 안된다.

그렇기 때문에 아이템 자체에 아이템의 타입(종류)과 그 아이템에 해당하는 이미지 정보,

그리고 아이템을 몇개까지 겹칠수 있는지? 에 대한 정보를 가지고 있을 필요가 있다.


아이템에 대한 인스펙터창 정보이다.



플레이어에 대한 레이어 설정도 잊지 말도록 하자.




2. 아이템 획득 시 검사조건 만들기 및 슬롯 기능 만들기.


아이템이 인벤토리 스크립트에 존재하는 AddItem() 함수를 호출했다.

AddItem()안에서 해야 될것은 두 가지가 있다.


첫 번째는 넣을려는 아이템과 똑같은 아이템이 슬롯에 존재하는가?

존재 한다면 겹칠수 있는 최대치는 넘지 않았는가?


위의 조건에 해당되면 아이템을 슬롯에 넣는다.


두 번째는 위에 조건을 검사했지만 해당되지 않을 때 하는 검사이다.

똑같은 아이템이 없기 때문에 그냥 비어있는 슬롯에 아이템을 넣어주면 된다.


위의 모든 조건에 해당되지 않는다면 아이템을 먹지 못하기 때문에 AddItem()함수는 false를 반환하면 된다.


슬롯에 존재하는 아이템과 넣을려는 아이템이 똑같은지를 검사하기 위해서는

슬롯에 존재하는 아이템의 정보를 끌어올 필요가 있다.


그러기 위해서는 위에 조건의 작성보다 선행되어야 하는 것이 있는데

바로 슬롯 스크립트의 작성이다.



'slot' Script

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class Slot : MonoBehaviour {
 
    public Stack<Item> slot;       // 슬롯을 스택으로 만든다.
    public Text        text;       // 아이템에 개수를 표현해줄 텍스트.
    public Sprite      DefaultImg; // 슬롯에 있는 아이템을 다 사용할 경우 아무것도 없는 이미지를 넣어줄 필요가 있다.
    
    private Image      ItemImg;
    private bool       isSlot;     // 현재 슬롯이 비어있는지?
 
    public Item ItemReturn()          { return slot.Peek();   } // 슬롯에 존재하는 아이템이 뭔지 반환.
    public bool ItemMax(Item item)    { return ItemReturn().MaxCount > slot.Count; } // 겹칠수 있는 한계치를 넘으면.   
    public bool isSlots()             { return isSlot;        } // 슬롯이 현재 비어있는지? 에 대한 플래그 반환.
    public void SetSlots(bool isSlot) { this.isSlot = isSlot; }
    
    void Start()
    {
        // 스택 메모리 할당.
        slot = new Stack<Item>();
 
        // 맨 처음엔 슬롯이 비어있다.
        isSlot = false;
 
        // 인벤토리 및 슬롯의 크기가 커지가나 작아지면
        // 텍스트 폰트의 크기도 유동적으로 바뀌어야 한다.
        // 텍스트 폰트의 크기를 슬롯에 크기에 따라 변경해주는 구문이다.
        //RectTransform rect = text.gameObject.GetComponent<RectTransform>();
        float Size = text.gameObject.transform.parent.GetComponent<RectTransform>().sizeDelta.x;
        text.fontSize = (int)(Size * 0.5f);
 
        // 텍스트 컴포넌트의 RectTransform을 가져온다.
        // 텍스트 객체의 부모 객체의 x지름을 가져온다.
        // 폰트의 크기를 부모 객체의 x지름 / 2 만큼으로 지정해준다.
        ItemImg = transform.GetChild(0).GetComponent<Image>();
    }
 
    public void AddItem(Item item)
    {
        // 스택에 아이템 추가.
        slot.Push(item);
        UpdateInfo(true, item.DefaultImg);
    }
 
    // 아이템 사용.
    public void ItemUse()
    {
        // 슬롯이 비어있으면 함수를 종료.
        if (!isSlot)
            return;
 
        // 슬롯에 아이템이 1개인 경우.
        // 아이템이 1개일 때 사용하게 되면 0개가 된다.
        if (slot.Count == 1)
        {
            // 혹시 모를 오류를 방지하기 위해 slot리스트를 Clear해준다
            slot.Clear();
            // 아이템 사용으로 인해 아이템 개수를 표현하는 텍스트가 달라졌으므로 업데이트 시켜준다.
            UpdateInfo(false, DefaultImg);
            return;
        }
 
        slot.Pop();
        UpdateInfo(isSlot, ItemImg.sprite);
    }
 
    // 슬롯에 대한 각종 정보 업데이트.
    public void UpdateInfo(bool isSlot, Sprite sprite)
    {
        SetSlots(isSlot);                                          // 슬롯이 비어있다면 false 아니면 true 셋팅.
        ItemImg.sprite = sprite;                                   // 슬롯안에 들어있는 아이템의 이미지를 셋팅.
        text.text = slot.Count > 1 ? slot.Count.ToString() : "";   // 아이템이 2개 이상일때면 텍스트로 표현.
        ItemIO.SaveDate();                                         // 인벤토리에 변동사항이 생겼으므로 저장.
    }
}
cs



텍스트 정보.




슬롯 스크립트의 작성이 끝났으면 인벤토리의 AddItem()함수를 작성할 수 있게 된다.


'Inventory' Script


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Inventory : MonoBehaviour
{
 
    // 공개
    public List<GameObject> AllSlot;    // 모든 슬롯을 관리해줄 리스트.
    public RectTransform InvenRect;     // 인벤토리의 Rect
    public GameObject OriginSlot;       // 오리지널 슬롯.
 
    public float slotSize;              // 슬롯의 사이즈.
    public float slotGap;               // 슬롯간 간격.
    public float slotCountX;            // 슬롯의 가로 개수.
    public float slotCountY;            // 슬롯의 세로 개수.
 
    // 비공개.
    private float InvenWidth;           // 인벤토리 가로길이.
    private float InvenHeight;          // 인밴토리 세로길이.
    private float EmptySlot;            // 빈 슬롯의 개수.
 
    void Awake()
    {
        // 인벤토리 이미지의 가로, 세로 사이즈 셋팅.
        InvenWidth = (slotCountX * slotSize) + (slotCountX * slotGap) + slotGap;
        InvenHeight = (slotCountY * slotSize) + (slotCountY * slotGap) + slotGap;
 
        // 셋팅된 사이즈로 크기를 설정.
        InvenRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, InvenWidth); // 가로.
        InvenRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, InvenHeight);  // 세로.
 
        // 슬롯 생성하기.
        for (int y = 0; y < slotCountY; y++)
        {
            for (int x = 0; x < slotCountX; x++)
            {
                // 슬롯을 복사한다.
                GameObject slot = Instantiate(OriginSlot) as GameObject;
                // 슬롯의 RectTransform을 가져온다.
                RectTransform slotRect = slot.GetComponent<RectTransform>();
                // 슬롯의 자식인 투명이미지의 RectTransform을 가져온다.
                RectTransform item = slot.transform.GetChild(0).GetComponent<RectTransform>();
 
                slot.name = "slot_" + y + "_" + x; // 슬롯 이름 설정.
                slot.transform.parent = transform; // 슬롯의 부모를 설정. (Inventory객체가 부모임.)
 
                // 슬롯이 생성될 위치 설정하기.
                slotRect.localPosition = new Vector3((slotSize * x) + (slotGap * (x + 1)),
                                                   -((slotSize * y) + (slotGap * (y + 1))),
                                                      0);
 
                // 슬롯의 자식인 투명이미지의 사이즈 설정하기.
                slotRect.localScale = Vector3.one;
                slotRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, slotSize); // 가로
                slotRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, slotSize);   // 세로.
 
                // 슬롯의 사이즈 설정하기.
                item.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, slotSize - slotSize * 0.3f); // 가로.
                item.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, slotSize - slotSize * 0.3f);   // 세로.
 
                // 리스트에 슬롯을 추가.
                AllSlot.Add(slot);
            }
        }
 
        // 빈 슬롯 = 슬롯의 숫자.
        EmptySlot = AllSlot.Count;
    }
 
    // 아이템을 넣기위해 모든 슬롯을 검사.
    public bool AddItem(Item item)
    {
        // 슬롯에 총 개수.
        int slotCount = AllSlot.Count;
 
        // 넣기위한 아이템이 슬롯에 존재하는지 검사.
        for (int i = 0; i < slotCount; i++)
        {
            // 그 슬롯의 스크립트를 가져온다.
            Slot slot = AllSlot[i].GetComponent<Slot>();
 
            // 슬롯이 비어있으면 통과.
            if (!slot.isSlots())
                continue;
 
            // 슬롯에 존재하는 아이템의 타입과 넣을려는 아이템의 타입이 같고.
            // 슬롯에 존재하는 아이템의 겹칠수 있는 최대치가 넘지않았을 때. (true일 때)
            if (slot.ItemReturn().type == item.type && slot.ItemMax(item))
            {
                // 슬롯에 아이템을 넣는다.
                slot.AddItem(item);
                return true;
            }
        }
 
        // 빈 슬롯에 아이템을 넣기위한 검사.
        for (int i = 0; i < slotCount; i++)
        {
            Slot slot = AllSlot[i].GetComponent<Slot>();
 
            // 슬롯이 비어있지 않으면 통과
            if (slot.isSlots())
                continue;
 
            slot.AddItem(item);
            return true;
        }
 
        // 위에 조건에 해당되는 것이 없을 때 아이템을 먹지 못함.
        return false;
    }
}
cs