Abstract
데이터 테이블은 엑셀 표를 게임 안으로 가져오는 통로입니다. 한 줄이 한 행, 한 행이 C# 객체 하나가 됩니다. 수치를 코드에서 빼내 텍스트 파일로 옮기면, 밸런스를 고칠 때 재컴파일이 필요 없고 기획자가 직접 손댈 수 있습니다.
실무에서 필요한 순간
•
적·아이템·스킬이 수십 종이 되어 코드 상수로는 한눈에 안 들어올 때
•
밸런스 수치를 프로그래머가 아닌 사람이 고쳐야 할 때
•
빌드 후에도 수치를 패치하고 싶을 때 (Addressables 원격 그룹과 조합)
직접 해 보기 — ToyboxNightmare 의 적 스탯을 테이블로 옮기기
예제는 적 스탯을 EnemyTable.cs 의 C# 배열로 들고 있습니다. 이것을 데이터 테이블로 바꾸는 전 과정입니다.
① 텍스트 파일을 만든다 — Enemy.txt, 탭 구분. # 으로 시작하는 줄은 주석입니다.
#Id AssetName MaxHP Attack Interval Score
1 Zombunny 100 10 0.5 10
2 ZomBear 100 10 0.5 10
3 ZombieDuck 120 20 1.0 20
4 Clown 150 30 1.5 25
5 Hellephant 200 35 2.0 50
Plain Text
복사
② 행 클래스를 만든다 — DataRowBase 를 상속하고 Id 와 ParseDataRow 를 구현합니다.
using UnityGameFramework.Runtime;
namespace ToyBoxNightmare
{
public class EnemyRow : DataRowBase
{
private int mId;
public override int Id { get { return mId; } }
public string AssetName { get; private set; }
public int MaxHitPoints { get; private set; }
public int AttackDamage { get; private set; }
public float TimeBetweenAttacks { get; private set; }
public int ScoreValue { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData)
{
const int ColumnCount = 6;
string[] columns = dataRowString.Split('\t');
if (columns.Length != ColumnCount)
return false;
mId = int.Parse(columns[0]);
AssetName = columns[1];
MaxHitPoints = int.Parse(columns[2]);
AttackDamage = int.Parse(columns[3]);
TimeBetweenAttacks = float.Parse(columns[4], System.Globalization.CultureInfo.InvariantCulture);
ScoreValue = int.Parse(columns[5]);
return true;
}
}
}
C#
복사
③ Addressables 에 등록한다 — Enemy.txt 를 Groups 창에 끌어다 놓고 주소를 EnemyTable 로 줍니다.
④ 로드한다 — 비동기이므로 성공 이벤트를 기다립니다. 게임 시작 전 로딩용 Procedure 에 두는 것이 정석입니다.
DataTableComponent dataTable = GameEntry.GetComponent<DataTableComponent>();
EventComponent events = GameEntry.GetComponent<EventComponent>();
events.Subscribe(LoadDataTableSuccessEventArgs.EventId, OnLoadDataTableSuccess);
events.Subscribe(LoadDataTableFailureEventArgs.EventId, OnLoadDataTableFailure);
IDataTable<EnemyRow> table = dataTable.CreateDataTable<EnemyRow>();
((DataTableBase)table).ReadData("EnemyTable");
C#
복사
⑤ 꺼내 쓴다 — 성공 이벤트 이후 어디서든.
IDataTable<EnemyRow> table = GameEntry.GetComponent<DataTableComponent>().GetDataTable<EnemyRow>();
EnemyRow row = table.GetDataRow(5); // Id 로
EnemyRow duck = table.GetDataRow(r => r.AssetName == "ZombieDuck"); // 조건으로
C#
복사
자주 걸리는 곳
•
ReadData 는 IDataTable<T> 에 없습니다. CreateDataTable<T>() 가 돌려준 객체를 DataTableBase 로 캐스팅해서 부릅니다.
•
ReadData 직후에는 테이블이 비어 있습니다. 성공 이벤트 전에 GetDataRow 를 하면 null 입니다.
•
같은 행 타입으로 테이블을 두 번 만들면 예외입니다. 재시작 흐름이 있다면 HasDataTable<T>() 로 먼저 확인합니다.
•
float.Parse 는 기기 언어 설정에 따라 소수점 기호가 달라집니다. CultureInfo.InvariantCulture 를 넘기세요.
•
이 에디션은 에디터 툴이 없어 엑셀 → 텍스트 변환을 제공하지 않습니다. 엑셀에서 "탭으로 분리된 텍스트"로 저장하면 그대로 쓸 수 있습니다.
데이터 테이블 (Data Tables)
관련 소스 파일
•
Scripts/Editor/ResourceAnalyzer/ResourceAnalyzerController.cs
•
Scripts/Editor/ResourceCollection/Asset.cs
•
Scripts/Editor/ResourceCollection/ResourceCollection.cs
•
Scripts/Editor/ResourceEditor/ResourceEditorController.cs
•
Scripts/Runtime/Config/ConfigComponent.cs
•
Scripts/Runtime/Config/ConfigHelperBase.cs
•
Scripts/Runtime/Config/DefaultConfigHelper.cs
•
Scripts/Runtime/DataTable/DataRowBase.cs
•
Scripts/Runtime/DataTable/DataTableComponent.cs
•
Scripts/Runtime/DataTable/DataTableHelperBase.cs
•
Scripts/Runtime/DataTable/DefaultDataTableHelper.cs
•
Scripts/Runtime/Localization/DefaultLocalizationHelper.cs
•
Scripts/Runtime/Localization/LocalizationComponent.cs
•
Scripts/Runtime/Localization/LocalizationHelperBase.cs
개요
데이터 테이블 시스템은 Unity Game Framework의 특화된 구성 요소로, 구조화된 게임 데이터를 효율적으로 관리하기 위해 설계되었습니다.
이 시스템은 다양한 소스로부터 테이블 형태의 데이터(행과 열) 를 로드, 파싱, 접근할 수 있는 강력한 메커니즘을 제공합니다.
게임 설정, 적(Enemy) 능력치, 아이템 데이터베이스 등 게임 고유의 구조적 정보의 기반 역할을 합니다.
•
비테이블 형식 설정 값:
Configuration System 참고
Configuration System 참고•
다국어 및 지역화 데이터:
Localization System 참고
Localization System 참고아키텍처
데이터 테이블 시스템은 컴포넌트 - 매니저 - 헬퍼 패턴을 따릅니다.
이는 관심사의 분리를 명확히 하고, 헬퍼 클래스를 통한 유연한 커스터마이징을 가능하게 합니다.
핵심 구성 요소
1. DataTableComponent
•
데이터 테이블 시스템의 주요 인터페이스
•
역할:
◦
데이터 테이블 매니저 초기화
◦
데이터 테이블 생성, 접근, 파괴
◦
데이터 로딩 및 이벤트 디스패치
◦
데이터 테이블 존재 여부 및 내용 쿼리 제공
DataTableComponent dataTableComponent = GameEntry.GetComponent<DataTableComponent>();
C#
복사
2. DataTableHelperBase
•
추상 클래스 (abstract base class)
•
인터페이스:
◦
IDataProviderHelper<DataTableBase>: 데이터 읽기 및 로딩
◦
IDataTableHelper: 데이터 테이블 관련 작업
•
확장하여 커스텀 헬퍼 구현 가능
3. DefaultDataTableHelper
•
DataTableHelperBase의 기본 구현체
•
기능:
◦
텍스트 또는 바이너리 에셋에서 데이터 테이블 읽기
◦
문자열/바이트 배열 파싱
◦
사용 종료 시 데이터 에셋 해제
•
특징:
◦
텍스트 기반/바이너리 데이터 테이블 모두 지원
◦
.bytes 파일 특별 처리
◦
리소스 시스템과 통합 관리
4. DataRowBase
•
사용자 정의 데이터 행(Row)의 기반 클래스
•
특징:
◦
IDataRow 인터페이스 구현
◦
필수: Id 속성 구현
◦
가상 메서드 제공: 문자열/바이트 기반 데이터 파싱
•
모든 게임 전용 데이터 행 클래스는 반드시 DataRowBase 상속 필요
데이터 흐름
데이터 테이블 로드 및 파싱 시 데이터 흐름은 다음과 같습니다:
1.
DataTableComponent → 로드 요청
2.
DefaultDataTableHelper → 데이터 읽기 및 파싱
3.
DataRowBase 상속 클래스 → 실제 데이터 구조화
4.
DataTableComponent → 접근/조회 가능 상태로 관리
지원 데이터 포맷
1. 텍스트 형식 (Text Format)
•
각 줄이 데이터 행(Row)
•
#으로 시작하는 줄은 주석 처리
•
AddDataRow를 통해 테이블에 추가
•
예시:
10001 Sword 500
10002 Shield 800
Plain Text
복사
2. 바이너리 형식 (Binary Format)
•
텍스트보다 로드 속도 빠름 (하지만 사람이 읽기 힘듦)
•
특징:
◦
BinaryReader로 데이터 탐색
◦
7비트 인코딩 정수로 행 길이 지정
◦
각 행을 바이트 배열로 처리
•
대규모 데이터셋 및 프로덕션 빌드 최적화에 유용
사용 예시
1. 데이터 행 정의
public class ItemData : DataRowBase
{
private int mId;
// Id 는 DataRowBase 의 abstract 프로퍼티다. 반드시 override 한다
public override int Id { get { return mId; } }
public string Name { get; private set; }
public int Price { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData)
{
string[] columns = dataRowString.Split('\t');
if (columns.Length < 3) return false;
mId = int.Parse(columns[0]);
Name = columns[1];
Price = int.Parse(columns[2]);
return true;
}
}
C#
복사
2. 데이터 테이블 생성 및 로드
DataTableComponent dataTableComponent = GameEntry.GetComponent<DataTableComponent>();
IDataTable<ItemData> itemDataTable = dataTableComponent.CreateDataTable<ItemData>();
// ReadData 는 IDataTable<T> 가 아니라 DataTableBase 에 있다. 인자는 Addressables 주소, 로드는 비동기
((DataTableBase)itemDataTable).ReadData("Items");
C#
복사
3. 데이터 접근
ItemData item = itemDataTable.GetDataRow(10001);
Debug.Log($"Item: {item.Name}, Price: {item.Price}");
ItemData[] items = itemDataTable.GetAllDataRows();
for (int i = 0; i < items.Length; i++)
{
// 아이템 데이터 처리
}
// 조건으로 고르기
ItemData[] expensive = itemDataTable.GetDataRows(row => row.Price >= 500);
C#
복사
리소스 시스템과의 통합
•
DefaultDataTableHelper → ResourceComponent를 통해 에셋 로딩
•
데이터 테이블이 필요 없어지면 자동 언로드
관리 기능
테이블 관리
작업 | 설명 | 메서드 |
존재 확인 | 테이블 존재 여부 확인 | HasDataTable<T>() |
생성 | 새 데이터 테이블 생성 | CreateDataTable<T>() |
가져오기 | 기존 데이터 테이블 반환 | GetDataTable<T>() |
전체 조회 | 모든 테이블 반환 | GetAllDataTables() |
파괴 | 데이터 테이블 제거 | DestroyDataTable<T>() |
행 관리
작업 | 설명 | 메서드 |
존재 확인 | 특정 데이터 행 존재 여부 확인 | HasDataRow(id) |
단일 조회 | ID로 데이터 행 조회 | GetDataRow(id) |
복수 조회 | 조건(Predicate)에 맞는 데이터 행 조회 | GetDataRows(condition) |
전체 조회 | 모든 데이터 행 가져오기 | GetAllDataRows() |
성능 최적화
•
바이너리 포맷 지원 → 로드 속도 향상
•
Cached Bytes → 바이너리 작업 시 메모리 사전 할당
•
행은 로드 시 한 번만 생성 → 테이블이 파괴될 때까지 유지되므로 조회 중 할당이 없음
메모리 관리 메서드:
•
EnsureCachedBytesSize(size) : 캐시 메모리 확보
•
FreeCachedBytes() : 메모리 해제
이벤트 지원
로드 결과는 EventComponent 로 전달됩니다. Subscribe(XxxEventArgs.EventId, handler) 로 받습니다.
•
LoadDataTableSuccessEventArgs: 데이터 성공적으로 로드됨
•
LoadDataTableFailureEventArgs: 데이터 로드 실패
•
LoadDataTableUpdateEventArgs: 로딩 진행 상황 업데이트
•
LoadDataTableDependencyAssetEventArgs: 종속 에셋 로드 시 호출
요약
데이터 테이블 시스템은 Unity Game Framework에서 구조화된 게임 데이터 관리를 위한 핵심 도구입니다.
•
데이터 로딩, 파싱, 접근을 분리해 클린 아키텍처 구현
•
텍스트/바이너리 지원 및 성능 최적화 기능 내장
•
리소스 시스템과 통합되어 효율적 관리 가능
간단한 아이템 목록부터 RPG의 대규모 데이터베이스까지 확장 가능한 유연한 구조를 제공합니다.







