본문 바로가기

팀프로젝트

[01] 아이템 데이터베이스

팀 프로젝트에서 아이템 - 인벤토리 관련을 만드는 역할을 맡았다.

우선 순서는 아이템을 다른 사람이 편하게 추가할 수 있는 DB를 만드는 것. .csv파일을 아이템 데이터베이스로 활용하여 다른 팀원들이 쉽게 수정할 수 있도록 만들어보자.

 

.csv파일은 여러가지로 만들 수 있지만, 역시 가장 편한것은 엑셀과 메모장이라고 생각한다. 간단하게 확인할 때는 메모장으로, 작성할 때는 엑셀로 쓰는 것이 편하다. 임시로 몇가지 아이템을 만들어서 .csv 파일로 저장해보자. 인코딩은 UTF-8을 사용할 것이다.

엑셀의 첫 번째(열 이름) 행에는 분류가 들어가고, 각각 ID에 의해 지정될 것이다. 그 뒤에는 아이템의 특성들이 나열되어있다. 작성한 이후 유니티의 Asset-Resources 폴더에 넣어주자.

※ 폴더 이름은 중요하다. Assets 다음으로 있는 Resources 폴더는 유니티에서 제공하는 Resources.Load 기능을 사용할 때 쓰인다.

 

이제 다른 사람들(기획)이 쉽게 아이템 정보를 수정할 수 있도록 엑셀파일을 넣어놓았으니, 나는 csv파일을 읽어서 사용하는 스크립트를 작성할 차례이다. .csv 파일을 읽어오는 스크립트이니 CSVReader라는 스크립트를 만들어주자.

 

public class CSVReader : MonoBehaviour
{}

이 CSVReader 스크립트는 다른 사람이 잘 만들어준 것이 있으나... 항상 그렇듯 공부를 겸하므로 직접 만들어보자.

 

우선적으로 필요한 것은 csv파일을 불러오는 것. 

TextAsset data = Resources.Load(file) as TextAsset;

TextAsset이라는 형식으로 Resources 폴더에 있는 file을 가져온다. 이렇게 하면 csv파일을 가져올 수 있다.

var list = new List<Dictionary<string, object>>();
var lines = Regex.Split(data.text, LINE_SPLIT_RE);

 

가져온 csv 파일을 Dicticonary 형태로 list에 저장할 것이다. 그러기 위해서는 csv 파일을 줄 단위로 끊어 Split 해 줄 필요가 있다. 여기서 사용되는 Split 조건이 바로 LINE_SPLIT_RE.

static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";

해당하는 문자가 나오면 줄이 바뀌는 것으로 생각한다. 윈도우에서는 \r\n(CRLF), 리눅스에서는 \n(LF)이기에 다양한 줄바꿈 요소를 체크해야한다. \r\n, \n\r, \n, \r이 감지되면 다음 줄로 생각한다.

 

이렇게 나온 줄이 1 이하라면 아무 데이터도 없는 것이므로 return을 선언하고, 그게 아니라면 받아온 정보-행(Row)-을 쉼표 단위 데이터로 끊어줄 필요가 있다. 거기에 사용되는 것이 다시 SPLIT_RE.

static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";

아래 문자가 등장하면 별개의 항목으로 취급하며 제외한다는 것이다. 조금 복잡하게 생겼는데, 이는 정규표현식이므로 어쩔 수 없다. 

관련 정보가 필요하면 정규표현식을 따로 찾아봐야한다. 여기서의 정규 표현식은 큰 따옴표 " " 세트 안의 쉼표는 무시하고, 세트가 이루어지지 않는 바깥의 쉼표는 탐색하는 용도로 코드가 짜여졌다.

if (lines.Length <= 1)
{
    return list;
}
//항목 단위로 끊기. 
var header = Regex.Split(lines[0], SPLIT_RE);
for (var i = 1; i < lines.Length; i++)
{
    var values = Regex.Split(lines[i], SPLIT_RE);
    if (values.Length == 0 || values[0] == "")
    {
        continue;
    }
}

첫 줄은 header로, 0번째 라인을 끊는다. 이 header들이 딕셔너리의 key 값이 될 예정이다.

그 이후 엑셀의 2번째 행부터는 정보가 들어가는데, 마찬가지로 SPLIT_RE를 통해 Split하고, 비어있다면 continue를 통해 다음 줄을 읽는다.

 

위에서 딕셔너리의 key 값이 될 예정이라고 했으니, 이제는 진짜 Dictionary를 선언할 시간이다.

데이터베이스는 List <Dictionary> 형태로, 데이터베이스의 한 줄 한 줄을 입력하는 코드인 것이다.

var entry = new Dictionary<string, object>();
for (var j = 0; j < header.Length && j < values.Length; j++)
{
    string value = values[j];
    value = value.TrimStart(TRIM_CHARS).TrimEnd(TRIM_CHARS).Replace("\\", "");
    object finalvalue = value;
    int n;
    float f;
    if (int.TryParse(value, out n))
    {
        finalvalue = n;
    }
    else if (float.TryParse(value, out f))
    {
        finalvalue = f;
    }
    entry[header[j]] = finalvalue;
}
list.Add(entry);

Header의 길이만큼(ID, Name, Power... 등의 갯수)만큼과 그 줄의 구성요소만큼을 읽을 때 까지 반복한다.

주목해야할 것은 Trim과 TryParse.

Trim은 static char[] TRIM_CHARS = { '\"' };로 선언된 TRIM_CHARS를 기반으로 문자열 앞 뒤의 "를 제거하고, Replace는 역슬래시와 공백문자를 제거하는데 사용된다.

그 이후 object로 선언한 finalvalue 값을 list에 더한다.

참고한 자료는 https://bravenewmethod.com/2014/09/13/lightweight-csv-reader-for-unity/#comment-7111

위의 링크처럼 완성되었다.

 

그럼 이제 이 아이템 베이스를 어떻게 가져다 써야할까?

나는 아이템에 관련한 부분을 담당할 Item Manager를 하나 만들어서 그 값을 읽어오도록 할 예정이다.

Manager는 하나만 존재하면 되니 싱글톤 패턴으로 만들고, 키를 눌러서 값을 제대로 읽어올 수 있는지 확인하는 코드를 작성해보자.

    List<Dictionary<string, object>> data;

    private void Start()
    {
        data = CSVReader.Read("ItemDataBase");
    }


    private int count = 0;
    public void Update()
    {
        if(Input.GetKeyDown(KeyCode.A)) 
        {
            Debug.Log(("ID : " + data[count]["ID"] + "\t" +
           "Name : " + data[count]["Name"] + "\t" +
           "Description : " + data[count][eItemKeyColumns.Description.ToString()]));

            text.text = ("ID : " + data[count]["ID"] + "\t" +
           "Name : " + data[count]["Name"] + "\t" +
           "Description : " + data[count]["Description"]);
            count++;
            if (count > 6) count = 0;
        }
    }
public enum eItemKeyColumns
{
    ID,
    Name,
    Description
}

그리고 아이템의 열 이름은 언제든지 쉽게 바뀔 수 있고, 계속 직접 적어주면 오타때문에 버그가 날 수 있으므로 enum으로 선언해서 사용하도록 하자. 기존의 "ID" 대신 eItemKeyColumns.ID.ToString()이 들어가는 방식이다.

 

Debug.log와 Text 둘 다 잘 출력되는 것을 볼 수 있다. 

다만 게임 내에서는 깨진 글자로 보이는데, 이는 기본적으로 한글을 지원하지 않는 폰트이기 때문이다. 이를 위해서는 한글을 지원하는 폰트를 따로 설치해야한다.(Indian Poker 개발과정 참고)

 

※ csv 파일의 인코딩은 UTF-8로 진행한다. 엑셀에 작성할 때는 ANSI로 열어서 작성하지만, Unity에서 기본적으로 UTF-8로 읽기에, 작성 이후 저장은 UTF-8.

'팀프로젝트' 카테고리의 다른 글

[完] 인벤토리, 아이템 제작 요약  (1) 2024.01.21
[05] 아이템 슬롯 상속, Create Table  (0) 2023.12.14
[04] 아이템 슬롯 상속, 퀵슬롯  (1) 2023.12.14
[03] 인벤토리 기능  (0) 2023.12.11
[02] 아이템 슬롯  (0) 2023.11.28