내일배움캠프/TIL

내일배움캠프 6일차 TIL - Null 값을 가질수 없는 struct

서보훈 2024. 9. 20. 19:48

 

문제 발생

개인과제 텍스트 RPG 제작중 인벤토리를 만들던중, 인벤토리가 비어있는지 체크하고 빈 슬롯이 있으면 해당 슬롯에 아이템을 넣도록 제작하는데 null 값을 사용할 수 없는 오류가 발생했습니다.

 

시도해본 것

아이템은 제가 만든 구조체로, 이 정보에 ItemId 가 존재하며 생성자를 통해서 아이템을 만들때 아이템의 아이디를 필수적으로 정해주어야 합니다.

public struct Item
{
    public string Name;
    public string Description;
    public ItemType Type;
    public float ItemAttack;
    public float ItemDefence;
    public int ItemId;

    public Item(int id, string itemName, string itemDescription, ItemType type, int attck, int defence)
    {
        ItemId = id;
        Name = itemName;
        Description = itemDescription;
        Type = type;
        ItemAttack = attck;
        ItemDefence = defence;
    }
}

또한 현재 이 게임의 첫번째 아이템의 아이디를 1로 지정해두었기 때문에 아이디가 0인 아이템은 존재하지 않습니다.

public List<Item> items = new List<Item>();

public void ResetItemList()
{
    items.Add(new Item(1, "시작의 검", "처음 만들어진 검", ItemType.Weapon, 1, 0));
}

따라서 해당 슬롯의 아이템 아이디가 0일경우 빈칸으로 처리하여 문제를 해결할 수 있습니다.

//인벤토리에 아이템 획득
public void GetItem(Item item)
{
    //아이템 인벤토리의 빈칸에 아이템 등록
    for(int i = 0; i < inventory.Length; i++)
    {
        //
        if (inventory[i].ItemId == 0)
        {
            inventory[i] = item;
            break;
        }

        //만약, 빈칸이 없을경우(빈칸이 있으면 break를 통해 for문을 종료함)
        if(i == inventory.Length)
        {
            Console.WriteLine("인벤토리가 가득차 아이템 획득에 실패했습니다.");
        }
    }
}

 

해결방법

단, 위의 방법이 근본적인 해결방법은 아닙니다.

이 문제의 원인은 아이템 구조체로, 구조체가 가진 모든 멤버변수는 null을 가질수 없으며 구조체가 생성될때 입력되지 않은변수들은 모두 기본값으로 초기화 됩니다.

 

따라서 Item 구조체를 클래스 형태로 변경할경우, 생각했던대로 배열에 빈 칸이 있을때 해당 배열에 아이템을 넣울 수 있게됩니다.

//인벤토리에 아이템 획득
public void GetItem(Item item)
{
    //아이템 인벤토리의 빈칸에 아이템 등록
    for(int i = 0; i < inventory.Length; i++)
    {
        //
        if (inventory[i] == null)
        {
            inventory[i] = item;
            break;
        }

        //만약, 빈칸이 없을경우(빈칸이 있으면 break를 통해 for문을 종료함)
        if(i == inventory.Length)
        {
            Console.WriteLine("인벤토리가 가득차 아이템 획득에 실패했습니다.");
        }
    }
}

 

 

알게된것

class와 struct는 생성과 사용이 비슷하지만, struct는 상속을 받지 못하며 데이터를 담기 위한 그릇이기 때문에 null 값을 가지지 못한다는것을 알 수 있었습니다.

 

처음엔 배열로 만들어서 배열이 초기화된 상태라 생기는 문제라고 생각해서 리스트로 내용을 변경해야하나 생각했었지만,

분명 이전에 사용했던 기억이 있는 내용이라 해당 내용을 다시 확인해보니 확실히 null 여부를 사용하여 아이템을 배열에 넣어주는것을 볼 수 있었습니다.

public class Equipment : Singleton<Equipment>
{
	#region Variables
	private Inventory inventory;

	public Item[] mainWeaponItems;
	public Item[] consumWeaponItems;
	public Item[] equipItems;

	public UnityAction<Item, Item> OnEquipChange;

	private const int armorTypeIndex = 2;
	#endregion

	private void Start()
	{
		inventory = Inventory.Instance;

		mainWeaponItems = new Item[3];
		consumWeaponItems = new Item[2];
		equipItems = new Item[4];

		for (int i = 0; i < mainWeaponItems.Length; i++)
		{
			equipItems[i] = new Item();
		}

		for (int i = 0; i < consumWeaponItems.Length; i++)
		{
			equipItems[i] = new Item();
		}

		for (int i = 0; i < equipItems.Length; i++)
		{
			equipItems[i] = new Item();
		}

	}

	//주무기류 장착
	public void EquipMainWeapon(Item newWeapon, int equipIndex = -1)
	{
		//0번 타입이 아닌 아이템 장착 시도시 실패처리
		if (GetEquipIndex(newWeapon.itemId) != 0)
		{
			Debug.Log("잘못된 아이템 장착 시도");
			return;
		}

		Item oldItem = null;
		//슬롯지정 없이 아이템 장착 시도시
		if(equipIndex == -1)
		{
			//비어있는 무기 슬롯에 아이템 장착, 비어있는 슬롯이 없을경우 실패처리
			for(int i = 0; i <mainWeaponItems.Length; i++)
			{
				if(mainWeaponItems[i] == null)
				{
					mainWeaponItems[i] = newWeapon;
					break;
				}
			}
			Debug.Log("주무기칸이 꽉찼습니다.");
		}
		//슬롯을 지정하고 아이템 장착 시도시
		else
		{
			//기존 장착된 무기가 있을경우 그 무기 저장
			oldItem = mainWeaponItems[equipIndex];
			//슬롯에 새로운 무기 저장
			mainWeaponItems[equipIndex] = newWeapon;

			//기존 장착 무기가 있을경우 인벤토리에 추가
			if(oldItem != null)
			{
				inventory.AddItem(oldItem, true);
			}
		}
		//장비창 새로고침
		OnEquipChange?.Invoke(oldItem, newWeapon);
	}

 

 

이후 혹시나 해서 Item 의 내용을 확인해본 결과, class로 정의하여 사용중이였으며 이 부분에서 문제가 발생했음을 인지하고 확인해본 결과 struct는 null 값을 가질수 없다는것을 알게 되었습니다.

 

해당내용으로 인해서 이미 만들어둔 내용을 바꿔줄 필요가 생기게 되었습니다...