csv 파일 포맷
콤마로 구분된 데이터
엑셀 테이블로 작성하여 csv로 저장
유니티에서 TextAsset 형태로 임포트
Marshalling
객체의 메모리 데이터를 저장 또는 전송에 적합한 데이터로 변환
마샬링을 통해 csv 테이블의 레코드를 구조체로 변환하여 사용
Type type = typeof(TMarshalStruct);
int structSize = Marshal.SizeOf(type);
byte[] structBytes = new byte[structSize];
int structBytesIndex = 0;
const string spliter = ",";
string[] fieldDataList = line.Split(spliter.ToCharArray());
Type dataType;
string splited;
byte[] fieldByte;
FieldInfo[] fieldInfos = type.GetFields();
for (int i = 0; i < fieldInfos.Length; i++)
{
dataType = fieldInfos[i].FieldType;
splited = fieldDataList[i];
fieldByte = null;
MakeBytesByFieldType(out fieldByte, dataType, splited);
Buffer.BlockCopy(fieldByte, 0, structBytes, structBytesIndex, fieldByte.Length);
structBytesIndex += fieldByte.Length;
}
TMarshalStruct tStruct = MakeStructFromBytes<TMarshalStruct>(structBytes);
만들어야 하는 데이터의 타입(TMarshalStruct)과 크기를 보고 byte 배열 생성
일단 레코드를 ","를 사용하여 나눠놓고, 데이터 타입 수 만큼 반복문을 돈다.
반복문에서는 MakeBytesByFieldType을 통해 적절한 데이터 크기의 byte 배열을 만들고,
해당 byte 배열의 메모리를 structBytes에 붙여넣는다.
structBytes에는 모든 데이터가 저장되어야 하기 때문에
structBytesIndex를 이용하여 메모리의 어느 지점에 들어갈지를 계산한다.
if (typeof(int) == dataType)
{
fieldByte = BitConverter.GetBytes(int.Parse(splite));
}
else if (typeof(float) == dataType)
{
fieldByte = BitConverter.GetBytes(float.Parse(splite));
}
else if (typeof(bool) == dataType)
{
bool value = bool.Parse(splite);
int temp = value ? 1 : 0;
fieldByte = BitConverter.GetBytes((int) temp);
}
else if (typeof(string) == dataType)
{
fieldByte = new byte[MarshalTableConstant.charBufferSize];
byte[] byteArr = Encoding.UTF8.GetBytes(splite);
Buffer.BlockCopy(byteArr, 0, fieldByte, 0, byteArr.Length);
}
이미 정의된 데이터 타입(int, float, ...)의 Parse를 이용시 TryParse로 예외처리 하는 것이 좋음
public static T MakeStructFromBytes<T>(byte[] bytes)
{
int size = Marshal.SizeOf(typeof(T));
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
T tStruct = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return tStruct;
}
ptr에 데이터 T의 사이즈 만큼 메모리를 할당하고 ptr에 bytes를 복사한 후
PtrToStructure 함수를 이용하여 바이트 배열을 구조체로 변환
TextAsset textAsset = Resources.Load<TextAsset>(FilePath);
if (textAsset == null)
{
Debug.LogError("load failed : " + FilePath);
return false;
}
ParseTable(textAsset.text);
유니티에서 텍스트 파일은 TextAsset으로 로드할 수 있음
StringReader reader = new StringReader(text);
string line = null;
bool fieldRead = false;
while ((line = reader.ReadLine()) != null)
{
if (fieldRead == false)
{
fieldRead = true;
continue;
}
TMarshalStruct data = tableRecordParser.ParseRecordLine(line);
AddData(data);
}
StringReader를 통하여 텍스트 파일의 내용을 라인별로 읽어와 레코드를 파싱
csv의 헤더 부분은 플래그를 통하여 스킵
public class MarshalTableConstant
{
public const int charBufferSize = 256;
}
[System.Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EnemyStruct
{
public int index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MarshalTableConstant.charBufferSize)]
public string FilePath;
public int MaxHP;
public int Damage;
public int CrashDamage;
public int BulletSpeed;
public int FireRemainCount;
public int GamePoint;
}
string의 경우 위와 같이 고정된 크기로 마샬링