유니티 버전 - 2022.3.44f1
목차
- JSON이란?
- JsonUtility와 System.IO
- DataManager의 코드 및 설명
- 전체 코드
- 설명
- JsonUtility의 장점과 단점, 코드 개선점
- 장점
- 단점
- 개선점
- 코드 개선하기
- 개선점
- 완성된 코드 및 설명
- 완성코드
- 설명
- 실행결과
- 마치며
- 간단 정리
JSON이란?
JsonUtility는 유니티의 데이터 구조를 Json 형식으로 변환하거나 Json 형식의 데이터를 유니티 데이터 구조로 변환하는 데 사용됩니다
System.IO는 파일 읽기/쓰기, 디렉토리 관리, 파일 경로 조작 등의 작업을 수행 할 수 있게 해줍니다
DataManager의 코드 및 설명
초기 형태의 DataManager 코드입니다, 하나씩 나누어서 설명 하겠습니다
1. using 지시문
using System.Collections.Generic;
using UnityEngine;
using System.IO;
파일 읽기,쓰기 및 JsonUtility를 사용하기 위해 System.IO를 선언합니다
2. 커스텀 클래스 Datas
[System.Serializable]
public class Datas
{
public Vector2 playerPositon;
public string playername;
public int level;
public int hp;
public float stamina;
public List<string> items = new List<string>();
}
[System.Serializable] : 데이터를 저장할 수 있는 형식으로 변환하기 위한 직렬화 과정
커스텀 클래스 Datas : 저장하고자 하는 정보 선언 및 FromJson의 형식 참조(LoadData에서 설명)
3. 선언부
public class DataManager : ConvertToSingleton<DataManager>
{
public Vector2 playerPositon; // 여기부터
public string playername;
public int level;
public int hp;
public float stamina;
public List<string> items = new List<string>(); // 여기까지 저장하고자 하는 데이터
private Datas theDatas = new Datas();
private string saveDataDirectory;
private string saveFileName = "playerData.txt";
private Datas theDatas = new Datas();
데이터를 저장하기 위한 일시적인 그릇 역할, new 키워드를 이용해 새로운 객체를 생성하여 내부 변수를 이용
private string saveFileName = "playerData.txt";
데이터가 저장되어 파일로 존재할때 사용할 이름
Awake()
protected override void Awake() // 싱글톤화 및 오브젝트 유지
{
base.Awake();
DontDestroyOnLoad(gameObject);
}
싱글톤화는 아래를 참조 바랍니다
Start()
private void Start()
{
saveDataDirectory = Application.dataPath + "/Saves/";
if (!Directory.Exists(saveDataDirectory)) // 디렉토리 존재 여부 확인
{
Directory.CreateDirectory(saveDataDirectory); // 디렉토리가 존재하지 않을때 생성
}
LoadData();
}
saveDataDirectory = Application.dataPath + "/Saves/";
읽기 전용 경로로, 데이터 파일이 저장된 경로를 고정 값으로 반환합니다, 이 뒤에 /Saves/ 를 붙여 파일을 저장하고자하는 디렉토리의 경로를 saveDataDirectory에 저장합니다
SaveData()
public void SaveData()
{
theDatas.playerPositon = playerPositon;
theDatas.playername = playername;
theDatas.level = level;
theDatas.hp = hp;
theDatas.stamina = stamina;
theDatas.items = items;
string json = JsonUtility.ToJson(theDatas);
File.WriteAllText(saveDataDirectory + saveFileName, json);
}
theDatas.XXX = XXX
theDatas 클래스에 현재 상태의 정보를 저장하는 역할을 합니다
string json = JsonUtility.ToJson(theDatas);
정보를 저장한 후 theDatas를 JSON 형식의 문자열로 반환합니다
File.WriteAllText(saveDataDirectory + saveFileName, json);
json 문자열을 아래의 경로에 작성합니다(saveDataDirectory + saveFileName), 이미 파일이 존재할 경우 내용을 덮어씁니다
LoadData()
public void LoadData()
{
if (File.Exists(saveDataDirectory + saveFileName)) // 경로에 파일이 존재하는지 확인
{
string loadJson = File.ReadAllText(saveDataDirectory + saveFileName);
// 경로에 존재하는 파일에서 JSON 형식의 문자열을 읽어옴
theDatas = JsonUtility.FromJson<Datas>(loadJson);
playerPositon = theDatas.playerPositon;
playername = theDatas.playername;
level = theDatas.level;
hp = theDatas.hp;
stamina = theDatas.stamina;
items = theDatas.items;
}
}
}
theDatas = JsonUtility.FromJson<Datas>(loadJson);
읽어온 JSON 문자열(loadJson)을 theDatas의 각 속성에 매핑합니다(저장된 내용을 불러와서 theDatas클래스에 넣어줌)
FromJson<Datas> 여기서 Datas는 청사진같은 역할입니다, loadJson의 안에 있는 정보들은 그저 일렬로 나열되어있기때문에 청사진(Datas라는 구조도)을 제공하여 알맞게 데이터가 들어갈 수 있도록 합니다
playerPositon = theDatas.playerPositon; 및 나머지 5개
현재의 상태에 불러온 데이터를 적용시킵니다
JsonUtility의 장점과 단점, 코드 개선점
장점
- Unity에 내장되어 있기때문에 별도의 라이브러리 설치 없이 쉽게 사용 할 수 있고 과정이 간단합니다
- 다른 형식에 비해 상대적으로 빠르며, 데이터 크기도 작아 효율적입니다
단점
- 다차원 배열, 딕셔너리 등 지원하지 않는 데이터 구조가 존재합니다
- JSON형식은 사용자가 직접 열어 데이터를 수정할 수 있기에 보안에 취약합니다
코드 개선점
- 저장해야할 데이터의 증가에 따른 클래스의 복잡도 증가
- Application.dataPath의 문제점
- 경로 하드코딩에 대한 휴먼 에러️
- 각종 오류에 대한 처리 부족
완성된 코드 및 설명
먼저 저장해야할 데이터의 증가에 따른 클래스 복잡도를 해결하기위해 데이터를 실질적으로 활용하는 클래스에서 관리하도록하고 중앙 클래스인 DataManager에서는 클래스들의 전체적인 데이터 저장, 로드 등의 관리를 하도록 하였습니다
클래스를 구분 후 근본적인 복잡도를 해결하기 위해 필드 선언 없이 Datas클래스만 선언 후 이용하려 하였으나 코드의 복잡성과 참조의 어려움의 문제가 생겨 그대로 사용하기로 하였습니다
Application.dataPath의 문제점
saveDataDirectory = Path.Combine(Application.persistentDataPath + "/Saves/");
플랫폼간 호환성 및 안전한 데이터 저장을 위해 Application.dataPath 대신 Application.persistentDataPath을 사용하였습니다
경로 하드코딩에 대한 휴먼 에러️
saveDataDirectory = Path.Combine(Application.persistentDataPath, "Saves");
사용자가 직접 / 를 이용하여 경로를 내려가거나 + 를 이용하여 연산하는 것을 Path.Combine을 이용하여 해결하였습니다
경로는 , (콤마)를 이용하여 나열하고 / 를 이용하여 경로를 내려갈 필요없이 시스템이 자동적으로 처리하도록 하였습니다
각종 오류에 대한 처리 부족
1. SaveData() 및 LoadData() 에서 try-catch를 이용하여 예외 처리를 하여 대응할 수 있도록 하였습니다
2. 널 병합 연산자를 이용하여 items 리스트가 null일 경우 새로운 리스트를 할당하여 예외 처리 해주었습니다
items = theDatas.items ?? new List<string>();
실행결과
SaveData()실행시 파일이 제대로 생성되고 내용이 저장되었음을 확인할 수 있었습니다
Debug.Log(Path.Combine(DataManager.Instance.SaveDataDirectory, saveFileName));
// 디버그를 통해 파일 경로를 확인하고 들어가서 확인해보시기 바랍니다
// 유니티 Project 창에서 파일을 확인할 수 없습니다
LoadData()실행시 파일의 내용이 제대로 불러와져서 오브젝트에 반영되었음을 확인할 수 있었습니다
마치며
간단한 데이터 저장 및 로드 방법을 알아보았습니다, 프로젝트의 규모에 맞추어서 알맞은 데이터 관리법을 선택해야 할때 JSON은 좋은 방법 중 하나가 될 수 있겠습니다
이상입니다.
'코드 및 공부 > 데이터 관리' 카테고리의 다른 글
인벤토리 슬롯 저장/로드시 간헐적으로 아이템이 사라지는 현상 (0) | 2024.12.27 |
---|---|
유니티 조합 시스템 - 슬롯 이용 (0) | 2024.12.24 |
OnDrop이 실행되었을때 OnEndDrag가 실행되지 못하도록 하기 (1) | 2024.12.24 |
OnValidate()을 이용한 SO 데이터 관리 (0) | 2024.12.11 |
Application.dataPath와 Application.persistentDataPath (1) | 2024.11.27 |