2024. 1. 2. 03:09ㆍ스파르타코딩클럽 게임개발
유니티 인벤토리 시스템 구현
오늘은 유튜브에서 공부한 내용을 바탕으로 유니티 게임에서 사용할 수 있는 기본적인 인벤토리 시스템을 구현해 보았다.
아직 완성본은 아니지만.. 프로젝트에 구현하기 위해 학습한 내용을 정리하겠다.
인벤토리 시스템의 핵심 구성 요소
인벤토리 시스템은 주로 다음과 같은 클래스들로 구성
- InventorySystem - 인벤토리의 핵심 기능을 관리
- InventorySlot - 인벤토리 내의 각각의 슬롯을 나타냄
- InventoryItemData - 인벤토리 아이템의 데이터를 저장함.
- InventoryPickUp - 아이템을 주워 인벤토리에 추가하는 기능을 담당함
- InventoryHolder - 인벤토리 시스템을 가지는 객체를 위한 클래스
InventorySystem 클래스
- 인벤토리 슬롯의 초기화
- 아이템을 인벤토리에 추가하는 기능
- 인벤토리에 특정 아이템이 있는지 확인
- 빈 슬롯이 있는지 확인
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System.Linq;
[System.Serializable] // Unity 인스펙터에서 보이도록 직렬화
public class InventorySystem
{
[SerializeField] private List<InventorySlot> inventorySlots; // 인벤토리 슬롯을 저장하는 리스트
public List<InventorySlot> InventorySlots => inventorySlots; // 인벤토리 슬롯에 대한 공개 접근자
public int InventroySize => InventorySlots.Count; // 인벤토리 크기 반환
public UnityAction<InventorySlot> OnIventorySlotChanged; // 슬롯이 변경될 때 호출될 이벤트
public InventorySystem(int size) // 생성자: 인벤토리 크기를 지정받아 초기화
{
inventorySlots = new List<InventorySlot>(size);
for (int i = 0; i < size; i++) // 지정된 크기만큼 인벤토리 슬롯 생성
{
inventorySlots.Add(new InventorySlot());
}
}
public bool AddToInventory(InventoryItemData itemToAdd, int amountToAdd) // 아이템을 인벤토리에 추가하는 메소드
{
// 아이템이 이미 인벤토리에 있는지 확인하고, 해당 슬롯 리스트를 가져옴
if (ContainsItem(itemToAdd, out List<InventorySlot> inventorySlot))
{
foreach (var slot in inventorySlot) // 각 슬롯을 확인하며 추가 가능 여부 검사
{
if (slot.RoomLeftInStack(amountToAdd))
{
slot.AddToStack(amountToAdd);
OnIventorySlotChanged?.Invoke(slot); // 슬롯이 변경되었으니 이벤트 발생
return true;
}
}
}
// 빈 슬롯이 있는지 확인하고, 첫 번째 빈 슬롯에 아이템 추가
if (HasFreeSlot(out InventorySlot freeSlot))
{
freeSlot.UpdateInventorySlot(itemToAdd, amountToAdd);
OnIventorySlotChanged?.Invoke(freeSlot); // 슬롯이 변경되었으니 이벤트 발생
return true;
}
return false; // 추가 실패
}
public bool ContainsItem(InventoryItemData ItemToAdd, out List<InventorySlot> inventorySlot)
{
// 해당 아이템을 가진 슬롯을 찾아 리스트로 반환
inventorySlot = InventorySlots.Where(i => i.ItemData == ItemToAdd).ToList();
Debug.Log(inventorySlot.Count);
return inventorySlot != null && inventorySlot.Count > 0; // 수정: 아이템이 있는지 여부 반환
}
public bool HasFreeSlot(out InventorySlot freeSlot)
{
// 빈 슬롯을 찾아 반환
freeSlot = InventorySlots.FirstOrDefault(i => i.ItemData == null);
return freeSlot != null; // 수정: 빈 슬롯이 있는지 여부 반환
}
}
InventorySlot 클래스
- 슬롯 초기화
- 아이템 데이터와 스택 크기 관리
- 슬롯에 남은 공간 계산
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable] // Unity 인스펙터에서 보이도록 직렬화
public class InventorySlot
{
[SerializeField] private InventoryItemData itemData; // 슬롯에 저장된 아이템 데이터
[SerializeField] private int stackSize; // 아이템의 수량
public InventoryItemData ItemData => itemData; // 아이템 데이터에 대한 공개 접근자
public int StackSize => stackSize; // 스택 크기에 대한 공개 접근자
// 생성자: 아이템 데이터와 수량을 받아 초기화
public InventorySlot(InventoryItemData source, int amount)
{
itemData = source;
stackSize = amount;
}
// 기본 생성자: 슬롯을 초기화
public InventorySlot()
{
ClearSlot();
}
// 슬롯을 비워 초기 상태로 설정
public void ClearSlot()
{
itemData = null;
stackSize = -1;
}
// 슬롯을 새로운 아이템 데이터와 수량으로 업데이트
public void UpdateInventorySlot(InventoryItemData data, int amount)
{
itemData = data;
stackSize = amount;
}
// 스택에 추가할 공간이 있는지 확인하고, 남은 공간을 반환
public bool RoomLeftInStack(int amountToAdd, out int amountReamining)
{
amountReamining = itemData.MaxStackSize - amountToAdd;
return RoomLeftInStack(amountToAdd);
}
// 스택에 추가할 공간이 있는지 확인 (오버로드)
public bool RoomLeftInStack(int amountToAdd)
{
if (stackSize + amountToAdd <= itemData.MaxStackSize) return true;
else return false;
}
// 스택에 아이템 추가
public void AddToStack(int amount)
{
stackSize += amount;
}
// 스택에서 아이템 제거
public void RemoveFromStack(int amount)
{
stackSize -= amount;
}
}
이 InventorySlot 클래스는 인벤토리 시스템에서 하나의 슬롯을 관리한다.
각 슬롯은 특정 아이템(InventoryItemData 객체)과 그 아이템의 수량(stackSize)을 가지고 있다.
클래스는 이러한 아이템과 수량을 관리하는 여러 메소드를 제공한다.
예를 들어, 슬롯을 초기화하거나, 새 아이템으로 업데이트하거나, 아이템을 추가/제거하는 기능 등등이 있다.
InventoryItemData 클래스
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 유니티 에디터 메뉴에서 새 인벤토리 아이템 데이터를 생성할 수 있게 해주는 어트리뷰트
[CreateAssetMenu(menuName ="Inventory System/Inventory Item")]
public class InventoryItemData : ScriptableObject // ScriptableObject를 상속받음
{
public int ID; // 아이템의 고유 ID
public string DisplayName; // 아이템의 표시 이름
[TextArea(4, 4)] // 인스펙터에서 멀티라인 텍스트 영역으로 표시
public string Description; // 아이템 설명
public Sprite Icon; // 아이템 아이콘
public int MaxStackSize; // 아이템이 쌓일 수 있는 최대 크기
}
InventoryItemData 클래스는 각
아이템의 고유 식별자(ID),
표시 이름(DisplayName),
설명(Description),
아이콘(Icon),
그리고 최대 쌓을 수 있는 개수(MaxStackSize) 등의 필드를 가진다.
InventoryPickUp 클래스
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// CircleCollider2D 컴포넌트가 필요함을 명시
[RequireComponent(typeof(CircleCollider2D))]
public class InventoryPickUp : MonoBehaviour
{
public float PickUpRadius = 1f; // 아이템을 줍는 반경
public InventoryItemData itemData; // 줍을 아이템의 데이터
private CircleCollider2D myCollider; // 이 게임 오브젝트의 CircleCollider2D 참조
private void Awake()
{
myCollider = GetComponent<CircleCollider2D>(); // CircleCollider2D 컴포넌트 가져오기
myCollider.isTrigger = true; // 트리거로 설정하여 물리적 충돌 없이 이벤트만 발생하게 함
myCollider.radius = PickUpRadius; // 설정된 반경으로 콜라이더의 반경 설정
}
// 다른 콜라이더가 이 오브젝트의 트리거 영역에 들어왔을 때 호출됨
private void OnTriggerEnter2D(Collider2D collision)
{
// 충돌한 객체에서 InventoryHolder 컴포넌트를 가져옴
var inventory = collision.transform.GetComponent<InventoryHolder>();
// InventoryHolder 컴포넌트가 없으면 함수 종료
if (!inventory) return;
// 인벤토리에 아이템을 추가할 수 있으면 아이템을 추가하고, 이 게임 오브젝트를 파괴
if (inventory.InventorySystem.AddToInventory(itemData, 1))
{
Destroy(this.gameObject);
}
}
}
이 스크립트는 InventoryHolder 컴포넌트를 가진 객체(일반적으로 플레이어)가 이 오브젝트의 트리거 영역 내로 들어올 때, 해당 객체의 인벤토리에 아이템을 추가하는 기능이다.
OnTriggerEnter2D 메소드는 플레이어가 아이템 줍기 영역에 들어올 때 자동으로 호출되며, 이 때 인벤토리에 아이템을 추가하고, 아이템을 대표하는 게임 오브젝트는 파괴되고..
이렇게 하면 아이템이 인벤토리로 이동한 것처럼(!) 보이게 된다.
InventoryHolder 클래스
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[System.Serializable] // Unity 인스펙터에서 보이도록 직렬화
public class InventoryHolder : MonoBehaviour
{
[SerializeField] private int inventorySize; // 인벤토리 크기
[SerializeField] protected InventorySystem inventorySystem; // 인벤토리 시스템
public InventorySystem InventorySystem => inventorySystem; // 인벤토리 시스템에 대한 공개 접근자
// 인벤토리 디스플레이 요청시 사용될 정적 이벤트
public static UnityAction<InventorySystem> OnDynamicInventoryDisplayRequested;
private void Awake()
{
inventorySystem = new InventorySystem(inventorySize); // 인벤토리 시스템 초기화
}
}
InventoryHolder 클래스는 인벤토리의 크기(inventorySize)와 실제 인벤토리 시스템(inventorySystem)을 필드로 가진다.
OnDynamicInventoryDisplayRequested는 정적 이벤트로, 인벤토리 시스템의 디스플레이를 동적으로 요청할 때 사용된다.
예를 들어, 게임에서 플레이어가 인벤토리를 열 때 이 이벤트를 통해 인벤토리 시스템을 UI에 표시할 수 있다.
이는 인벤토리 시스템과 UI 사이의 커뮤니케이션을 원활하게 만들어 준다.
구현 방식 및 동작
이 인벤토리 시스템은 아이템의 추가, 삭제, 조회 등의 기능을 제공하며, 각 아이템은 슬롯에 저장된다.
플레이어가 아이템을 주울 때, InventoryPickUp 클래스의 OnTriggerEnter2D 메소드가 호출되어 인벤토리에 아이템이 추가되고(그렇게 보인다.). 이때, InventorySystem 클래스의 AddToInventory 메소드를 통해 아이템이 실제로 인벤토리에 추가되며, 해당 슬롯이 업데이트된다.
'스파르타코딩클럽 게임개발' 카테고리의 다른 글
오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 44일차(ResourceManager 클래스 사용하는법) (2) | 2024.01.03 |
---|---|
오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 42일차땜빵(아이템 제작 스크립트 리팩토링 .. 실패) (0) | 2024.01.02 |
오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 41일차(코루틴에 대해) (1) | 2023.12.28 |
오늘는 스파르타 코딩 클럽 unity 게임 개발 과정 40일차(파티클에 대해 ) (0) | 2023.12.27 |
오늘는 스파르타 코딩 클럽 unity 게임 개발 과정 37일차(아이템 매니저 추가 구현 ) (0) | 2023.12.20 |