내일배움캠프 59일차 TIL
인스펙터 창에서 ItemSO의 값을 수정하면, 해당 내용을 CSV 파일에 자동 반영하는 에디터 툴을 만들었다. 이를 구현하기 위해선 다음과 같은 흐름으로 작업이 진행했다.
목표 기능 요약
- ItemSO ScriptableObject의 값이 변경되면
- 지정한 ItemData.csv의 해당 행(idx 기반)을 찾아
- 변경 내용을 반영한 후 저장한다
필요 요소 정리
1. ItemSOPostprocessor.cs (Asset 변경 감지)
- AssetModificationProcessor를 사용해 SO가 저장될 때 감지
2. ItemSOToCSVUpdater.cs (CSV 업데이트 로직)
- ItemSO를 읽어서 지정된 ItemData.csv의 행을 수정
1. ItemSOPostprocessor.cs
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
public class ItemSOPostprocessor : AssetPostprocessor
{
static void OnPostprocessAllAssets(
string[] importedAssets, string[] deletedAssets,
string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (var path in importedAssets)
{
if (path.EndsWith(".asset") && path.Contains("/08_ScriptableObjects/"))
{
ItemSO item = AssetDatabase.LoadAssetAtPath<ItemSO>(path);
if (item != null)
{
ItemSOToCSVUpdater.UpdateCSV(item);
}
}
}
}
}
#endif
2. ItemSOToCSVUpdater.cs
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
public static class ItemSOToCSVUpdater
{
static string csvPath = "Assets/09_Settings/ItemData.csv"; // 경로 수정 가능
public static void UpdateCSV(ItemSO item)
{
if (!File.Exists(csvPath))
{
Debug.LogError("[CSV] 파일이 존재하지 않습니다: " + csvPath);
return;
}
var lines = new List<string>(File.ReadAllLines(csvPath));
if (lines.Count < 1) return;
// 헤더에서 컬럼 인덱스 매핑
var header = lines[0].Split(',');
Dictionary<string, int> colMap = new();
for (int i = 0; i < header.Length; i++)
colMap[header[i].Trim()] = i;
// 해당 item.idx에 해당하는 행 찾기
for (int i = 1; i < lines.Count; i++)
{
var cols = lines[i].Split(',');
if (cols.Length < 1) continue;
if (cols[colMap["idx"]].Trim() == item.idx)
{
// 기존 데이터를 수정
cols[colMap["itemName"]] = item.itemName;
cols[colMap["description"]] = item.description.Replace(",", " ");
cols[colMap["ItemTypes"]] = item.itemTypes.ToString().Replace(", ", "|");
cols[colMap["stackable"]] = item.stackable ? "y" : "n";
cols[colMap["maxStack"]] = item.maxStack.ToString();
// 필요 시 ToolData, EquipableData 등도 추가 반영
if (item.itemTypes.HasFlag(ItemType.Tool) && item.toolData != null)
{
if (colMap.TryGetValue("toolType", out int idx)) cols[idx] = item.toolData.toolType.ToString();
if (colMap.TryGetValue("getAmount", out idx)) cols[idx] = item.toolData.getAmount.ToString();
if (colMap.TryGetValue("luck", out idx)) cols[idx] = item.toolData.luck.ToString();
if (colMap.TryGetValue("durability", out idx)) cols[idx] = item.toolData.durability.ToString();
if (colMap.TryGetValue("atkSpd", out idx)) cols[idx] = item.toolData.atkSpd.ToString();
}
if (item.itemTypes.HasFlag(ItemType.Equipable) && item.equipableData != null)
{
if (colMap.TryGetValue("equipableType", out int idx)) cols[idx] = item.equipableData.equipableType.ToString();
if (colMap.TryGetValue("maxHealth", out idx)) cols[idx] = item.equipableData.maxHealth.ToString();
if (colMap.TryGetValue("atk", out idx)) cols[idx] = item.equipableData.atk.ToString();
if (colMap.TryGetValue("def", out idx)) cols[idx] = item.equipableData.def.ToString();
if (colMap.TryGetValue("spd", out idx)) cols[idx] = item.equipableData.spd.ToString();
if (colMap.TryGetValue("crt", out idx)) cols[idx] = item.equipableData.crt.ToString();
}
if (item.itemTypes.HasFlag(ItemType.Eatable) && item.eatableData != null)
{
if (colMap.TryGetValue("recoverHP", out int idx)) cols[idx] = item.eatableData.recoverHP.ToString();
if (colMap.TryGetValue("fullness", out idx)) cols[idx] = item.eatableData.fullness.ToString();
if (colMap.TryGetValue("slimeGauge", out idx)) cols[idx] = item.eatableData.slimeGauge.ToString();
if (colMap.TryGetValue("duration", out idx)) cols[idx] = item.eatableData.duration.ToString();
if (colMap.TryGetValue("eatableRottable", out idx)) cols[idx] = item.eatableData.rottenable ? "y" : "n";
}
// 수정한 line으로 다시 합치기
lines[i] = string.Join(",", cols);
break;
}
}
// 다시 쓰기
File.WriteAllLines(csvPath, lines);
AssetDatabase.Refresh();
Debug.Log($"[CSV] {item.itemName} 수정 반영 완료");
}
}
#endif
참고
- ItemTypes는 "Tool|Equipable" 형식으로 "|" 구분자로 CSV에 저장됩니다.
- description은 "," 때문에 CSV에서 깨질 수 있어 공백으로 대체 저장합니다.
- idx는 고유값이므로 중복되지 않도록 주의해야 합니다.
사용법 요약
- ItemSO.asset을 수정 후 저장 (Ctrl+S 또는 Unity 저장)
- 자동으로 ItemData.csv의 해당 행이 수정됨
- 로그에 [CSV] 아이템명 수정 반영 완료 출력 확인

'TIL' 카테고리의 다른 글
| 육각형 맵 Flat-Top 전환 & 6방향 랜덤 지형 배치 (0) | 2025.07.04 |
|---|---|
| 아이템 레시피 데이터를 SO에 반영하고 CSV ↔ SO 동기화 기능 구현 (2) | 2025.07.03 |
| CSV 기반 ScriptableObject 자동 생성 시스템 구축 (0) | 2025.07.01 |
| Pointy-Top 육각형 외형 맵 생성 (with Unity Tilemap + 사각형 타일) (2) | 2025.06.30 |
| 커스텀 에디터(Custom Editor) (1) | 2025.06.04 |