Unity 내일배움캠프 TIL 0920 | DTO와 DataManager | 인벤토리 아이템 장착 및 개수 정보 보여주기

cheeseonrose·2023년 9월 20일
0

Unity 내일배움캠프

목록 보기
40/89
post-thumbnail

어제에 이어 과제 노동 하는 날..

개인 프로젝트

PlayerDTO

  • 어제 야근하면서 만들었던 PlayerDTO이다.
    왜 만들었냐면 Scene 이동 고려해서 데이터 값이 전달되도록 하고 싶은데 DontDestroyOnLoad를 써도 되지만 그냥 하는 김에 저장까지 하자! 하고 JSON 파싱해서 파일 읽기/쓰기를 하게 되었다는 구구절절한 이야기
  • 이름, 레벨, 돈, 체력, 공격력, 방어력, 피로도, 인벤토리 객체 정보를 갖는다.
  • 원래 체력, 공격력, 방어력만 있었는데 내 프로젝트 분위기가 때려부수는 느낌이 아니라서 귀엽?게? 피로도 추가해봄
    약간 노동하고 나면 피곤하니까...뭐 그런......
[Serializable]
public class PlayerDTO
{
    public string Name;
    public int Level;
    public int Gold;
    public int Health;
    public int Attack;
    public int Shield;
    public int Tiredness;
    public InventoryDTO Inventory;

    public PlayerDTO(string name)
    {
        Name = name;
        Level = 1;
        Gold = 20000;
        Health = 100;
        Attack = 10;
        Shield = 10;
        Tiredness = 0;
        Inventory = new InventoryDTO();
    }

    ...
    
    }
}

InventoryDTO

  • 플레이어의 인벤토리 정보를 갖는다.
  • TODO AddItem에서 아이템 타입이 ATTACK/SHIELD일 경우 개수 증가 X
[Serializable]
public class InventoryDTO
{
    public List<ItemDTO> ItemList;
 
    public InventoryDTO()
    {
        ItemList = new List<ItemDTO>();
    }

    public void AddItem(ItemDTO item)
    {
        ItemDTO curItem = ItemList.Find(x => x.Name == item.Name);
        if (curItem != null)
        {
            curItem.Count += 1;
        } else
        {
            ItemList.Add(item);
        }
    }

    public ItemDTO GetItem(int index)
    {
        return ItemList[index];
    }
}

ItemDTO

  • 이름, 타입(HEALTH/ATTACK/SHIELD), 설명, 값, 개수, 사용 여부, 이미지 파일 이름의 정보를 갖는다.
[Serializable]
public class ItemDTO 
{
    public string Name;
    public ItemType Type;
    public string Description;
    public int Value;
    public int Count;
    public bool IsUsed;
    public string Image;

    public ItemDTO(string name, ItemType type, string description, int value, int count, string image, bool isUsed)
    {
        Name = name;
        Type = type;
        Description = description;
        Value = value;
        Count = count;
        Image = image;
        IsUsed = isUsed;
    }
}

DataManager

  • JSON 파싱으로 Player 정보를 파일에 읽기/쓰기한다.
  • 전체 아이템 리스트를 DB로부터 읽어오며, 전체 아이템의 이미지 Sprite를 Load해와서 저장해놓는다. 이미지 Sprite는 아이템의 이름을 key 값으로 Dictionary에 저장된다.
    -> 이건 이미지 Sprite를 Load할 일이 많은데 DataManager가 아이템 Sprite를 미리 다 들고 있게 하면 Load 연산을 덜 할 것 같아서 이렇게 구현해봤다.
public class DataManager : MonoBehaviour
{
    const string ITEM_DB_PATH = "DB/ItemDatabase";
    const string PLAYER_DB_PATH = "/Resources/DB/PlayerDatabase";

    public static DataManager Instance;

    private TextAsset _itemDatabase;

    public List<ItemDTO> itemList;

    public Dictionary<string, Sprite> itemDicspriteDic;

    private void Awake()
    {
        Instance = this;
        _itemDatabase = (TextAsset)Resources.Load(ITEM_DB_PATH);
        InitItemList();
        InitItemSprite();
    }

    // DB로부터 전체 아이템 리스트를 받아옴
    private void InitItemList()
    {
        string[] line = _itemDatabase.text.Substring(0, _itemDatabase.text.Length - 1).Split('\n');
        for (int i = 0; i < line.Length; i++)
        {
            string[] row = line[i].Split('\t');
            itemList.Add(
                new ItemDTO(
                    row[0],
                    (ItemType)Enum.Parse(typeof(ItemType), row[1]), 
                    row[2],
                    int.Parse(row[3]),
                    int.Parse(row[4]),
                    row[5],
                    row[6] == "TRUE"
                ));
        }
    }

    private void InitItemSprite()
    {
        itemDicspriteDic = new Dictionary<string, Sprite>();
        foreach (ItemDTO item in itemList)
        {
            itemDicspriteDic.Add(item.Name, Resources.Load<Sprite>($"Images/{item.Image}"));
        }
    }

#nullable enable

    // DB에서 플레이어 정보를 읽어오고 없으면 null 리턴
    public PlayerDTO? GetPlayerInfo()
    {
        try
        {
            string jdata = File.ReadAllText(Application.dataPath + PLAYER_DB_PATH);
            Debug.Log(jdata);
            return JsonUtility.FromJson<PlayerDTO>(jdata);
        } catch
        {
            return null;
        }
    }

    // 플레이어 정보를 DB에 저장
    public void SavePlayerInfo(PlayerDTO player)
    {
        string jdata = JsonUtility.ToJson(player);
        File.WriteAllText(Application.dataPath + PLAYER_DB_PATH, jdata);
        Debug.Log("save " + jdata);
    }
}

인벤토리 아이템 장착 및 개수 정보

  • 아이템의 장착 여부 및 개수에 대한 정보를 아이템 오브젝트 하나하나가 각자 본인에게 맞는 값으로 설정하도록 하기 위해 스크롤 뷰 밑의 content에 스크립트를 추가했다.
  • UIInventoryItemContainer
    • 아이템 버튼 오른쪽 아래에 아이템 장착 여부 또는 개수 정보가 표시된다.
    • 아이템 타입을 검사해서 소모 아이템일 경우 개수를 표시하고, 10개를 넘으면 폰트 사이즈를 줄이고 10+로 표시한다.
    • 장착 아이템의 경우 장착했을 경우 E 표시를, 하지 않았을 경우 아무 표시도 하지 않는다.
    • 버튼에는 클릭 리스너를 연결하여 눌렀을 때 아이템을 사용 또는 장착/해제 하겠냐는 팝업이 뜨도록 해줬다.
      -> 이 부분은 event를 응용해서 수정될 예정
public class UIInventoryItemContainer : GameUIClass
{
    const int LARGE_FONT_SIZE = 35;
    const int SMALL_FONT_SIZE = 25;

    [SerializeField] private Image _inventoryItemImage;
    [SerializeField] private GameObject _inventoryItemStatusImage;
    [SerializeField] private TMP_Text _inventoryItemStatusText;

    private ItemDTO _item;

    // 인벤토리에서 아이템을 클릭하면 팝업이 나타나도록 클릭 리스너 연결
    private void Awake()
    {
        GetComponent<Button>().onClick.AddListener(OpenItemPopUp);
    }

    private void OpenItemPopUp()
    {
        UIPopUp uiPopUp = UIManager.Instance.GetUIComponent<UIPopUp>();
        uiPopUp.OpenUI();
        uiPopUp.SetPopUp(_item);
    }

    // 각각의 아이템 정보에 따라서 UI를 set
    public void SetItemInfo(ItemDTO item)
    {
        _item = item;

        _inventoryItemImage.sprite = DataManager.Instance.itemDicspriteDic[item.Name];

        switch (_item.Type)
        {
            // 소모 아이템일 경우 항상 개수 표시
            case ItemType.HEALTH:
                _inventoryItemStatusImage.SetActive(true);
                SetInventoryItemStatusText(true, _item.Count);
                break;
            // 장착 아이템일 경우 장착 상태일 때만 장착 표시
            case ItemType.ATTACK:
            case ItemType.SHIELD:
                if (_item.IsUsed)
                {
                    _inventoryItemStatusImage.SetActive(true);
                    SetInventoryItemStatusText(false, 0);
                }
                else
                {
                    _inventoryItemStatusImage.SetActive(false);
                }
                break;
        }
    }

    // 장착 아이템은 장착 상태일 경우 "E" 표시
    // 소모 아이템은 개수 표시 (10개 넘으면 10+로 표시)
    private void SetInventoryItemStatusText(bool isConsumable, int itemNum)
    {
        if (isConsumable)
        {
            _inventoryItemStatusText.text = (itemNum <= 10) ? itemNum.ToString() : "10+";
            _inventoryItemStatusText.fontSize = (itemNum <= 10) ? LARGE_FONT_SIZE : SMALL_FONT_SIZE;
        } else
        {
            _inventoryItemStatusText.text = "E";
            _inventoryItemStatusText.fontSize = LARGE_FONT_SIZE;
        }
    }
}

  • 그래서 이런 식으로 표시가 된다. 개수가 표시된 아이템은 소모 아이템, 아무것도 없거나 E가 표시된 아이템은 장착 아이템이다.

BUG LOG

  • 인벤토리의 스크롤 뷰 안의 아이템 오브젝트들이 제대로 보이지 않는 현상이 있었다.
    분명 프리팹 Instantiate도 잘 되어서 content 하위로 잘 들어갔는데 보이지 않는 것...
    • 이것저것 만져보다 깨달은 사실 .. scale 값이 0으로 설정되어 있었다...!
      근데 이상한건 프리팹의 scale은 정상적으로 설정되어 있다는 점
    • 같이 도와주시던 분이 원인을 찾으셨는데, 인벤토리 캔버스 자체의 scale이 0으로 설정되어 있었다!!!! 두둥 ㄴ(ㅇㅅㅇ)ㄱ
    • 1로 다시 바꿔주니까 잘 되긴 하는데 이상하게 캔버스 프리팹 수정할 때마다 다시 0으로 바뀌어서 귀찮아서 그냥 localScale 값을 1로 설정하는 코드를 Awake에 넣어주었다.
  • event를 사용할 때 구독하고 싶은데 구독이 안 된다 쾅쾅쾅
    • 현재 구독하는 로직이 Start 안에 있는데, 나는 처음에 보이면 안 되는 캔버스 UI 프리팹들은 다 active 상태를 false로 해놔서 정상적으로 작동하지 않았던 것 같다.
      구독하기도 전에 event가 실행되어서 event에 null만 있게 되었던 것
    • 프리팹들을 active true로 바꿔주고 Awake에서 CloseUI를 호출해줬더니 문제가 해결됐다!! 얏호

TODO

  • UI Event 로직 마저 작성
  • Inventory hover 이벤트 UI 구현
  • Inventory hover 이벤트 로직 작성
  • 닉네임 입력해서 player 정보 저장해보기 -> Scene 추가
  • GameScene에 아이템 뿌려놓은 맵 만들어서 아이템 먹으면 인벤토리에도 반영되게 해보기
  • StartScene....?
  • 상...점.....? ㅇㅅaㅇ
  • 고통받기
  • 끝없는 노동



전 언제쯤 욕심을 버릴 수 있나요

끗...

0개의 댓글