[Unity] 인벤토리 시스템 2

이경현·2024년 1월 22일

유니티

목록 보기
2/4

1. Items scriptable objects

  1. 새로운 C# 스크립트 Item.cs 만들기

Item.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

[CreateAssetMenu(menuName = "Scriptable object/Item")]
// Monobehabiour 대신 SriptableObject를 쓰는 이뉴는 최적화 관련
public class Item : ScriptableObject {

    [Header("Only gameplay")]
    public TileBase tile;	// 그리드 보여주기
    public ItemType type;	
    public ActionType actionType;
    public Vector2Int range = new Vector2Int(5, 4);
    
    [Header("Only UI")]
    public bool stackable = true;	// 여러 개 중첩할 수 있는지 아이템인지
    
    [Header("Both")]
    public Sprite image;	// 인벤토리 스프라이트 보여주기
    
}

public enum ItemType {
	BuildingBlock,
    Tool
}

public enum ActionType {
	Dig,
    Mine
}
  1. Items 폴더 만들기
  2. Create -> Scriptable object -> Item 선택
  3. Digging block 이름 붙이기
  4. Both Image 적당한 스프라이트 넣기

2. Initialise items based on scriptable objects

InventoryItem.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
	public Item item;
    
    [Header("UI")]
    public Image image;
    
    [HideInInspector] public Transform parentAfterDrag;
    
    // 매개변수로 Item newItem을 받고 받은 newItem의 이미지를 이 객체의 sprite로 초기화
    public void InitialiseItem(Item newItem) {
    	item = newItem;
    	image.sprite = newItem.image;
	}
    
    public void OnBeginDrag(PointerEventData eventData) {
    	image.raycastTarget = false;
        parentAfterDrag = transform.parent; 
        transform.SetParent(transform.root);
	}
    
    public void OnDrag(PointerEventData eventData) {
    	transform.position = Input.mousePosition;
	}
    
    public void OnEndDrag(PointerEventData eventData) {
    	image.raycastTarget = true;
        transform.SetParent(parentAfterDrag);
	}
}

3. Finding free slot in the inventory

  1. 하이어러키에 InventoryManager 추가 후 하이어러키 Main Camera 밑에 위치
  2. InventoryManager.cs 컴포넌트 추가

InventoryManger.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour {
	// 만들어둔 InventorySlot을 저장할 배열
    public InventorySlot[] inventorySlots;
    public GameObject inventoryItemPrefab;
    
    // 비어있는 InventoryItemSlot 순서데로 Item 생성
	public void AddItem(Item item) {
    	// Find any empty slot
        for (int i = 0; i < inventorySlots.Length; i++) {
        	InventorySlot slot = inventorySlots[i];
            // InventoryItemSlot의 자식인 InventoryItem 가져오기
            InventoryItem itemInSlot = slot.GetComponentInChildren<InventoryItem>();
            
            // 만약 InventorySlot에 InventoryItem이 없는 경우 아이템 생성
            if (itemInSlot == null) {
            	SpawnNewItem(item, slot);
                return;
			}
		}
    }
    
    // 아이템을 이동할 InventorySlot이 비어있다면 해당 Slot에 아이템 스폰
    void SpawnNewItem(Item item, InventorySlot slot) {
    	GameObject newItemGo = Istantiate(inventoryItemPrefab, slot.transform);
        InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
        inventoryItem.InitialiseItem(item);
    }
  1. InventoryManager Inventory Slots에 여태까지 만들어둔 InventorySlot 모두 넣기
  2. IventoryManager Inventory Item PrefabInventoryItem 프리펩 추가
  3. Canvas 안에 Create Emtpy TestGroup 만들기
  4. TestGroup 안에 UI ->Button 3개 Spawn Item 1, Spawn Item 2, Spawn Item 3 만들기
  5. TestGroupDemoScript.cs 추가

DemoScript.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DemoScript : MonoBehaviour {
	public InventoryManager inventoryManager;
    // 아이템을 id와 아이템 매칭을 위한 배열
    public Item[] itemsToPickup;
    
    // 아이템 id를 매개변수로 받아서 itemsToPickup에서 Item을 얻고, InventoryManager.AddItem()을 통해 인벤토리에 추가
    public void PickupItem(int id) {
    	inventoryManager.AddItem(itemsToPickup[id]);
	}
}
  1. TestGroup Inventory ManagerInventory Manager 할당
  2. TestGroup Items To PickupItems 할당
  3. Spawn item 1, Spawn item 2, Spawn item 3 모두에 On Click() TestGroup -> DemoScript -> PickupItem(int) 추가
  4. 각각의 Spawn item에 서로 다른 id 부여

4. Checking if the inventory if full

InventoryManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour {
	public InventorySlot[] inventorySlots;
    public GameObject inventoryItemPrefab;
    
    public bool AddItem(Item item) {
    	for (int i = 0; i < inventorySlots.Length; i++) {
        	InventorySlot slot = inventorySlots[i];
            InventoryItem itemInSlot = slot.GetComponentInChildren<InventoryItem>();
            if(itemInSlot == null) {
            	SpawnNewItem(item, slot);
                // 인벤토리에 빈공간이 있다면 true 반환
                return true;
			}
            // 인벤토리에 빈공간이 없다면 false 반환
            return false;
	}
    
    void SpawnNewItem(Item item,  InventorySlot slot) {
    	GameObject newItemGo = Instantiate(inventoryItemPrefab, slot.transform);
        InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
        inventoryItem.InitialiseItem(item);
	}
}

DemoScript.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DemoScript : MonoBehaviour {
	public InventoryManager inventoryManager;
    public Item[] itemsToPickup;
    
    public void PickupItem(int id) {
    	bool result = inventoryManager.AddItem(itemsToPickup[id]);
        if (result == true) {
        	Debug.Log("Item added");
		}
        else {
        	Debug.Log("INVENTORY FULL!");
		}
	}
}

5. Stacking items

InventoryItem.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
	[Header("UI")]
    public Image image;
    public Text countText;
    
    [HideInInspector] public Item item;
    
    // 쌓을 수 있는 아이템을 표시하기 위해 사용
    [HideInInspector] public int count = 1;
    
    
    [HideInInspector] public Transform parentAfterDrag;
    
    public void InitialiseItem(Item newItem) {
    	item = new Item;
        image.sprite = newItem.image;
        RefreshCount();
	}
    
    // 아이템 수량 리프레쉬
    public void RefreshCount() {
    	countText.text = count.ToString();
	}
    
    public void OnBeginDrag(PointerEventData eventData) {
    	image.raycastTarget = false;
        parentAfterDrag = transform.parent;
        transform.SetParent(transform.root);
	}
    
    public OnDrag(PointerEventData eventData) {
    	transform.position = Input.mousePosition;
	}
    
    public OnEndDrag(PointerEventData eventData) {
    	image.raycastTarget = true;
        transform.SetParent(parentAfterDrag);
	}
}
  1. InventoryItem 프리펩에 UI -> Text Count 추가
  2. Count 아래쪽에 위치
  3. Text = 1로 설정
  4. 오른쪽 정렬 설정
  5. Font Style Bold로 설정
  6. Color 하얀색으로 설정
  7. InventoryItemText 추가

InventoryItem.cs

	. . .
    
    public RefreshCount() {
    	countText.text = count.ToString();
        
        // 1 보다 클 때만 숫자 표시
        bool textActive = count > 1;
        countText.gameObject.SetActive(textActive);
    }
    
    . . . 

InventoryManager.cs

using Systems.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour {
	// 최대 중첩 횟수
	public int maxStackedItems = 4;
	public InventorySlot[] inventorySlots;
    public GameObject inventoryItemPrefab;
    
    public bool AddItem(Item item) {
    	// 모든 아이템 슬롯을 대상으로 진행
    	for(int i = 0; i < inventorySlots.Length; i++) {
        	// i 번째 InventorySlot을 가져와서
        	InventorySlot slot = inventorySlots[i];
            // 해당 Slot에 있는 InventoryItem을 가져옴
    		InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            
            // 가져온 아이템이 null이 아니고, 같은 아이템 이고, 갯수가 4보다 작다면
            // 아이템 갯수 증가
            if(itemInSlot != null &&
            	itemInSlot.item == item &&
                itemInSlot.count < maxStackedItems ) {
                	itemInSlot.count++;
                    itemInSlot.RefreshCont();
                    return true;
			}
		}
        
        // Find any empty slot
        for(int i = 0; i < inventorySlots.Length; i++) {
        	InventorySlot slot = inventorySlots[i];
            InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            if (itemInSlot == null) {
            	SpawnNewItem(item, slot);
                return true;
			}
		}
        
        return false;
	}
    
    void SpawnNewItem(Item item, InventorySlot slot) {
    	GameObject newItemGo = Instantiate(inventoryItemPrefab, slot.transform);
        InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
        inventoryItem.InitialiseItem(item);
	}
}

6. Showing selected slot

InventorySlot.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class InventorySlot : MonoBehaviour, IDropHandler {
	public Image image;
    // 선택됐으면 다른 색으로 표시
    public Color selectedColor, notSelectedColor;
    
    private void Awake() {
    	Deselect();
	}
    
    public void Select() {
    	image.color = selectedColor;
	}
    public void Deselect() {
    	image.color = notSelectedColor;
	}
    
    // Drag and Drop
    public void OnDrop(PointerEventData eventData) {
    	if (transform.childCount == 0) {
        	InventoryItem inventoryItem = eventData.pointerDrag.GetComponenet<InventoryItem>();
            inventoryItem.parentAfterDrag = transform;
		}
	}
}

InventoryManager.cs

using Systems.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour {
	// 최대 중첩 횟수
	public int maxStackedItems = 4;
	public InventorySlot[] inventorySlots;
    public GameObject inventoryItemPrefab;
    
    /*********************************************
    				추 가 된 부 분 
    **********************************************/
    // Default selectedSlot 번호
    int selectedSlot = -1;
    
    private void Start() {
    	ChangeSelectedSlot(0);
	}
    
    void ChangeSelectedSlot(int newValue) {
    	// 선택된 인덱스가 0 이상인지 검사
    	if (selectedSlot >= 0) {
        	inventorySlots[selectedSlot].Deselect();
		}
        
        inventorySlots[newValue].Select();
        selectedSlot = newValue;
	}
    /*********************************************
    				추 가 된 부 분 
    **********************************************/
    
    public bool AddItem(Item item) {
    	// 모든 아이템 슬롯을 대상으로 진행
    	for(int i = 0; i < inventorySlots.Length; i++) {
        	// i 번째 InventorySlot을 가져와서
        	InventorySlot slot = inventorySlots[i];
            // 해당 Slot에 있는 InventoryItem을 가져옴
    		InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            
            // 가져온 아이템이 null이 아니고, 같은 아이템 이고, 갯수가 4보다 작다면
            // 아이템 갯수 증가
            if(itemInSlot != null &&
            	itemInSlot.item == item &&
                itemInSlot.count < maxStackedItems ) {
                	itemInSlot.count++;
                    itemInSlot.RefreshCont();
                    return true;
			}
		}
        
        // Find any empty slot
        for(int i = 0; i < inventorySlots.Length; i++) {
        	InventorySlot slot = inventorySlots[i];
            InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            if (itemInSlot == null) {
            	SpawnNewItem(item, slot);
                return true;
			}
		}
        
        return false;
	}
    
    void SpawnNewItem(Item item, InventorySlot slot) {
    	GameObject newItemGo = Instantiate(inventoryItemPrefab, slot.transform);
        InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
        inventoryItem.InitialiseItem(item);
	}
}
  1. inventorySlot 프래팹 Image에 이미지 추가
  2. 적당한 색상 선택

7. Chaging selected slot

InventoryManager.cs

using Systems.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour {
	// 최대 중첩 횟수
	public int maxStackedItems = 4;
	public InventorySlot[] inventorySlots;
    public GameObject inventoryItemPrefab;
    
    // Default selectedSlot 번호
    int selectedSlot = -1;
    
    private void Start() {
    	ChangeSelectedSlot(0);
	}
    
    /*********************************************
    				추 가 된 부 분 
    **********************************************/
    
    private void Update() {
		if (Input.inputString != null) {
        	bool isNumber = int.TryParse(Input.inputString, out int number);
            if(isNumber && number > 0 && number < 8) {
            	ChangeSelectedSlot(number - 1);
			}
		}
    }
    
    /*********************************************
    				추 가 된 부 분 
    **********************************************/
    
    void ChangeSelectedSlot(int newValue) {
    	// 선택된 인덱스가 0 이상인지 검사
    	if (selectedSlot >= 0) {
        	inventorySlots[selectedSlot].Deselect();
		}
        
        inventorySlots[newValue].Select();
        selectedSlot = newValue;
	}

    public bool AddItem(Item item) {
    	// 모든 아이템 슬롯을 대상으로 진행
    	for(int i = 0; i < inventorySlots.Length; i++) {
        	// i 번째 InventorySlot을 가져와서
        	InventorySlot slot = inventorySlots[i];
            // 해당 Slot에 있는 InventoryItem을 가져옴
    		InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            
            // 가져온 아이템이 null이 아니고, 같은 아이템 이고, 갯수가 4보다 작다면
            // 아이템 갯수 증가
            if(itemInSlot != null &&
            	itemInSlot.item == item &&
                itemInSlot.count < maxStackedItems ) {
                	itemInSlot.count++;
                    itemInSlot.RefreshCont();
                    return true;
			}
		}
        
        // Find any empty slot
        for(int i = 0; i < inventorySlots.Length; i++) {
        	InventorySlot slot = inventorySlots[i];
            InventoryItem itemInSlot = slot.GetComponenetInChildren<InventoryItem>();
            if (itemInSlot == null) {
            	SpawnNewItem(item, slot);
                return true;
			}
		}
        
        return false;
	}
    
    void SpawnNewItem(Item item, InventorySlot slot) {
    	GameObject newItemGo = Instantiate(inventoryItemPrefab, slot.transform);
        InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
        inventoryItem.InitialiseItem(item);
	}
}
profile
게임 개발자 지망생

0개의 댓글