Post

[Jem Clash] 개발일지 #9 - 상점 기능 마무리, 아이템 장착 구현

[Jem Clash] 개발일지 #9 - 상점 기능 마무리, 아이템 장착 구현

아이템 가격 추가

지난 포스팅에 이어 상점에 아이템별 가격을 적용해 주도록 하겠습니다

items.json 파일에 itemPrice 데이터를 추가해 주고 ItemDescription, ItemImporter, ItemSlot, UpgradeData 등 변경이 필요한 모든 파일에 코드를 작성해 줍니다

간단하게 테스트를 해보면,

Image

구매 비용이 바뀌는 것을 잘 확인할 수 있습니다

아이템 장착 구현

다음으로 아이템을 구매하여 장착하는 기능을 만들어 볼게요

우선 아이템들을 관리하는 ItemManager 스크립트를 만들어줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ItemManager : MonoBehaviour
{
    public GameObject itemBtnPrefab;
    public List<UpgradeData> items = new();

    public void CreateItemBtn(UpgradeData data)
    {
        if (data == null) return;

        GameObject buttonObj = Instantiate(itemBtnPrefab, transform);
        Upgrade upgrade = buttonObj.GetComponent<Upgrade>();

        if (upgrade != null)
        {
            upgrade.upgradeData = data;
            buttonObj.GetComponent<Image>().sprite = upgrade.upgradeData.itemImage;
            upgrade.level = 0;
        }
    }
}

이 스크립트에 아이템 중복 확인 등 다른 기능들도 나중에 추가할 예정이에요

일단 CreateItemBtn 메서드를 만들어주고 ItemDescription과 ItemSlot 스크립트에 코드를 추가해 줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ItemDescription : MonoBehaviour
{
    [Header("Buy Values")] public ItemManager itemManager;
    public Button buyButton;
    public TMP_Text itemPriceText;
    public int itemPrice;

    private UpgradeData _selectedItem;

    public void SetSelectedItem(UpgradeData item)
    {
        _selectedItem = item;
    }

    private void BuyItem(int amount)
    {
        if (MoneyManager.Instance.SubtractMoney(amount))
            itemManager.CreateItemBtn(_selectedItem);
    }
}

ItemDescription에 현재 선택된 아이템을 저장하는 변수를 추가해 주고, 아이템을 구매할 경우 CreateItemBtn 메서드를 실행합니다

1
2
3
4
5
6
7
8
9
10
11
public class ItemSlot : MonoBehaviour
{
    private UpgradeData _item;

    public void OnClick()
    {
        itemDescription.UpdateItemDescPanel(
            itemNameText.text, _itemRarity, _itemCategory, _itemDescription, _itemCooldownTime, _itemPrice);
        itemDescription.SetSelectedItem(_item);
    }
}

ItemSlot에는 OnClick 메서드에 ItemDescription의 SetSelectedItem 메서드를 추가해 줍니다

이후 유니티 에디터에서 아이템의 부모 오브젝트에 ItemManager 스크립트를 붙여주고, 실행해 주면 잘 작동하겠지 싶었는데 콘솔에 NullReferenceException 에러가 발생했어요

기능들은 잘 작동하는 것 같은데 에러가 뜨는 게 불안해서 디버깅을 진행했습니다

원인은 Upgrade 스크립트의 OnEnable 메서드에 있었습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ItemManager
public void CreateItemBtn(UpgradeData data)
{
    if (data == null) return;

    GameObject buttonObj = Instantiate(itemBtnPrefab, transform);
    Upgrade upgrade = buttonObj.GetComponent<Upgrade>();

    if (upgrade != null)
    {
        upgrade.upgradeData = data;
        upgrade.level = 0;

        Button btn = buttonObj.GetComponent<Button>();
        btn.onClick.AddListener(upgrade.OnClick);
    }
}
1
2
3
4
5
6
7
8
9
10
11
// Upgrade
public void OnEnable()
{
    _levelText.text = $"Lv.{level:D2}";

    switch (upgradeData.itemType)
    {
        case UpgradeData.UpgradeType.UnitSpawn:
        // ...
    }
}

ItemManager 스크립트의 CreateItemBtn 메서드에 버튼 오브젝트의 Upgrade 컴포넌트를 가져오는 코드가 있는데, 이걸 가져오기 전에 Upgrade 스크립트에서 upgradeData.itemType에 접근하려고 해서 Null 에러가 발생하고 있었네요

기존 코드를 OnEnable이 아닌 Start 메서드에 넣어줌으로써 간단하게 해결할 수 있었습니다

Image

다시 실행해 보니 에러 메시지 없이 아이템 구매와 장착이 정상적으로 작동하는 걸 볼 수 있네요 👍


오늘 포스팅은 상대적으로 짧은 느낌인데, 현재 맵을 어떻게 구현할지 기획하고 생성 방식을 다듬는 중입니다

로그라이트 게임으로 만드는 만큼 매번 경로가 다르게 생성되도록 할 계획인데, 절차적 생성을 이용해 구현할 계획이에요

맵 생성 관련해선 한 포스팅에 모아서 올리고 싶어 다음 포스팅에서 소개해 드리도록 하겠습니다 👋


추가로 라이더 디버깅 기능을 계속 쓰고 있는데 너무 편하네요

그동안 코딩하면서 Debug.Log 넣고 지우고 반복하는 거 힘들었는데, 왜 진작 안 썼는지 모르겠습니다 ㅎ

여러분도 디버거 꼭 쓰세요 🫠

This post is licensed under CC BY 4.0 by the author.