✅ 기능
이전 글에서 아이템을 데이터화하여 관리하는 ScriptableObject을 이용한 아이템을 생성하였습니다. 본 글에서는 해당 아이템의 데이터를 활용하여 인벤토리 슬롯에 저장하고 보관할 수 있도록 하는 기능을 정리해 보았습니다.
🔨 로드맵
2. 유니티 인벤토리 시스템(2) - 아이템(ScriptableObject)
[📌현재 글] 3. 유니티 인벤토리 시스템(3) - 아이템 슬롯(인벤토리 슬롯)
5. 유니티 인벤토리 시스템(5) - 아이템 슬롯 관리
✅ 구현
1. InventorySlot
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 인벤토리 슬롯 하나를 담당
/// </summary>
public class InventorySlot : MonoBehaviour
{
private Item mItem; //현재 아이템 인스턴스
public Item Item
{
get
{
return mItem;
}
}
[Header("해당 슬롯에 어떠한 타입만 들어올 수 있는지 타입 마스크")]
[SerializeField] private ItemType mSlotMask;
private int mItemCount; //획득한 아이템의 개수
[Header("아이템 슬롯에 있는 UI 오브젝트")]
[SerializeField] private Image mItemImage; //아이템의 이미지
[SerializeField] private Image mCooltimeImage; //아이템 쿨타임 이미지
[SerializeField] private Text mTextCount; //아이템의 개수 텍스트
// 아이템 이미지의 투명도 조절
private void SetColor(float _alpha)
{
Color color = mItemImage.color;
color.a = _alpha;
mItemImage.color = color;
}
/// <summary>
/// mSlotMask에서 설정된 값에 따라 비트연산을한다.
/// 현재 마스크값이 비트연산으로 0이 나온다면 현재 슬롯에 마스크가 일치하지 않는다는 뜻.
/// 0이 아닌 수는 현재 비트위치(10진수로 1, 2, 4, 8)로 값이 나온다.
/// </summary>
public bool IsMask(Item item)
{
return ((int)item.Type & (int)mSlotMask) == 0 ? false : true;
}
// 인벤토리에 새로운 아이템 슬롯 추가
public void AddItem(Item item, int count = 1)
{
mItem = item;
mItemCount = count;
mItemImage.sprite = mItem.Image;
if (mItem.Type <= ItemType.Equipment_SHOES)
{
mTextCount.text = "";
}
else
{
mTextCount.text = mItemCount.ToString();
}
SetColor(1);
}
// 해당 슬롯의 아이템 개수 업데이트
public void UpdateSlotCount(int count)
{
mItemCount += count;
mTextCount.text = mItemCount.ToString();
if (mItemCount <= 0)
ClearSlot();
}
// 해당 슬롯 하나 삭제
public void ClearSlot()
{
mItem = null;
mItemCount = 0;
mItemImage.sprite = null;
SetColor(0);
mTextCount.text = "";
}
}
- 인벤토리 슬롯 하나를 관리하는 클래스입니다.
- 자기 자신이 가지고 있는 데이터를 인벤토리 관리자에게 건네주고, 또는 아이템 데이터를 받아옵니다.
private Item mItem; //현재 아이템 인스턴스
- 이전 글에서 구현한 아이템 데이터를 가지는 변수입니다.
- 이 변수에 아이템 데이터가 저장되며, 데이터를 건네줄 수 있습니다.
[Header("해당 슬롯에 어떠한 타입만 들어올 수 있는지 타입 마스크")]
[SerializeField] private ItemType mSlotMask;
- 해당 아이템 슬롯의 Type Mask입니다.
- 인스펙터에서 설정하며, 설정된 마스크만 해당 슬롯에 아이템이 들어올 수 있습니다.
- 인스펙터에서 다중으로 선택이 가능합니다.
private int mItemCount; //획득한 아이템의 개수
- 아이템의 개수입니다.
public bool IsMask(Item item)
- 마스크가 참인지 계산하는 함수입니다.
- Enum으로 생성된 마스크를 비트연산인 &연산을 통해 해당 비트가 서로 참이면 마스크가 참으로 리턴됩니다.
public void AddItem(Item item, int count = 1)
- 아이템을 현재 슬롯에 가져옵니다.
- Item 파라미터를 받아오며, 해당 Item을 슬롯의 mItem 변수로 설정하여 저장합니다.
- ItemType이 SHOES(장비아이템) 보다 작다면, 개수를 출력하지 않습니다.
- Item에서 Sprite를 가져와 자신의 mItemImage(Image)로 설정합니다.
public void UpdateSlotCount(int count)
- 아이템을 획득할 때 이미 아이템이 있는 상태에서 중첩 가능한 아이템을 또 획득했다면, 새로운 슬롯에 저장하지 않습니다.
- 해당 아이템 슬롯에서 개수를 업데이트합니다.
public void ClearSlot()
- 아이템을 사용하거나, 버리거나, 퀘스트 등으로 소모된 경우 해당 아이템슬롯을 지웁니다.
- mItem을 비우고, 개수를 0, 이미지도 제거합니다.
2. InventoryMain
using UnityEngine;
/// <summary>
/// 여러 아이템을 담을 가장 기본적인 인벤토리
/// </summary>
public class InventoryMain : InventoryBase
{
...
/// <summary>
/// 특정 아이템 슬롯에 아이템을 등록시킨다
/// </summary>
/// <param name="item">어떤 아이템?</param>
/// <param name="targetSlot">어느 슬롯에?</param>
/// <param name="count">개수는?></param>
public void AcquireItem(Item item, InventorySlot targetSlot, int count = 1)
{
//중첩이 가능하다면?
if (item.CanOverlap)
{
//마스크를 사용하여 해당 슬롯이 마스크에 허용되는 위치인경우에만 아이템을 집어넣도록 한다.
if (targetSlot.Item != null && targetSlot.IsMask(item))
{
if (targetSlot.Item.ItemID == item.ItemID)
{
//현재 슬롯의 아이템 개수(Count)를 갱신한다.
targetSlot.UpdateSlotCount(count);
}
}
}
else
{
targetSlot.AddItem(item, count);
}
}
public void AcquireItem(Item item, int count = 1)
{
//중첩이 가능하다면?
if (item.CanOverlap)
{
for (int i = 0; i < mSlots.Length; i++)
{
//마스크를 사용하여 해당 슬롯이 마스크에 허용되는 위치인경우에만 아이템을 집어넣도록 한다.
if (mSlots[i].Item != null && mSlots[i].IsMask(item))
{
if (mSlots[i].Item.ItemID == item.ItemID)
{
//현재 슬롯의 아이템 개수(Count)를 갱신한다.
mSlots[i].UpdateSlotCount(count);
return;
}
}
}
}
//장비 아이템이 아닌경우 새로운 슬롯에 놓는다.
for (int i = 0; i < mSlots.Length; i++)
{
if (mSlots[i].Item == null && mSlots[i].IsMask(item))
{
mSlots[i].AddItem(item, count);
return;
}
}
}
...
}
- 인벤토리 관리자 글에서 구현한 InventoryMain에 추가 함수를 작성합니다.
- 아이템을 획득하는 함수로, 인벤토리 슬롯들을 확인하고 적절한 슬롯에 아이템을 넣어주도록 하였습니다.
public void AcquireItem(Item item, InventorySlot targetSlot, int count = 1) {}
public void AcquireItem(Item item, int count = 1) {}
- 아이템을 획득합니다.
- 인벤토리 슬롯들을 확인하여 주울 수 있는 아이템인지, 중첩은 가능한지 확인하여 해당 슬롯에 아이템 데이터를 저장합니다.
- 아이템 타입에서 마스크를 적용하여 해당 슬롯에 마스크가 허용 가능한지 확인하여 적절한 슬롯에 들어가도록 하였습니다.
if (targetSlot.Item != null && targetSlot.IsMask(item))
- 해당 아이템이 현재 슬롯의 마스크에서 허용인지 확인합니다.
✅ 사용(적용) 예시
- 각 ItemSlot에 InventorySlot 스크립트를 컴포넌트로 생성합니다.
- 인스펙터 내 컴포넌트에서 각 오브젝트들을 알맞게 넣어줍니다.
- 인벤토리 슬롯에서 허용할 아이템 타입에 대한 마스크를 설정합니다.
✅ 테스트
아이템이 제대로 들어오는지, 마스크가 제대로 작동하는지 테스트합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleScript : MonoBehaviour
{
[Header("인벤토리 메인")]
[SerializeField] private InventoryMain mInventoryMain;
[Header("획득할 아이템")]
[SerializeField] private Item mHPItem, mManaItem;
private void OnGUI()
{
if (GUI.Button(new Rect(20, 20, 300, 40), "체력포션 아이템 획득"))
{
mInventoryMain.AcquireItem(mHPItem);
}
if (GUI.Button(new Rect(400, 20, 300, 40), "마나포션 아이템 획득"))
{
mInventoryMain.AcquireItem(mManaItem);
}
}
}
- 체력포션, 마나포션 아이템을 획득했다고 가정할 때, 인벤토리에 제대로 들어오는지 확인합니다.
- 위에서 나온 것처럼 NONE, SKILL을 제외한 모든 아이템에 대한 마스크를 허용한 상태에서는 좌측 위(0번 인덱스)부터 아이템이 들어오는 것을 볼 수 있습니다.
- Consumable인 마나포션은 하위슬롯에 저장되고, Ingredient인 체력포션은 상위슬롯에 저장되는 것을 볼 수 있습니다.
- 마스크에 대한 작동이 제대로 이루어지는 것을 확인하였습니다.
'unity game modules' 카테고리의 다른 글
[유니티] 인벤토리 시스템(5) - 아이템 슬롯 관리 (0) | 2022.12.26 |
---|---|
[유니티] 인벤토리 시스템(4) - 아이템 획득 (0) | 2022.12.26 |
[유니티] 인벤토리 시스템(2) - 아이템(ScriptableObject) (0) | 2022.12.26 |
[유니티] 인벤토리 시스템(1) - 인벤토리 관리자 (0) | 2022.12.26 |
[유니티] 3D World, Sprite Ordering Layer (0) | 2022.12.23 |