22.01.26 유니티와 JSON 연동해 데이터 불러오고 저장하기

2022. 1. 26. 12:44Unity3D/수업 내용

데이터 불러오기

Resources 폴더에 json파일을 갖다놓고 그 데이터를 유니티 런타임에서 불러오도록 한다.

json파일

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

public class MonsterData
{
    public int id;
    public int hp;
}

public class useJson : MonoBehaviour
{
    public static useJson instance;

    public GameInfo gi;

    private Dictionary<int, MonsterData> dictMonsterData = new Dictionary<int, MonsterData>();

    void Awake() //Start로 하면 이 데이터를 참조하는 다른 스크립트에서 에러 남
    {
        instance = this;
        TextAsset data = Resources.Load<TextAsset>("monster_data");
        string json = data.text;
        dictMonsterData = JsonConvert.DeserializeObject<MonsterData[]>(json).ToDictionary(x => x.id);
        gi.InitMonsterInfo();
    }

    public MonsterData GetMonsterData(int i)
    {
        return dictMonsterData[i];
    }

}

Resources.Load문장이 Resources 폴더에서 json파일을 불러오는 코드이다.

주의할 점이 저 불러오는 코드가 위치한 이벤트 함수는 Start()가 아니라 Awake()이어야 한다.

이유는 다른 스크립트에서도 이 useJson 스크립트가 불러온 데이터를 참조할 것인데,

그 다른 스크립트가 참조하는 코드가 위치한 이벤트 함수도 Start()라서

불러오는 것과 참조하는 것이 동시에 일어나서 에러가 나기 때문이다.

 

그래서 유니티 이벤트 함수의 생명 주기를 보면 알 수 있듯이

Awake()가 Start()보다 먼저 실행이 되므로 Start() 대신 Awake()를 쓴다.

 

참고로 유니티 Project 창 내의 Resources 폴더에 있는 것은 모두 File이자 Asset이다.

JSON파일도 TextAsset으로 분류되기 때문에 불러오는 타입도 TextAsset타입으로 캐스팅했다.

그 후 텍스트에셋의 문자열 데이터를 역직렬화하고 미리 만들어둔 딕셔너리에 데이터를 담았다.

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

public class Monster
{
    public int id;
    public int hp;

    public Monster(int id, int hp)
    {
        this.id = id;
        this.hp = hp;
    }
}

public class GameInfo
{
    public Monster monsterInfo;

    public void InitMonsterInfo()
    {
        int id = useJson.instance.GetMonsterData(0).id;
        int hp = useJson.instance.GetMonsterData(0).hp;
        monsterInfo = new Monster(id, hp);
    }
}

 그리고 useJson클래스에서 InitMonsterInfo() 메서드를 호출하여

GameInfo를 초기화시켜 useJson 클래스가 갖고 있게 한다.

 

그 결과 useJson 클래스는

속성이 할당된 Monster객체를 참조하는 변수 monsterInfo를 갖고 있는

GameInfo 를 갖고 있게 된다.

 

그러면 이 GameInfo를 파일로 저장해보자.

 

 

데이터 저장하기

 

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

public class MonsterData
{
    public int id;
    public int hp;
}

public class useJson : MonoBehaviour
{
    public static useJson instance;

    public GameInfo gi;

    private Dictionary<int, MonsterData> dictMonsterData = new Dictionary<int, MonsterData>();

    void Awake()
    {
        instance = this;
        TextAsset data = Resources.Load<TextAsset>("monster_data");
        string json = data.text;
        Debug.Log(json);
        dictMonsterData = JsonConvert.DeserializeObject<MonsterData[]>(json).ToDictionary(x => x.id);
        Debug.Log(dictMonsterData[0].hp);
        gi.InitMonsterInfo();
    }

    public void SaveData()
    {
        Debug.Log("데이터를 저장했습니다.");
        string saveJson = JsonConvert.SerializeObject(this.gi);
        File.WriteAllText("Assets/Resources/monster_data3.json", saveJson);
    }

    public MonsterData GetMonsterData(int i)
    {
        return dictMonsterData[i];
    }

}

SaveData() 에서 Resources 폴더에 JSON파일을 저장한다.

이때는 File.WriteAllText를 써서 이름 뒤에 "3"만 붙여 저장했다.

저장됨.

잘 저장 된 것이 확인 됐으면 그 저장한 GameInfo의 JSON파일을 불러와보자. 

 

 

저장한 GameInfo 객체의 데이터 불러오기

 

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

public class MonsterData
{
    public int id;
    public int hp;
}

public class useJson : MonoBehaviour
{
    public static useJson instance;

    public GameInfo gi;

    private Dictionary<int, MonsterData> dictMonsterData = new Dictionary<int, MonsterData>();

    void Awake()
    {
        instance = this;
        TextAsset data2 = Resources.Load<TextAsset>("monster_data3");
        string json2 = data2.text;
        gi = new GameInfo();
        gi = JsonConvert.DeserializeObject<GameInfo>(json2);
    }

    public void SaveData()
    {
        Debug.Log("데이터를 저장했습니다.");
        string saveJson = JsonConvert.SerializeObject(this.gi);
        File.WriteAllText("Assets/Resources/monster_data3.json", saveJson);
    }

    public MonsterData GetMonsterData(int i)
    {
        return dictMonsterData[i];
    }

}

딕셔너리에 데이터를 넣는 사전작업은 건너뛰고 저장한 GameInfo객체의 데이터를

잘 불러오는지만 확인하기 위해 Awake()에서 코드를 바꿨다.

불러온 GameInfo 객체를 다른 스크립트에서 참조해보고 정상적으로 불러왔는지 확인해보자.

 

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

public class MainSCR : MonoBehaviour
{
    [SerializeField] private Button saveBtn;
    private GameObject[] enemies;

    void Start()
    {
        enemies = GameObject.FindGameObjectsWithTag("Enemy");
        saveBtn.onClick.AddListener(LetDataSaved);
        EnemiesInit();
    }

    void LetDataSaved()
    {
        useJson.instance.SaveData();
    }

    void EnemiesInit()
    {
        foreach(GameObject em in enemies)
        {
            Monster monsterInfo = useJson.instance.gi.monsterInfo;
            em.GetComponent<EnemyControl>().LoadStatData(monsterInfo.id, monsterInfo.hp);
        }
    }
}

메인 스크립트에서 EnemiesInit()을 통해 게임씬에 있는 모든 enemy오브젝트들에게

불러온 GameInfo객체의 enemy정보를 우겨넣는다.

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

public class EnemyControl : MonoBehaviour
{
    [SerializeField] private int id;
    [SerializeField] private int hp;

    public void LoadStatData(int id, int hp)
    {
        this.id = id;
        this.hp = hp;
    }

    public void DisplayStat()
    {
        Debug.Log(this.hp + ", " + this.id);
    }
}

참고로 원래 몬스터의 hp는 8이고, 불러온 GameInfo객체의 담은 몬스터 hp 정보는 16이다.

프로그램을 실행시켜서 hp가 16인지 확인해보자.

 

정상