본문 바로가기
TIL

CSV 기반 ScriptableObject 자동 생성 시스템 구축

by vvin39 2025. 7. 1.

내일배움캠프 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를 생성한다.

주요 로직:

  1. Unity Editor 내에 CSV 파일 선택 및 SO 생성 버튼을 만들었다.
  2. 선택된 CSV 파일을 라인별로 읽어 들여 아이템 데이터를 파싱했다.
  3. 파싱된 데이터 중 itemType 필드를 기반으로 SO 내부의 해당 세부 데이터(예: eatableData, equipableData)에 값을 할당했다.
  4. 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 파일에 다시 반영하는 기능을 제공한다.

동작 방식:

  1. 프로젝트 내에 존재하는 모든 ItemSO 에셋을 검색했다.
  2. 기존 ItemData.csv 파일을 라인별로 파싱했다.
  3. 파싱된 CSV 데이터의 ID 값과 SO의 ID 값을 매칭하여 해당 SO의 최신 값으로 CSV 행을 업데이트했다.
  4. 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 파일로 전환했으니, 앞으로 유지보수 측면에서도 큰 이점을 얻을 수 있을 것이라고 생각한다.

 

 

 

 

최근댓글

최근글

skin by © 2024 ttuttak