Post

[Jem Clash] 개발일지 #8 - 이미지 버그 수정, 상점 기능 추가

[Jem Clash] 개발일지 #8 - 이미지 버그 수정, 상점 기능 추가

이미지 버그 해결 + ItemImporter 업그레이드

지난 포스팅 때 아이템 이미지가 제대로 보이지 않는 버그가 있어서, 먼저 수정하고 가주겠습니다

Image

경로 설정을 제대로 했음에도 버그가 발생해서 구글링을 해보았습니다

1
newItem.itemImage = Resources.Load<Sprite>(item.imagePath);

아이템 이미지를 파싱하는 코드인데, 유니티 문서를 찾아보니 Resources.Load<T> 함수는 “Assets/Resources/” 경로 내부에서만 작동하는 것을 알 수 있네요

https://docs.unity3d.com/ScriptReference/Resources.Load.html

따라서 접근하는 에셋이 Resources 폴더 안에 있어야 하므로, Sprites 폴더에 Resources 폴더를 추가해 주고 스프라이트들을 넣어줬습니다

또한 Resources.Load<T>를 사용할 때 파일 확장자 (.png, .jpg 등)를 빼야 잘 작동하더라고요

임시로 만들어둔 items.json 파일을 확인해 보니 확장자도 붙어있어서 모두 제거해 줬습니다

이후 게임을 실행해 보면,

Image

이미지가 정상적으로 적용되는 것을 확인할 수 있네요!


추가로 ItemImporter 도구를 사용할 때 ItemDatabase에 수동으로 ScriptableObject를 매번 추가해 주는 게 은근히 귀찮아서, 코드를 추가해 업그레이드를 했습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static void ImportItems()
{
    string path = "Assets/Resources/items.json";
    if (!File.Exists(path))
    {
        Debug.LogError("JSON 파일을 찾을 수 없습니다");
        return;
    }

    string json = File.ReadAllText(path);
    ItemDataList itemList = JsonUtility.FromJson<ItemDataList>(json);

    const string databasePath = "Assets/SO/ItemDatabase.asset";
    ItemDatabase itemDatabase = AssetDatabase.LoadAssetAtPath<ItemDatabase>(databasePath);
    if (itemDatabase == null)
    {
        Debug.LogError("ItemDatabase를 찾을 수 없습니다");
        return;
    }

    itemDatabase.items.Clear();

    foreach (ItemDataJSON item in itemList.items)
    {
        UpgradeData newItem = ScriptableObject.CreateInstance<UpgradeData>();

        newItem.itemName = item.itemName;
        newItem.itemType = (UpgradeData.UpgradeType)Enum.Parse(typeof(UpgradeData.UpgradeType), item.itemType);
        newItem.itemRarity = (UpgradeData.Rarity)Enum.Parse(typeof(UpgradeData.Rarity), item.itemRarity);
        newItem.itemCategory = (UpgradeData.Category)Enum.Parse(typeof(UpgradeData.Category), item.itemCategory);
        newItem.itemImage = Resources.Load<Sprite>(item.imagePath);
        newItem.description = item.description;

        newItem.maxLevel = item.maxLevel;
        newItem.counts = item.counts;
        newItem.energyCosts = item.energyCosts;
        newItem.cooldownTime = item.cooldownTime;

        AssetDatabase.CreateAsset(newItem, $"Assets/SO/Items/{item.itemName}.asset");
        itemDatabase.items.Add(newItem);
    }

    EditorUtility.SetDirty(itemDatabase);
    AssetDatabase.SaveAssets();
    AssetDatabase.Refresh();
}

itemDatabase.items.Clear 함수로 기존 리스트에 있던 데이터를 제거하고, foreach 문 안에 itemDatabase.items.Add 함수를 통해 items 리스트에 아이템을 하나씩 추가합니다

이후 EditorUtility.SetDirty로 변경 사항을 감지하고, AssetDatabase.SaveAssets로 세이브까지 해주면 완성입니다

이제 버튼만 눌러주면 ScriptableObject 생성부터 데이터베이스에 저장까지 모든 게 한 방에 끝나네요 😍

너무나도 편리한 부분입니다

상점 기능 추가

다음으로 상점 기능을 몇 가지 추가했습니다

먼저 상점에서 사용할 플레이어의 돈을 관리하는 매니저 스크립트를 새로 만들어줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MoneyManager : MonoBehaviour
{
    [SerializeField] private int playerMoney;
    public TMP_Text playerMoneyText;
    public static MoneyManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
    }

    private void Start()
    {
        UpdatePlayerMoney();
    }

    private void UpdatePlayerMoney()
    {
        playerMoneyText.text = playerMoney.ToString();
    }

    public int GetMoney()
    {
        return playerMoney;
    }

    public void AddMoney(int amount)
    {
        playerMoney += amount;
        UpdatePlayerMoney();
    }

    public bool SubtractMoney(int amount)
    {
        if (amount > playerMoney)
        {
            Debug.Log("Not enough money");
            return false;
        }

        playerMoney -= amount;
        UpdatePlayerMoney();
        return true;
    }
}

SubtractMoney 메서드는 불리안을 리턴하여 다른 스크립트에서 사용할 때 if 문에서 사용하도록 해줍니다

다음으로 아이템 버튼을 눌렀을 때 중간의 빈 공간에 아이템 정보가 표시되어야 하므로, 이를 관리하는 스크립트를 작성해 줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class ItemDescription : MonoBehaviour
{
    [Header("Default Text")] public TMP_Text rarityDefaultText;
    public TMP_Text categoryDefaultText;
    public TMP_Text coolTimeDefaultText;

    [Header("Item Description")] public TMP_Text nameText;
    public TMP_Text rarityText;
    public TMP_Text categoryText;
    public TMP_Text descriptionText;
    public TMP_Text coolTimeText;

    [Header("Buy Values")] public Button buyButton;
    public TMP_Text itemPriceText;
    public int itemPrice;

    private void Start()
    {
        EraseDescPanel();
        buyButton.onClick.AddListener(() => BuyItem(itemPrice));
        itemPriceText.text = itemPrice.ToString();
    }

    public void UpdateItemDescPanel(string itemName, string rarity, string category, string description, string coolTime)
    {
        InitDescPanel();

        nameText.text = itemName;
        rarityText.text = rarity;
        categoryText.text = category;
        descriptionText.text = description;
        coolTimeText.text = $"{coolTime} s";
    }

    private void InitDescPanel()
    {
        rarityDefaultText.text = "Rarity";
        categoryDefaultText.text = "Type";
        coolTimeDefaultText.text = "Cooldown";
    }

    public void EraseDescPanel()
    {
        rarityDefaultText.text = "";
        categoryDefaultText.text = "";
        coolTimeDefaultText.text = "";
        nameText.text = "";
        rarityText.text = "";
        categoryText.text = "";
        descriptionText.text = "";
        coolTimeText.text = "";
    }

    private void BuyItem(int amount)
    {
        MoneyManager.Instance.SubtractMoney(amount);
    }
}

UpdateItemDescPanel 메서드로 아이템 정보 업데이트, InitDescPanel 메서드로 고정 텍스트 초기화, EraseDescPanel 메서드로 아이템 정보 삭제를 해줍니다

마지막으로 유니티에서 오브젝트들 연결을 해주고 실행해 보면,

Image

아이템 정보가 정상적으로 표시되는 것을 확인할 수 있네요

아이템 희귀도, 타임에 따라 색깔이 바뀌는 기능도 넣고 싶었는데, 당장 필요한 기능은 아니니 우선순위를 뒤로 미루겠습니다


이제 실제로 돈을 사용해서 아이템을 구매하거나 새로고침을 할 수 있도록 코드를 작성해 줍니다

ItemDescription 스크립트에서 itemPriceText를 적용하는 코드를 아까 작성해 두었고, 이제 새로고침 기능을 살짝 수정해 줍시다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class ItemShopUI : MonoBehaviour
{
    private const int DisplayItemCount = 4;

    [Header("Item Data")] public ItemDatabase itemDatabase;
    public ItemDescription itemDescription;
    public ItemSlot[] itemSlots;

    [Header("Shop Data")] public TMP_Text rerollPriceText;
    public int rerollPrice;

    private List<UpgradeData> _currentItems = new();

    private void Start()
    {
        InitShopData();
    }

    private void InitShopData()
    {
        _currentItems = GetRandomItems(DisplayItemCount);

        for (int i = 0; i < itemSlots.Length; i++)
            if (i < _currentItems.Count)
                itemSlots[i].SetItem(_currentItems[i]);
            else
                itemSlots[i].SetItem(null);

        itemDescription.EraseDescPanel();
        rerollPriceText.text = rerollPrice.ToString();
    }

    public void RerollShop()
    {
        if (UpdateRerollPrice())
            InitShopData();
    }

    private bool UpdateRerollPrice()
    {
        if (MoneyManager.Instance.SubtractMoney(rerollPrice))
        {
            rerollPrice++;
            rerollPriceText.text = rerollPrice.ToString();
            return true;
        }

        Debug.Log("Not enough money");
        return false;
    }
}

UpdateRerollPrice 메서드를 통해 버튼을 눌렀을 때 아이템이 새로고침 되며 새로고침 비용이 증가하도록 해줍니다

이전에 생성한 SubtractMoney 메서드의 불리안이 여기서 잘 쓰이네요

그럼 지금까지 만든 기능들을 테스트해 보겠습니다

Image

아이템 구매 및 새로고침 기능이 잘 작동하네요 👍

플레이어가 가진 돈이 구매 비용보다 적은 경우 작동하지 않는 모습까지 확인할 수 있습니다

이제 아이템별 비용을 가져와서 그 데이터를 버튼에 적용해 계산해야 하는데, 내일 이어서 구현해 볼게요

이후엔 아이템을 구매하여 플레이어가 장착하는 기능을 구현할 예정입니다

그럼 다음 포스팅에서 뵙겠습니다 🫠

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