게임개발 예제모음

CSV파일을 로드하여 TEXT 출력 본문

유니티 메모장

CSV파일을 로드하여 TEXT 출력

게임을만들어보자 2025. 6. 6. 17:22

○ 구현목표

- 게임을 진행하는 도중 등장인물들간의 대화를 작성하게 되는경우는 거의 필수적으로 발생
- 각각의 대화를 NPC오브젝트에 직접 하드코딩을 하는 방법도 있으니 유지보수가 어렵고 코드가 지저분해진다
- 별도의 CSV파일을 작성하여 내용을 읽어들여 TEXT를 뿌려주는 기능을 구현한다
- Resources폴더를 사용하지 않는다

요구조건
- TextMeshPro에서 사용할 폰트는 이미 설치되어있다고 가정한다

 

○ 참고자료

https://gist.github.com/SiarheiPilat/de4688651f106e7993a7e2fdb743cac4

 

A lightweight CSV reader for Unity.

A lightweight CSV reader for Unity. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

  ※ 국내에서 찾아볼 수 있는 대부분의 자료는 위 링크를 참고하여 작성된 자료들이라고 생각됨

  ※ 이 포스트에서도 동일하게 위 자료를 참고하여 약간의 커스터마이징을 진행함

 

 

○ 대화에 사용될 CSV파일 준비 (Sample.csv)

  ※ 한글을 출력하고 싶다면 반드시 UTF-8 형식으로 작성되어야 함

단순 구현을 목표로 하기 때문에 메세지는 한줄로 한정

 

○ 대화용 UI구성 

  ※ 배경(Box)은 [ UI → panel ], 텍스트는 [ UI → Text - TextMeshPro]

 

 

○ CSV파일 임포트

  ※ 상기 참고자료의 코드를 그대로 사용하게 되면 반드시 Resources폴더를 사용해야 함

  ※ 구현 목표에 따라 별도의 폴더에 임포트 한다

 

 

○ CSV파일 로더 작성

  ① 로더 전용 오브젝트 작성 [ Create Empty ]를 통하여 비어있는 오브젝트 작성

  ② 프로젝트 윈도우에서 [ Create   MonoBehaviour Script ]를 통해 LoadCsv스크립트 작성 및 어태치

  ③ 스크립트 작성

using System.Collections.Generic;
using UnityEngine;
using System.Text.RegularExpressions;


public class LoadCsv : MonoBehaviour
{
    static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
    static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";
    static char[] TRIM_CHARS = { '\"' };

    public static List<Dictionary<string, object>> Read(TextAsset file)  
    //메소드에서 매개변수로 CSV를 직접 받아 오도록 작성
    {
        var list = new List<Dictionary<string, object>>();
        //CSV파일을 직접 매개변수로 사용하기 때문에 Resources.Load는 필요 하지 않음
        //Resources폴더를 사용하지 않음

        var lines = Regex.Split(file.text, LINE_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;

            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);
        }
        return list;
    }
}

  ※ NPC오브젝트 등 해당 스크립트를 언제든지 불러와서 사용할 수 있도록 public static으로 작성

  ※ 위 메소드를 호출하는 기본 형

//메소드 호출
List<Dictionary<string, object>> data = LoadCsv.Read(filename);

//값 출력
Debug.Log(data.[int 행번호]["칼럼명"])
//앞 [] : int타입 행번호, 뒤 [] : string타입 칼럼명을 쌍따옴표("") 안에 직접 입력

 

 

○ NPC역할을 하게 될 오브젝트 생성 및 스크립트 작성

  ① Empty오브젝트에 스크립트(TextManager)만 추가하여 간단하게 진행

 

  ② 스크립 작성 - (1)

using TMPro;
using UnityEngine;

public class TextManager : MonoBehaviour
{
    [SerializeField] TextAsset csvFile;  //불러오게 될 CSV파일용 변수
    [SerializeField] TextMeshProUGUI nameTextBox;    //nameBox의 text 변수
    [SerializeField] TextMeshProUGUI messageTextBox; //messageBox의 text 변수
}

 

  ③ 스크립트 작성 - (2) : 선언한 변수에 오브젝트 및 어셋 어태치

 

  ④ 스크립트 작성 - (3) : CSV파일을 읽어들이기 위한 구조체 작성 및 구조체 배열 선언

TextSturct[] myText; //② 구조체 배열 선언


//① 구조체 선언
struct TextSturct
{
    public string nameText;
    public string messageText;
}

    ※ 보통 CSV파일의 내용이 단 한줄만 존재하는 것은 아니기 때문에 구조체 배열을 선언 하여 진행

    ※ 이 포스팅의 내용처럼 단 한줄만 출력한다면 구조체 및 배열없이 직접 텍스트를 출력해도 무방함

 

  ⑤ 스크립트 작성 - (4) : LoadCSV 호출 메소드 작성

using System.Collections.Generic;

public class TextManager : MonoBehaviour
{
    void GetText(TextAsset filename)
    {
        List<Dictionary<string, object>> data = LoadCsv.Read(filename);
        //LoadCsv의 메소드를 호출하여 CSV파일을 list에 로딩
        myText = new TextSturct[data.Count];
        //CSV파일을 배열에 저장

        for (int i = 0; i < data.Count; i++) //각각의 값을 구조체 배열에 저장
        {
            string tempName = data[i]["NAME"].ToString();
            string tempText = data[i]["TEXT"].ToString();
            myText[i].nameText = tempName;
            myText[i].messageText = tempText;
        }
    }
}

 

  ⑥ 스크립트 작성 - (5) : Update 작성

void Update()
{
    GetText(csvFile);  //csvFile에 어태치한 CSV파일을 매개변수로 하여 메소드 호출
    
    nameTextBox.text = myText[0].nameText;       //nameText 출력
    messageTextBox.text = myText[0].messageText; //messageText 출력
}

  ※ myText[0]만 출력한 이유는 샘플 CSV가 한줄짜리 데이터이기 때문임

  ※ 필요에 따라 for 또는 if 등등을 활용하여 커스텀 하여 사용

 

 

○ 실행 테스트

 

 

○ 스크립트 전문

  ① LoadCsv.cs

using System.Collections.Generic;
using UnityEngine;
using System.Text.RegularExpressions;

public class LoadCsv : MonoBehaviour
{
    static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
    static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";
    static char[] TRIM_CHARS = { '\"' };

    public static List<Dictionary<string, object>> Read(TextAsset file)
    {
        var list = new List<Dictionary<string, object>>();
        var lines = Regex.Split(file.text, LINE_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;

            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);
        }
        return list;
    }
}

 

  ② TextManager.cs

using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class TextManager : MonoBehaviour
{
    [SerializeField] TextAsset csvFile;
    [SerializeField] TextMeshProUGUI messageTextBox;
    [SerializeField] TextMeshProUGUI nameTextBox;
    
    TextSturct[] myText;

    void Update()
    {
        GetText(csvFile);
        nameTextBox.text = myText[0].nameText;
        messageTextBox.text = myText[0].messageText;
    }

    void GetText(TextAsset filename)
    {
        List<Dictionary<string, object>> data = LoadCsv.Read(filename);
        myText = new TextSturct[data.Count];

        for (int i = 0; i < data.Count; i++)
        {
            string tempName = data[i]["NAME"].ToString();
            string tempText = data[i]["TEXT"].ToString();
            myText[i].nameText = tempName;
            myText[i].messageText = tempText;
        }
    }

    struct TextSturct
    {
        public string nameText;
        public string messageText;
    }
}