EasyCastleUNITY

골드 상점 GUI 본문

유니티 심화

골드 상점 GUI

EasyCastleT 2023. 9. 9. 15:22

정적 스크롤 뷰를 사용한 골드를 파는 상점 UI를 만들어 보려고 한다. 

먼저 기본 바탕인 정적 스크롤 뷰를 만들고 그 안에 각각의 셀들을 만들려고 한다.

가이드 이미지와 스크롤뷰의 영역을 잡은 모습
계층구조
스크롤뷰 생성 완료

scrollview에 Scroll Rect 컴포넌트 부여하고, 이 컴포넌트의 content 속성의 만들어둔 content를 할당한다. 

content의 인스펙터

이 다음에 scrollview에 Mask 컴포넌트를 할당한다. 

이런 과정을 거치면 정적 스크롤뷰가 완성된다. 

 

이제 content의 자식으로 들어갈 UIGoldCell을 만들어 보겠다. 

cell 완성본
cell의 구조

guide 이미지를 바탕으로 필요한 리소스를 가져와 만들었다. 

이름과 구매하는 골드의 양을 표시하는 텍스트의 색은 각각

이름 텍스트 색상 BEB5B6 , 골드 텍스트 색상 FFBC12 이다 (헥사코드)

각각 들어갈 이미지들은 전부 Native Size를 사용한다. 

씬 구조

이러한 구조로 만들기로 하였다.

제일 상위가 UIMain이고 UIPageGoldShop->UIScrollView -> UIGoldCell 순이다. 

따라서 필요한 스크립트는 총 4개이다. 

데이터 테이블

데이터 연동을 통해 만들것이므로 데이터 테이블이 필요하다.

만든 데이터 테이블

이름과 골드 이미지 변경을 위한 스프라이트 이름,  골드의 양, 가격 등의 정보를 저장하도록 만들었다. 

 

만든 json 파일 

[
  {
    "id": "10000",
    "name": "Tiny of Gold",
    "sprite_name": "set_icon_gold_0",
    "gold_amount": "240",
    "price": "1.99"
  },
  {
    "id": "10001",
    "name": "Fistful of Gold",
    "sprite_name": "set_icon_gold_1",
    "gold_amount": "650",
    "price": "4.99"
  },
  {
    "id": "10002",
    "name": "Pouch of Gold",
    "sprite_name": "set_icon_gold_2",
    "gold_amount": "1500",
    "price": "9.99"
  },
  {
    "id": "10003",
    "name": "Box of Gold",
    "sprite_name": "set_icon_gold_3",
    "gold_amount": "3000",
    "price": "24.99"
  },
  {
    "id": "10004",
    "name": "Chest of Gold",
    "sprite_name": "set_icon_gold_4",
    "gold_amount": "8000",
    "price": "59.99"
  },
  {
    "id": "10005",
    "name": "Vault of Gold",
    "sprite_name": "set_icon_gold_5",
    "gold_amount": "15000",
    "price": "119.99"
  }
]

DataManager

using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class DataManager 
{
    
    public static readonly DataManager instance = new DataManager(); //싱글톤 
    //상자 정보를 저장할 사전 
    private Dictionary<int, ChestData> chestDataDics = new Dictionary<int, ChestData>();
    //미션 정보를 저장할 사전
    private Dictionary<int, MissionData> missionDataDic = new Dictionary<int, MissionData>();
    //골드 정보를 저장할 사전 
    private Dictionary<int, GoldData> goldDataDic = new Dictionary<int, GoldData>();
    //생성자
    private DataManager()
    {

    }

    public void LoadChestDatas()
    {
        TextAsset aseet = Resources.Load<TextAsset>("chest_data"); //json은 안 붙임

        //역직렬화
        ChestData[] chestDatas = JsonConvert.DeserializeObject<ChestData[]>(aseet.text);
        foreach(var chestData in chestDatas)
        {
            this.chestDataDics.Add(chestData.id, chestData);
        }
        Debug.Log("상자 로드 완료");
    }

    public void LoadMissionDatas()
    {
        TextAsset asset = Resources.Load<TextAsset>("mission_data");
        //역직렬화 
        MissionData[] missionDatas = JsonConvert.DeserializeObject<MissionData[]>(asset.text);
        //사전 등록
        foreach(var missionData in missionDatas)
        {
            this.missionDataDic.Add(missionData.id, missionData);
        }
        Debug.Log("미션 로드 완료");
    }

    public void LoadGoldDatas()
    {
        TextAsset asset = Resources.Load<TextAsset>("gold_data");
        //역직렬화
        GoldData[] goldDatas = JsonConvert.DeserializeObject<GoldData[]>(asset.text);
        //사전 등록
        foreach(var goldData in goldDatas)
        {
            this.goldDataDic.Add(goldData.id, goldData);
        }
        Debug.LogFormat("<color=yellow>Gold Count: {0}</color>",this.goldDataDic.Count);
        Debug.Log("골드 로드 완료");
    }
    public List<ChestData> GetChestDatas()
    {
        return this.chestDataDics.Values.ToList();
    }

    public List<MissionData> GetMissionDatas()
    {
        return this.missionDataDic.Values.ToList();
    }

    public List<GoldData> GetGoldDatas()
    {
        return this.goldDataDic.Values.ToList();
    }
}

GoldData -> 데이터 매핑 클래스 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GoldData 
{
    //id  name   sprite_name gold_amount   price
    //int string string      int           float
    public int id;
    public string name;
    public string sprite_name;
    public int gold_amount;
    public float price;
}

실행결과 및 스크립트

사용한 아틀라스

UIGoldShopMain

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIGoldShopMain : MonoBehaviour
{
    [SerializeField]
    private UIPageGoldShop uIPageGoldShop;
    // Start is called before the first frame update
    void Start()
    {
        DataManager.instance.LoadGoldDatas();
        List<GoldData> goldDatas = DataManager.instance.GetGoldDatas();
        this.uIPageGoldShop.Init(goldDatas);
    }

}

UIPageGoldShop

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIPageGoldShop : MonoBehaviour
{
    [SerializeField]
    private UIGoldScrollview goldScrollview;

    public void Init(List<GoldData> goldDatas)
    {
        foreach(GoldData data in goldDatas)
        {
            Debug.LogFormat("<color=yellow>{0},{1},{2},{3},{4}</color>",
            data.id, data.name, data.sprite_name, data.gold_amount, data.price);
            this.goldScrollview.Init(data);
            //클로져 
            int goldAmount = data.gold_amount;
            float goldPrice = data.price; 
        }
        
    }

}

UIGoldScrollview

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;

public class UIGoldScrollview : MonoBehaviour
{
    [SerializeField]
    private GameObject goldCellPrefab;
    [SerializeField]
    private SpriteAtlas goldAtlas;
    
    public void Init(GoldData data)
    {
        GameObject context = GameObject.FindGameObjectWithTag("Content");
        GameObject go = Instantiate(this.goldCellPrefab, context.transform);
        UIGoldCell gold = go.GetComponent<UIGoldCell>();
        this.SetGoldCellInfo(gold, data);
        gold.Init();
        gold.onBuyButtonClick = () => {
            Debug.LogFormat("<color=yellow>골드 양:{0}, 가격:{1}</color>",
                    data.gold_amount, data.price);
        };
    }

    private void SetGoldCellInfo(UIGoldCell cell, GoldData data)
    {
        cell.Id = data.id;
        cell.Name = data.name;
        cell.Sprite_name = data.sprite_name;
        cell.GoldSprite = this.goldAtlas.GetSprite(data.sprite_name);
        cell.Gold_amount = data.gold_amount;
        cell.Price = data.price;
    }
}

UIGoldCell

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class UIGoldCell : MonoBehaviour
{
    public System.Action onBuyButtonClick;
    #region Info
    private int id;
    private new string name;
    private string sprite_name;
    private int gold_amount;
    private float price;
    private Sprite goldSprite;

    public string Name { get => name; set => name = value; }
    public int Id { get => id; set => id = value; }
    public string Sprite_name { get => sprite_name; set => sprite_name = value; }
    public int Gold_amount { get => gold_amount; set => gold_amount = value; }
    public float Price { get => price; set => price = value; }
    public Sprite GoldSprite { get => goldSprite; set => goldSprite = value; }
    #endregion
    [SerializeField] private TMP_Text txtName;
    [SerializeField] private TMP_Text txtGoldAmount;
    [SerializeField] private TMP_Text txtPrice;
    [SerializeField] private Image goldImage;
    [SerializeField] private Button btnBuy;

    public void Init()
    {
        this.txtName.text = this.name;
        this.txtGoldAmount.text = string.Format("{0} Gold", this.gold_amount);
        this.txtPrice.text = string.Format("US ${0}", this.price);
        this.SetImage(this.goldImage, this.goldSprite);
        this.btnBuy.onClick.AddListener(() => {
            this.onBuyButtonClick();
            Debug.LogFormat("골드 양:{0}, 가격:{1}", this.gold_amount, this.price);
        });
        
    }

    private void SetImage(Image image, Sprite sprite)
    {
        image.sprite = sprite;
        image.SetNativeSize();
    }
}

이미지 위치가 이상하다.

기준이 되는 가이드 이미지가 모두 Native Size여서 신경쓰지 않았었지만, 

이미지의 위치 때문인지 이상하게 나오게 된다. 

그래서 데이터 테이블의 이미지의 위치 정보를 추가해보려고 한다. 

이미지 위치를 맞추기 위해 새로운 데이터 추가

변경된 json파일

[
  {
    "id": "10000",
    "name": "Tiny of Gold",
    "sprite_name": "set_icon_gold_0",
    "gold_amount": "240",
    "price": "1.99",
    "posX": "0",
    "posY": "100"
  },
  {
    "id": "10001",
    "name": "Fistful of Gold",
    "sprite_name": "set_icon_gold_1",
    "gold_amount": "650",
    "price": "4.99",
    "posX": "0",
    "posY": "117"
  },
  {
    "id": "10002",
    "name": "Pouch of Gold",
    "sprite_name": "set_icon_gold_2",
    "gold_amount": "1500",
    "price": "9.99",
    "posX": "-45",
    "posY": "80"
  },
  {
    "id": "10003",
    "name": "Box of Gold",
    "sprite_name": "set_icon_gold_3",
    "gold_amount": "3000",
    "price": "24.99",
    "posX": "0",
    "posY": "100"
  },
  {
    "id": "10004",
    "name": "Chest of Gold",
    "sprite_name": "set_icon_gold_4",
    "gold_amount": "8000",
    "price": "59.99",
    "posX": "0",
    "posY": "18"
  },
  {
    "id": "10005",
    "name": "Vault of Gold",
    "sprite_name": "set_icon_gold_5",
    "gold_amount": "15000",
    "price": "119.99",
    "posX": "0",
    "posY": "21"
  }
]

스크립트 변경되거나 추가된 부분 

UIGoldScrollView의 SetGoldCellInfo 메서드
UIGoldCell에 속성 추가
매핑 클래스 posX, posY 추가
UIGoldCell에서 위치 설정 추가

실행결과

그런데 이렇게 하니 이미지가 안 보이는 결과가 나왔다. 

 

그래서 어떤 이유로 이런 문제가 생기는지 확인 하니 

위에 메서드 SetImage에서 그냥 position을 받은 것이 문제였다. 

데이터로 저장한 json 파일의 정보는 앵커를 기준으로 위치를 계산한 것이다. 

그런데 그냥 월드 좌표 기준으로 위치를 지정해버리니, 이미지가 다른 곳으로 날아가 안보이는 결과가 나오게 된 것이다. 

 

따라서 그냥 position이 아닌 anchoredPosition을 사용하면 된다는 것을 알았다. 

https://keykat7.blogspot.com/2020/01/unity-rectTransform.html

 

[Unity3D] UI RectTransform posX, posY 값 조절

알고리즘, 코딩 테스트, C++, java, 파이썬, AI, 백준, 기업 코딩 테스트, 자료구조, 프로젝트, codeforces, unity, android

keykat7.blogspot.com

이 블로그를 참고했다. 

수정한 메서드
정상적으로 나오는 결과

전체결과

이렇게 골드 상점에 대한 제작을 끝냈다.