22.01.27 런타임마다 외부의 데이터로 갱신하는 법

2022. 1. 27. 22:54Unity3D/수업 내용

오늘은 위의 사진처럼 실행할 때마다 외부에서 가져온 데이터를, 

오브젝트의 멤버로 할당하는 법을 실습해보겠다.

 

먼저, Project 내의 Resources 폴더에 갱신할 데이터 대상인 JSON파일을 저장해주어야 한다.

그리고 몬스터를 가장한 더미오브젝트 프리팹도 저 곳에 놔둔다.

 

그 다음, 아래와 같이 스크립트를 작성해준다.

먼저 핵심이 되는 DataManage 스크립트.

모노를 상속받지 않고 static이며 GetInstance()로만 static 인스턴스를 참조할 수 있다.

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

public class DataManage
{
    static DataManage instance;
    Dictionary<int, MonsterData> dictMonsterData;
    Dictionary<int, GameObject> dictMonsterGameObjects;

    DataManage()
    {
        this.dictMonsterData = new Dictionary<int, MonsterData>();
        this.dictMonsterGameObjects = new Dictionary<int, GameObject>();
    }

    public static DataManage GetInstance()
    {
        if(DataManage.instance == null)
        {
            DataManage.instance = new DataManage();
        }
        return DataManage.instance;
    }

    public GameObject GetMonsterObjectByName(string name)
    {
        foreach(KeyValuePair<int, MonsterData> data in dictMonsterData)
        {
            if(data.Value.name == name)
            {
                return FindMonsterObjectByID(data.Value.id);
            }
        }
        return null;
    }

    GameObject FindMonsterObjectByID(int id)
    {
        foreach(KeyValuePair<int, GameObject> obj in dictMonsterGameObjects)
        {
            if(obj.Key == id)
            {
                return obj.Value;
            }
        }
        return null;
    }

    public void LoadMonsterData()
    {
        string path = "Monster_data";
        string json = Resources.Load<TextAsset>(path).text;
        this.dictMonsterData = JsonConvert.DeserializeObject<MonsterData[]>(json).ToDictionary(x => x.id);
        LoadMonsterPrefabs();
    }

    void LoadMonsterPrefabs()
    {
        foreach(KeyValuePair<int, MonsterData> data in dictMonsterData)
        {
            GameObject go = Resources.Load<GameObject>(data.Value.name);
            go.GetComponent<Monster>().InitMonsterStat(data.Value.id, data.Value.name, data.Value.hp);
            dictMonsterGameObjects.Add(data.Key, go);
        }
    }
}

데이터 매니지 스크립트의 흐름은 이렇다.

 

1) 게임매니져에서 자신을 GetInstance()로 부르고, LoadMonsterData()를 호출받으면

Resources 폴더에서 Monster_data 텍스트에셋을 찾고 읽어들인다.

그 텍스트들은 MonsterData 매핑클래스 타입으로 역직렬화되어 배열이 되었다가 

MonsterData의 id 속성을 Key로 삼아 미리 지정해둔 MonsterData 딕셔너리로 추가되어 들어간다.

 

2) 그 후 LoadMonsterPrefabs()을 호출하여 MonsterData딕셔너리를 반복문으로 순회시킨다.

반복문 안에서는 Resources 폴더에 있던 더미오브젝트 프리팹을

MonsterData의 name 속성을 이용해 가져온다.

오브젝트를 가져왔으면, 그 오브젝트에 미리 부착돼 있던 Monster 클래스의 InitMonsterStat()을 통해

Monster의 멤버값을 외부에서 가져온 데이터인 MonsterData 딕셔너리의 Value값과 동일하게 할당해준다.

 

 

간단하게,

 

외부 데이터를 읽어들이고

 

그 데이터로 딕셔너리를 만들고

 

그 딕셔너리로 몬스터의 멤버값을 할당해준다.

 


사실 dictMonsterGameObjects 딕셔너리는 없어도 된다.

게임매니져에서 몬스터를 만들 때마다 GetMonsterData(int id) 같은 메서드를 통해

몬스터를 생성하고 그때 그때 멤버값을 할당해줘도 충분하지만

 

그냥 DataManage에서 처음 딱 한 번만 사전에 다 담아버린 다음,

그때 그때 꺼내쓰기만 하고 싶었다.

 

이 아래는 나머지 스크립트.

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

public class GameManager : MonoBehaviour
{
    DataManage dm;

    void Start()
    {
        dm = DataManage.GetInstance();
        dm.LoadMonsterData();
        GameObject go = Instantiate(dm.GetMonsterObjectByName("Slime"));
        go.transform.position = new Vector3(0f, 2f, 0f);
        GameObject go2 = Instantiate(dm.GetMonsterObjectByName("Rammus"));
        go2.transform.position = new Vector3(3f, 2f, 0f);
    }
}
public class MonsterData
{
    public int id;
    public string name;
    public int hp;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Monster : MonoBehaviour
{
    [HideInInspector] public int id;
    public string monName;
    public int hp;

    public void InitMonsterStat(int id, string name, int hp)
    {
        this.id = id;
        this.monName = name;
        this.hp = hp;
    }

    void Start()
    {
        Debug.Log(this.id + "/" + this.monName + "/" + this.hp);
    }
}

그리고, Monster의 멤버값 또한 public으로 선언해주어야

정상적으로 InitMonsterStat()을 통해 값을 할당받을 수 있다.

이유는 잘 모르겠다.