90.내일배움캠프 81일차 TIL <Unity Unity 2D 팀프로젝트- MartialGod:Reborn - 31일차> 08/04

정광훈(Unity_9기)·2025년 8월 4일

TIL (Today I Learned)

목록 보기
91/110
post-thumbnail

CSV를 이용한 대화 기능 구현

이런식으로 CSV로 작성하고


[System.Serializable]
public class DialogueData // 대화 데이터를 담는 그릇 역할
{
    public string ID;
    public string NpcName;
    public string Condition;
    public string Text;
}

using System.Collections.Generic;
using UnityEngine;

public class DialogueManager : MonoBehaviour
{
    public static DialogueManager Instance;
    public TextAsset csvFile; // 유니티 인스펙터에서 CSV 파일을 연결할 변수

    // NPC 이름, 상태, 대화 ID를 키로 사용하는 딕셔너리
    private Dictionary<string, List<DialogueData>> dialogueDatabase;

    public List<DialogueData> allDialogues;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            LoadDialogueData();
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 파싱작업
    private void LoadDialogueData()
    {
        dialogueDatabase = new Dictionary<string, List<DialogueData>>();

        string[] lines = csvFile.text.Split('\n'); // 데이터를 한 줄씩 나눔

        for (int i = 1; i < lines.Length; i++)
        {
            string line = lines[i];
            if (string.IsNullOrEmpty(line)) continue;

            string[] values = line.Split(","); // 한 줄의 콤마로 열을 나눔

            string replaceText = values[3].Replace("`", ","); // 대사에 콤마대신 썼던 `를 다시 콤마로 변환

            if (values.Length < 4) continue;

            // CSV 파일에서 읽어온 데이터를 기반으로 DialogueData라는 새로운 객체를 생성
            DialogueData data = new DialogueData 
            {
                ID = values[0],
                NpcName = values[1],
                Condition = values[2],
                Text = replaceText,
            };

            // 딕셔너리에 대화 데이터 저장
            string key = $"{data.NpcName}_{data.Condition}";
            if (!dialogueDatabase.ContainsKey(key))
            {
                dialogueDatabase[key] = new List<DialogueData>();
            }

            dialogueDatabase[key].Add(data);

            allDialogues.Add(data);
        }
    }
    
    // 해당 매개변수의 조건에 충족하는 대화를 가져옴
    public List<DialogueData> GetDialogues(string npcName, string condition) 
    {
        string key = $"{npcName}_{condition}";

        if (dialogueDatabase.ContainsKey(key))
        {
            return dialogueDatabase[key];
        }

        return new List<DialogueData>();
    }
}

private IEnumerator HandleDialogueFlow() // 상황에 맞는 대사 출력
{
    List<DialogueData> dialogue;

    // 첫 상호작용 대화일 경우(currentDialogueFlag가 0001이 아닌 경우)
    if (!HasDialogueFlag(DialogueFlags.HasSpokenFirstDialogue))
    {
        dialogue = DialogueManager.Instance.GetDialogues("UpgradeNpc", "First_Meet");
        yield return StartCoroutine(Dialogue(dialogue));
        // 현재 대화 상태를 첫 상호작용 한 걸로 표시 (0000 -> 0001로 변경) 
        SetDialogueFlag(DialogueFlags.HasSpokenFirstDialogue);
    }
    else
    {
        dialogue = DialogueManager.Instance.GetDialogues("UpgradeNpc", "Default");
        yield return StartCoroutine(Dialogue(dialogue));
    }

    dialogueCoroutine = null;
}

private IEnumerator Dialogue(List<DialogueData> dialogues) // 대화 코루틴
{
    SubtitleUI sUI = UIManager.Instance.GetUI<SubtitleUI>();
    sUI.isDialogue = true;

    foreach (DialogueData data in dialogues)
    {
        isEndDialogue = false;

        sUI.CallSubtitle(data.Text, true); // 대사 출력
        // isEndDialogue가 true가 될 때까지 현재 코루틴을 일시 중지 시킴
        yield return new WaitUntil(IsEndDialogue);
    }

    UIManager.Instance.Close<SubtitleUI>();
    UIManager.Instance.Open<TacticalCoreUI>(); // 전술 코어 UI 활성화

    sUI.isDialogue = false;
}

HandleDialogueFlow()에서 조건에 맞는 대화를 가져오고
Dialogue()에서 해당 조건의 맞는 대사를 data.Text로 출력한다.

0개의 댓글