내일배움캠프 58일차 TIL
2D 서바이벌 게임의 아이템 관리Unity에서 CSV 파일을 활용해 ScriptableObject (SO)를 자동으로 생성하고 관리하는 시스템을 구축했다. 우리 2D 서바이벌 게임의 다양한 아이템 데이터를 훨씬 효율적으로 관리할 수 있게 되었다.
💡 이 작업을 왜 했냐면? (배경)
우리 게임에는 장비, 음식, 도구 등 정말 많은 아이템이 존재한다. 그동안 아이템 하나하나 SO를 직접 만들고 값을 입력하는 건 너무나 비효율적인 작업이었다.
- 시간 소모: 아이템 수가 늘어날수록 SO를 개별적으로 생성하고 값을 입력하는 데 엄청난 시간을 쏟아야 했다.
- 데이터 불일치: Excel로 데이터를 관리하고 싶어 했고, Unity SO를 직접 수정해야 했는데, 이 과정에서 데이터가 서로 맞지 않는 문제가 발생할 수 있었다.
- 협업의 어려움: 데이터를 수정할 때마다 내가 SO에 수동으로 반영해야 하는 번거로움이 컸다.
이런 문제들을 해결하고 더 효율적으로 협업할 방법을 찾다가, CSV 파일 기반의 SO 자동 생성 및 양방향 동기화 시스템을 만들기로 결심했다.
🔧 시스템 구조
내가 구축한 시스템은 다음과 같은 구성 요소로 이루어져 있다.
1. ItemData.csv:
- 아이템 정보를 담고 있는 CSV 파일이다.
- Excel로 작성했고, UTF-8 인코딩으로 저장했다.
- Unity 프로젝트 내 Assets/Resources/Data/ItemData.csv 경로에 두었다.
2. ItemSO.cs:
- 아이템 데이터를 표현하는 ScriptableObject 클래스이다.
- ItemType (예: Eatable, Equipable)에 따라 EatableData, EquipableData 등 세부 데이터를 유연하게 담을 수 있도록 설계했다.
3. ItemSOGenerator.cs (Editor 전용):
- Unity Editor에서 작동하는 스크립트이다.
- ItemData.csv 파일을 파싱하여 자동으로 ItemSO 인스턴스를 생성하고 프로젝트 에셋으로 저장하는 역할을 한다.
4. ItemSOUpdater.cs (Editor 전용):
- 이것도 Unity Editor에서 작동하는 스크립트이다.
- ItemSO 인스펙터에서 수정한 내용을 다시 ItemData.csv 파일에 반영하여 덮어쓰는 기능을 구현했다.
1단계: ItemData.csv 작성
예시:
| ID | Name | ItemType | stackable | maxStack | HP | ATK | Fullness | Description |
| item_001 | SlimeHat | Equipable | Y | 1 | 20 | 5 | 귀여운 슬라임 모자 | |
| item_002 | Meat | Eatable | Y | 20 | 30 | 고기다! |
작성 시 주의할 점:
- 데이터 내부에 쉼표(,)가 포함될 경우 파싱 오류를 막기 위해 Replace(",", " ")와 같은 전처리 과정이 필요할 수 있다는 것을 알게 되었다.
- 소수점은 반드시 . (dot) 형식으로 표기해야 한다. (예: 0.5)
2단계: ItemSO.cs 구조 설계
public enum ItemType { Material, Tool, Equipable, Eatable, BuffItem }
[CreateAssetMenu(menuName = "Item/Item")]
public class ItemSO : ScriptableObject
{
public string id;
public string itemName;
public string description;
public Sprite icon;
public ItemType itemType;
public bool stackable;
public int maxStack;
public EquipableData equipableData;
public EatableData eatableData;
// 기타 ItemType에 따른 세부 데이터 필드도 추가할 수 있다.
}
// 세부 데이터 클래스 예시: EquipableData
[System.Serializable]
public class EquipableData
{
public float maxHealth;
public float atk;
public float atkSpd;
public float def;
public float spd;
public float crt;
}
- [CreateAssetMenu] 속성은 Unity Editor에서 SO를 수동으로 생성할 때 편리하지만, 나는 자동 생성에 중점을 두었다.
- [System.Serializable] 속성 덕분에 커스텀 클래스도 Unity Inspector에서 직렬화되어 표시되는 것을 확인했다.
3단계: ItemSOGenerator.cs 구현
이 스크립트는 Unity Editor 확장 기능을 활용하여 CSV 파일을 파싱하고 SO를 생성한다.
주요 로직:
- Unity Editor 내에 CSV 파일 선택 및 SO 생성 버튼을 만들었다.
- 선택된 CSV 파일을 라인별로 읽어 들여 아이템 데이터를 파싱했다.
- 파싱된 데이터 중 itemType 필드를 기반으로 SO 내부의 해당 세부 데이터(예: eatableData, equipableData)에 값을 할당했다.
- AssetDatabase.CreateAsset() 메서드를 사용하여 생성된 SO 인스턴스를 지정된 폴더(예: Assets/08_ScriptableObjects/)에 에셋으로 저장했다.
적용 예시 (코드 발췌):
if (asset.itemType == ItemType.Eatable)
{
asset.eatableData = new EatableData();
// CSV 파싱 시 인덱스 유의 (0부터 시작)
float.TryParse(cols[7], out asset.eatableData.fullness); // 예시 CSV 기준 Fullness 값은 8번째 열(인덱스 7)이었다.
}
트러블슈팅 및 고려사항:
- Enum.TryParse<TEnum>() 사용 시 파싱 실패에 대비하여 기본값(fallback) 설정 및 예외 처리가 필요하다는 것을 배웠다.
- CSV 파일 내의 공백 행이나 유효하지 않은 데이터(예: 빈 아이템명)는 무시하도록 예외 처리 로직을 추가해야 했다.
- CSV 헤더(컬럼명)는 데이터 파싱 대상에서 제외해야 한다는 것을 파악하고 반복문 시작 인덱스 조정(예: for i = 1 또는 for i = 2)으로 처리했다.
4단계: ItemSOUpdater.cs 구현 (CSV 역수정)
이 스크립트는 SO에서 변경된 내용을 CSV 파일에 다시 반영하는 기능을 제공한다.
동작 방식:
- 프로젝트 내에 존재하는 모든 ItemSO 에셋을 검색했다.
- 기존 ItemData.csv 파일을 라인별로 파싱했다.
- 파싱된 CSV 데이터의 ID 값과 SO의 ID 값을 매칭하여 해당 SO의 최신 값으로 CSV 행을 업데이트했다.
- File.WriteAllLines() 메서드를 사용하여 기존 CSV 파일을 업데이트된 내용으로 덮어썼다.
적용 예시 (코드 발췌):
cols[1] = so.itemName; // CSV의 2번째 열(인덱스 1)에 so.itemName 값을 반영했다.
cols[4] = so.stackable ? "Y" : "N"; // CSV의 5번째 열(인덱스 4)에 so.stackable 값을 반영했다.
cols[8] = so.eatableData?.fullness.ToString() ?? "0"; // CSV의 9번째 열(인덱스 8)에 eatableData.fullness 값을 반영했다.
- CSV 열 인덱스는 0부터 시작하므로, 코드 작성 시 정확한 인덱스 매핑이 중요하다는 것을 다시 한번 확인했다.
🧠 결과 및 적용 효과
이 시스템을 구축함으로써 다음과 같은 개선 효과를 얻었다.
| 항목 | 이전 (Before) | 이후 (After) |
| SO 생성 및 관리 시간 | 수작업, 개별 SO 생성 및 값 입력 | 자동화된 일괄 생성 및 관리 |
| SO ↔ CSV 데이터 동기화 | 수동 관리로 인한 불일치 가능성 | 양방향 동기화로 데이터 일관성 유지 |
| 협업 효율성 | 개발자 개입 필요 | Excel 기반 데이터 수정으로 즉시 반영 가능 |
| 게임 밸런싱 조절 | 코드 또는 인스펙터 수동 수정 | CSV 편집 후 일괄 반영으로 신속한 테스트 가능 |
📌 배운 점
- ScriptableObject의 강력한 활용을 위해서는 데이터 기반의 자동화 시스템 구축이 필수적이라는 것을 깨달았다.
- Unity Editor 확장 기능(AssetDatabase, EditorWindow 등)을 활용한 커스텀 툴 개발은 반복적인 작업을 줄이고 개발 생산성을 크게 향상시킬 수 있다는 것을 경험했다.
- 프로젝트 초기 단계에서의 데이터 구조 설계가 시스템의 확장성과 유지보수성에 지대한 영향을 미친다는 것을 다시 한번 확인했다.
향후 개선 예정 사항
- 아이템 아이콘 자동 로딩 기능 추가 (Resources.Load<Sprite>() 활용)를 계획하고 있다.
- 파싱 실패 시 상세한 오류 로그 기록 기능을 구현할 생각이다 (예: Enum.Parse 실패 시).
- CSV 열 이름 기반 파싱으로 코드 가독성 및 유지보수성을 향상시키고 싶다.
- SO 변경 내역을 기록하는 버전 관리 로깅 시스템 도입도 고려하고 있다.
마무리
이 시스템을 만들면서 Excel만으로도 게임 콘텐츠 및 밸런싱을 관리할 수 있게 되어 협업 효율을 극대화할 수 있었다. ScriptableObject의 장점을 유지하면서 데이터 관리의 중심을 CSV 파일로 전환했으니, 앞으로 유지보수 측면에서도 큰 이점을 얻을 수 있을 것이라고 생각한다.

'TIL' 카테고리의 다른 글
| 아이템 레시피 데이터를 SO에 반영하고 CSV ↔ SO 동기화 기능 구현 (2) | 2025.07.03 |
|---|---|
| CSV 기반 아이템 SO 수정 시 CSV 자동 업데이트 기능 구현 (0) | 2025.07.02 |
| Pointy-Top 육각형 외형 맵 생성 (with Unity Tilemap + 사각형 타일) (2) | 2025.06.30 |
| 커스텀 에디터(Custom Editor) (1) | 2025.06.04 |
| AnimationCurve로 구현한 낮과 밤 그리고 온도 (0) | 2025.05.27 |