Unity에서 JSON 데이터를 다루는 방법은 여러가지가 있지만 

가장 널리 쓰이는 방식은 다음 두 가지를 많이 사용합니다.

하나는 JsonUtility(Unity 내장 방식), 다른 하나는 Newtonsoft.Json(외부 라이브러리) 입니다.

 

이 글에서는 이 두 가지 방법을 비교해보고,

외부에서 JSON 파일을 불러와 직접 파싱하는 과정을 알아보겠습니다.

JsonUtility

JsonUtility메서드가 유니티에 내장되어 있으므로 별도의 설치 없이 그대로 사용합니다.

// 직렬화/역직렬화 할 요소를 별도의 구조로 만듬(구조체 또는 클래스)
[System.Serializable]
public class Item
{
    public int id;
    public string name;
    public string description;
}

[System.Serializable]
public class ItemDataList
{
    public List<Item> Item;
}

 

데이터를 직렬화하거나 역직렬화할 땐, 먼저 이렇게 클래스를 만듭니다.
Item 클래스는 개별 아이템 하나의 구조를 정의하고,
ItemDataList 클래스는 그걸 리스트 형태로 담는 역할을 합니다.

 

여기서 중요한 건 아래처럼 JSON에 "Item"이라는 키가 있을 경우,
ItemDataList 클래스 안에서도 Item이라는 이름(대소문자 포함)을 정확히 맞춰줘야

JsonUtility가 정상적으로 매핑해줍니다.

예를 들어 JSON이 이렇게 생겼다면:

{
  "Item": [
    { "id": 1, "name": "단검", "description": "짧은 단검" }
  ]
}

 

클래스 내부 필드도 정확히 "Item"이라는 이름이어야 매칭됩니다.

public class MonsterLoader : MonoBehaviour
{
    public static MonsterLoader Instance;
    public List<Monster> monsterList;
    void Start()
    {
        TextAsset jsonData = Resources.Load<TextAsset>("Monster");
        ItemDataList wrapper = JsonUtility.FromJson<ItemDataList>(jsonData.text);
        // 역직렬화된 결과에서 실제 몬스터 리스트를 추출하여 monsterList 변수에 저장
        // 여기서 wrapper.Monster는 JSON의 "Monster" 배열을 파싱한 결과이기 때문에,
        // 이걸 실제 게임에서 사용할 변수 monsterList에 저장하는 것
        monsterList = wrapper.Monster;
    }
}

[System.Serializable]
public class Monster
{
    public string MonsterID;
    public string Name;
    public string Description;
    public int Attack;
    public float AttackMul;
    public int MaxHP;
    public float MaxHPMul;
    public int AttackRange;
    public float AttackRangeMul;
    public float AttackSpeed;
    public float MoveSpeed;
    public int MinExp;
    public int MaxExp;
    public int[] DropItem;
}

[System.Serializable]
public class MonsterList
{
    public List<Monster> Monster;
}

 

Resources.Load를 통해 JSON 파일을 불러온 뒤

JsonUtility.FromJson 메서드를 사용해서 JSON 데이터를 역직렬화 합니다.

 

이때 JSON 구조에 맞게 만들어둔 ItemDataList 클래스를 통해 전체 데이터를 감싼 후,
그 안에서 실제 아이템 리스트(List<Item>)만 꺼내 itemList 변수에 복사해 두는 방식

 

[실제 사용하기]

더보기

리스트를 사용해서 적용

public class MonsterLoader : MonoBehaviour
{
    public static MonsterLoader Instance;
    public List<Monster> monsterList;
    
    void Start()
    {
        TextAsset jsonData = Resources.Load<TextAsset>("Monster");
        MonsterList wrapper = JsonUtility.FromJson<MonsterList>(jsonData.text);
        monsterList = wrapper.Monster;
    }
}

public class KnifeAttack : MonoBehaviour
{
    public string monsterId = "M0001";
    [SerializeField] private float attackRange;

    private void Start()
    {
        foreach (var m in MonsterLoader.Instance.monsterList)
        {
            if (m.MonsterID == monsterId)
            {
                attackRange = m.AttackRange + m.AttackRangeMul;
            }
        }
    }

    public void Attack()
    {
        Collider2D[] targets = Physics2D.OverlapCircleAll(transform.position, attackRange);
    }
}

 

딕셔너리를 사용해서 적용

public class MonsterLoader : MonoBehaviour
{
    public static MonsterLoader Instance;
    public List<Monster> monsterList;
    
    // 키(key)를 기반으로 값을 빠르게 조회하기(Tkey(MonsterID : M0001), Tvalue(Monster 객체))
    public Dictionary<string, Monster> monsterDict = new Dictionary<string, Monster>();
    
    void Start()
    {
        TextAsset jsonData = Resources.Load<TextAsset>("Monster");
        MonsterList wrapper = JsonUtility.FromJson<MonsterList>(jsonData.text);
        monsterList = wrapper.Monster;

        // JSON에서 로드된 모든 몬스터를 순회하면서
        // 각 몬스터의 MonsterID를 key로, Monster 객체를 value로 딕셔너리에 저장
        foreach (var monster in monsterList)
        {
            monsterDict[monster.MonsterID] = monster;
        }
    }
}


public class KnifeAttack : MonoBehaviour
{
    public string monsterId = "M0001";
    [SerializeField] private float attackRange;

    private void Start()
    {
        // monsterId에 해당하는 몬스터 데이터를 딕셔너리에서 가져옴
        // 찾으면 monster에 값이 들어가고 true 반환, 못 찾으면 false.
        if (MonsterLoader.Instance.monsterDict.TryGetValue(monsterId, out Monster monster))
        {
            attackRange = monster.AttackRange + monster.AttackRangeMul;
        }
    }

    public void Attack()
    {
        Collider2D[] targets = Physics2D.OverlapCircleAll(transform.position, attackRange);
    }
}

Newtonsoft.Json

Unity는 기본적으로 Newtonsoft.Json을 지원하지 않기 때문에

JsonUtility와 다르게 별도의 설치 과정이 존재합니다.

[Windows] → [Package Manager]에서 [Add package by name...] 클릭

 

com.unity.nuget.newtonsoft-json 를 입력 후 Version(선택) 입력[Add]

[설치가 완료된 모습]

 

using Newtonsoft.Json;

 

설치가 완료되면 using Newtonsoft.Json 네임스페이스를 사용할 수 있습니다.

using Newtonsoft.Json;

public class ItemLoaderNewtonsoft : MonoBehaviour
{
    public List<Item> itemList;

    void Start()
    {
        TextAsset jsonData = Resources.Load<TextAsset>("Item"); // Resources 폴더 안의 Item.json
        ItemDataList wrapper = JsonConvert.DeserializeObject<ItemDataList>(jsonData.text);
        itemList = wrapper.Items;
    }

    public void UseItem()
    {
        foreach (Item item in itemList)
        {
            Debug.Log(item.id);
        }
    }
}

public class Item
{
    [JsonProperty("id")]
    public int id;

    [JsonProperty("name")]
    public string name;

    [JsonProperty("description")]
    public string description;
}

public class ItemDataList
{
    [JsonProperty("Item")] // JSON의 "Item" 키와 정확히 매핑
    public List<Item> Items;
}

 

Resources.Load를 통해 JSON 파일을 불러온 뒤

JsonConvert.DeserializeObject 메서드를 사용해서 JSON 데이터를 역직렬화 합니다.

 

이때 JsonUtility와 다르게 JsonProperty(변수명이 다를때 치환)를 이용해서
JSON의 키 이름(예: "Item", "id", "name")과 C# 클래스의 변수명이 달라도 정확히 매핑할 수 있습니다.

역직렬화 결과로 감싸진 클래스(ItemDataList) 안의 Items 리스트를 꺼내서
itemList 변수에 저장하면 게임에서 아이템 정보를 자유롭게 쓸 수 있습니다.

+ Recent posts