Technology/Computer

Unity에서 SO와 JSON의 동시 활용 (퀘스트 만들때)

re-moon 2024. 12. 23. 12:44

ScriptableObject(SO)와 JSON을 조합해서 퀘스트 데이터를 관리하면, SO의 편리한 관리 기능JSON의 동적 업데이트를 동시에 활용할 수 있습니다. 예를 들어, 퀘스트 데이터를 외부 JSON 파일로 저장해 두고, 이 데이터를 SO로 불러와 사용하는 방법을 구현할 수 있습니다.


1. JSON과 ScriptableObject 조합: 기본 개념

  • ScriptableObject: 게임이 실행되는 동안 퀘스트 데이터를 메모리에서 관리.
  • JSON: 퀘스트 데이터를 저장하고 필요할 때 업데이트하거나 로드.
  • 병합 방식: JSON 데이터를 SO에 반영하거나 SO 데이터를 JSON 파일로 저장.

2. JSON 파일 구조

JSON 파일을 다음과 같이 설계합니다. 퀘스트 데이터를 리스트 형태로 관리합니다.

[
    {
        "questId": "quest_001",
        "title": "Collect Apples",
        "description": "Gather 5 apples from the forest.",
        "isCompleted": false,
        "tasks": [
            {
                "description": "Collect 5 apples.",
                "isCompleted": false
            }
        ]
    },
    {
        "questId": "quest_002",
        "title": "Talk to Elder",
        "description": "Speak with the village elder about the forest.",
        "isCompleted": false,
        "tasks": [
            {
                "description": "Talk to Elder Mark.",
                "isCompleted": false
            }
        ]
    }
]

3. JSON 데이터를 ScriptableObject로 병합하기

ScriptableObject 클래스 수정

SO에서 JSON 데이터와 매핑될 필드를 정의합니다.

using UnityEngine;
using System.Collections.Generic;

[CreateAssetMenu(fileName = "NewQuest", menuName = "Quest System/Quest")]
public class QuestSO : ScriptableObject
{
    public string questId;
    public string title;
    [TextArea] public string description;
    public bool isCompleted;
    public List<QuestTask> tasks;
}

[System.Serializable]
public class QuestTask
{
    public string description;
    public bool isCompleted;
}

JSON 파일을 ScriptableObject로 로드하는 스크립트

JSON 데이터를 ScriptableObject에 적용하려면 아래처럼 **JsonUtility.FromJson**를 사용합니다.

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

public class QuestLoader : MonoBehaviour
{
    public List<QuestSO> questTemplates; // ScriptableObjects 템플릿 리스트
    private string jsonFilePath;

    void Start()
    {
        jsonFilePath = Path.Combine(Application.persistentDataPath, "quests.json");
        LoadQuestsFromJson();
    }

    void LoadQuestsFromJson()
    {
        if (File.Exists(jsonFilePath))
        {
            string jsonData = File.ReadAllText(jsonFilePath);
            List<QuestData> questList = JsonUtility.FromJson<QuestDataList>(jsonData).quests;

            // JSON 데이터를 ScriptableObject로 병합
            for (int i = 0; i < questList.Count; i++)
            {
                if (i < questTemplates.Count)
                {
                    UpdateSOFromJson(questTemplates[i], questList[i]);
                }
            }

            Debug.Log("Quests loaded into ScriptableObjects!");
        }
        else
        {
            Debug.LogError("JSON file not found at: " + jsonFilePath);
        }
    }

    void UpdateSOFromJson(QuestSO questSO, QuestData jsonData)
    {
        questSO.questId = jsonData.questId;
        questSO.title = jsonData.title;
        questSO.description = jsonData.description;
        questSO.isCompleted = jsonData.isCompleted;

        questSO.tasks = new List<QuestTask>();
        foreach (var task in jsonData.tasks)
        {
            QuestTask newTask = new QuestTask
            {
                description = task.description,
                isCompleted = task.isCompleted
            };
            questSO.tasks.Add(newTask);
        }
    }
}

[System.Serializable]
public class QuestData
{
    public string questId;
    public string title;
    public string description;
    public bool isCompleted;
    public List<TaskData> tasks;
}

[System.Serializable]
public class TaskData
{
    public string description;
    public bool isCompleted;
}

[System.Serializable]
public class QuestDataList
{
    public List<QuestData> quests;
}

4. ScriptableObject 데이터를 JSON으로 저장하기

SO 데이터를 JSON 파일로 내보낼 수도 있습니다. 이렇게 하면, 런타임 중에 퀘스트 상태를 저장할 수 있습니다.

JSON으로 저장하는 스크립트

using System.IO;
using UnityEngine;

public class QuestSaver : MonoBehaviour
{
    public List<QuestSO> questTemplates; // ScriptableObjects 리스트
    private string jsonFilePath;

    void Start()
    {
        jsonFilePath = Path.Combine(Application.persistentDataPath, "quests.json");
    }

    public void SaveQuestsToJson()
    {
        List<QuestData> questList = new List<QuestData>();

        // ScriptableObject를 JSON 데이터로 변환
        foreach (QuestSO questSO in questTemplates)
        {
            QuestData jsonData = new QuestData
            {
                questId = questSO.questId,
                title = questSO.title,
                description = questSO.description,
                isCompleted = questSO.isCompleted,
                tasks = new List<TaskData>()
            };

            foreach (QuestTask task in questSO.tasks)
            {
                TaskData taskData = new TaskData
                {
                    description = task.description,
                    isCompleted = task.isCompleted
                };
                jsonData.tasks.Add(taskData);
            }

            questList.Add(jsonData);
        }

        // JSON으로 저장
        QuestDataList dataList = new QuestDataList { quests = questList };
        string jsonDataString = JsonUtility.ToJson(dataList, true);
        File.WriteAllText(jsonFilePath, jsonDataString);

        Debug.Log("Quests saved to JSON file at: " + jsonFilePath);
    }
}

5. JSON과 ScriptableObject 병합 활용 예시

퀘스트 데이터 관리 흐름

  1. 퀘스트 디자이너: Unity에서 ScriptableObject 템플릿을 작성해 기본 데이터를 생성.
  2. JSON 파일: 런타임에서 데이터 상태를 저장하거나 동적으로 업데이트.
  3. 병합:
    • 로드: JSON 파일의 데이터를 ScriptableObject에 덮어쓰기.
    • 저장: ScriptableObject의 데이터를 JSON 파일에 내보내기.

6. JSON과 SO 병합의 장점

  1. 유연한 업데이트:
    • JSON 파일만 수정하면 런타임 중 퀘스트 데이터를 변경 가능.
  2. 설계 분리:
    • 게임 디자이너는 ScriptableObject로 데이터를 직관적으로 관리.
    • 개발자는 JSON 파일을 통해 런타임 동작과 저장/불러오기를 구현.
  3. 대규모 데이터 관리:
    • 퀘스트가 수백 개일 때도 JSON 파일을 활용하면 효율적 관리 가능.

결론

ScriptableObject는 퀘스트 데이터를 관리하기에 적합하고, JSON과 결합하면 런타임 동적 업데이트와 저장이 가능합니다. 위 예제를 바탕으로 구현하면 대규모 퀘스트 시스템도 효과적으로 관리할 수 있습니다! 😊