[2025_11_18]ItemScriptableObject생성 및 Json과 연동
2025. 11. 18. 21:00ㆍTIL
문제 상황
Addressable로 JSON 데이터를 로드하려는데 계속 실패했다.
JSON 로드 완료: 0개 (Data/Item)
분명 JSON 파일도 있고, Addressable 설정도 확인했는데 아무것도 로드되지 않았다.
해결 과정
1. Addressable 설정 확인
- 주소: "Data/Item" - 정상
- 경로: Assets/Data/ItemData.json - 존재함
- 라벨: "Item" - 설정됨
- Build: Addressable Group도 빌드됨
Addressable은 문제 없음
2. JSON 파일 구조 확인
json
{
"items": [
{
"id": 1,
"itemName": "체력 물약",
"description": "HP를 50 회복",
"itemType": "Consumable",
"value": 50,
"duration": 0
}
]
}
JSON 구조도 정상
3. 로드 코드 확인
csharp
private async void LoadJsonData()
{
var handle = Addressables.LoadAssetAsync<TextAsset>("Data/Item");
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
string jsonText = handle.Result.text;
ItemJsonDatabase db = JsonUtility.FromJson<ItemJsonDatabase>(jsonText);
Debug.Log($"JSON 로드 완료: {db.items.Count}개"); // 0개 출력!
}
}
코드도 문제없어 보임
원인 발견
문제는 데이터 클래스에 있었다!
csharp
//이렇게 되어 있었음
public class JsonDatabase<T>
{
public List<T> items = new List<T>();
}
public class ItemJsonDatabase : JsonDatabase<ItemJsonData> { }
public class ItemJsonData
{
public int id;
public string itemName;
public string description;
// ...
}
JsonUtility는 [Serializable]이 없는 클래스를 직렬화하지 못한다!
해결 방법
각 클래스에 [Serializable] 추가:
csharp
[System.Serializable]
public class JsonDatabase<T>
{
public List<T> items = new List<T>();
}
[System.Serializable]
public class ItemJsonDatabase : JsonDatabase<ItemJsonData> { }
[System.Serializable]
public class ItemJsonData
{
public int id;
public string itemName;
public string description;
public string itemType;
public int value;
public float duration;
}
결과:
JSON 로드 완료: 15개 (Data/Item)
핵심 개념: JsonUtility의 직렬화 요구사항
JsonUtility.FromJson()이 동작하려면:
- 클래스에 [Serializable] 필수
csharp
[System.Serializable] // 이거 없으면 안 됨!
public class MyData { }
- 필드는 public이거나 [SerializeField]
csharp
[System.Serializable]
public class MyData
{
public int id;
[SerializeField] private string name;
private float value;
}
- 제네릭 클래스도 [Serializable] 필요
csharp
[System.Serializable]
public class JsonDatabase<T> // 제네릭도 필수!
{
public List<T> items;
}
- 상속받은 클래스도 마찬가지
csharp
[System.Serializable]
public class ItemJsonDatabase : JsonDatabase<ItemJsonData> // 이것도!
{ }
---
## 왜 이렇게 만들었나?
### **설계 의도: 기획자 친화적 데이터 관리**
기획자(Unity 모름)
↓
JSON 파일 수정
↓
ScriptableObject로 자동 변환
↓
게임에서 사용
장점:
- 기획자가 Unity 안 열어도 됨
- Excel -> JSON 변환 가능
- Git으로 버전 관리 용이
- ScriptableObject의 장점도 활용
구조:
csharp
// 1. JSON 파싱용 클래스
[System.Serializable]
public class ItemJsonData { /* JSON 구조와 1:1 매핑 */ }
// 2. ScriptableObject (Unity에서 사용)
public class ItemData : ScriptableObject
{
// JSON → ScriptableObject 변환
public void InitFromJson(ItemJsonData jsonData)
{
this.id = jsonData.id;
this.itemName = jsonData.itemName;
// ...
}
}
// 3. DataManager가 중개
public class DataManager
{
public void LoadJsonData()
{
// JSON 로드 → ItemJsonData
// ItemJsonData → ItemData ScriptableObject 생성
}
}
추가로 알게 된 점
JsonUtility vs Newtonsoft.Json
기능JsonUtilityNewtonsoft.Json
| 속도 | 빠름 | 느림 |
| [Serializable] | 필수 | 선택 |
| private 필드 | [SerializeField] 필요 | 자동 지원 |
| Dictionary | 지원 안 함 | 지원 |
| 복잡한 구조 | 제한적 | 강력 |
언제 뭘 써야 할까?
- 간단한 데이터 + 성능 중요 -> JsonUtility
- 복잡한 구조 + 편의성 중요 -> Newtonsoft.Json
교훈
- JsonUtility 쓸 땐 [Serializable] 체크리스트
- 모든 클래스에 [Serializable]
- 제네릭 클래스도 확인
- 상속받은 클래스도 확인
- 디버깅 팁
csharp
// JSON이 실제로 뭐가 들어왔는지 확인
Debug.Log($"JSON 원본:\n{jsonText}");
// 파싱 결과 확인
var db = JsonUtility.FromJson<ItemJsonDatabase>(jsonText);
Debug.Log($"items null? {db.items == null}");
Debug.Log($"items count: {db.items?.Count ?? 0}");
프로젝트 초기에 데이터 파이프라인 설계
- 기획자가 어떻게 수정할지
- 어떤 포맷으로 관리할지
- 변환 과정은 어떻게 할지
관련 개념
- Unity Serialization
- Addressable Asset System
- ScriptableObject
- JSON 파싱
- Data-Driven Development
'TIL' 카테고리의 다른 글
| [2025_11_20]3d에서 캐릭터의 물리 문제 (0) | 2025.11.20 |
|---|---|
| [2025_11_19]아이템 효과 플레이어와 연동 (0) | 2025.11.19 |
| [2025_11_17]장애물 충돌 관리(콜라이더 분리) (0) | 2025.11.17 |
| [2025_11_14]3d 러닝 게임 팀프로젝트 시작 (0) | 2025.11.14 |
| [2025_11_13]유니티 심화 개인과제 마감 (0) | 2025.11.13 |