[Jem Clash] 개발일지 #8 - 이미지 버그 수정, 상점 기능 추가
이미지 버그 해결 + ItemImporter 업그레이드
지난 포스팅 때 아이템 이미지가 제대로 보이지 않는 버그가 있어서, 먼저 수정하고 가주겠습니다
경로 설정을 제대로 했음에도 버그가 발생해서 구글링을 해보았습니다
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 파일을 확인해 보니 확장자도 붙어있어서 모두 제거해 줬습니다
이후 게임을 실행해 보면,
이미지가 정상적으로 적용되는 것을 확인할 수 있네요!
추가로 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 메서드로 아이템 정보 삭제를 해줍니다
마지막으로 유니티에서 오브젝트들 연결을 해주고 실행해 보면,
아이템 정보가 정상적으로 표시되는 것을 확인할 수 있네요
아이템 희귀도, 타임에 따라 색깔이 바뀌는 기능도 넣고 싶었는데, 당장 필요한 기능은 아니니 우선순위를 뒤로 미루겠습니다
이제 실제로 돈을 사용해서 아이템을 구매하거나 새로고침을 할 수 있도록 코드를 작성해 줍니다
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 메서드의 불리안이 여기서 잘 쓰이네요
그럼 지금까지 만든 기능들을 테스트해 보겠습니다
아이템 구매 및 새로고침 기능이 잘 작동하네요 👍
플레이어가 가진 돈이 구매 비용보다 적은 경우 작동하지 않는 모습까지 확인할 수 있습니다
이제 아이템별 비용을 가져와서 그 데이터를 버튼에 적용해 계산해야 하는데, 내일 이어서 구현해 볼게요
이후엔 아이템을 구매하여 플레이어가 장착하는 기능을 구현할 예정입니다
그럼 다음 포스팅에서 뵙겠습니다 🫠