유니티 일기

탑다운 2D RPG - 대화 시스템 구현하기

mky 2025. 8. 31. 15:54

골드메탈님의 유튜브 강의를 보고 배운 것을 정리하였습니다.

 

1. 오브젝트 관리

- 오브젝트의 ID와 NPC여부 변수를 생성

- 각 오브젝트마다 고유한 ID를 지정

using UnityEngine;

public class ObjData : MonoBehaviour
{
    public int id;
    public bool isNpc;
}

 

2. 대화 시스템

- 대화 데이터를 관리할 매니저 생성

- 대화 데이터를 저장할 Dictionary 변수 생성

- Add 함수를 사용하여 대화 데이터 입력 추가

  - 대화 하나에는 여러 문장이 들어있으므로 string[] 사용

- 지정된 대화 문장을 반환하는 함수 하나 생성

  - id로 대화 Get -> talkIndex로 대화의 한 문장을 Get

using System.Collections.Generic;
using UnityEngine;

public class TalkManager : MonoBehaviour
{
    Dictionary<int, string[]> talkData;

    public static TalkManager instance = null;
    void Awake()
    {
        if (instance == null)
            instance = this;

        talkData = new Dictionary<int, string[]>();
        GenerateData();
    }

    void GenerateData()
    {
        talkData.Add(1000, new string[] { "안녕?", "이 곳에 처음 왔구나?" });
        talkData.Add(2000, new string[] { "여어.", "이 호수는 정말 아름답지?", "사실 이 호수에는 무언가의 비밀이 숨겨져있다고 해." });
        talkData.Add(100, new string[] { "평범한 나무상자다." });
        talkData.Add(200, new string[] { "누군가 사용했던 흔적이 있는 책상이다." });
    }

    public string GetTalk(int id, int talkIndex)
    {
        if (talkIndex == talkData[id].Length)
            return null; // 더이상 남아있는 문장이 없음
        else
            return talkData[id][talkIndex];
    }
}

- 게임매니저에서 활용

  - 오브젝트 변수를 매개변수로 활용

- 대화가 모두 끝나야 액션이 끝나도록 설정을 바꾸어야 함 -> void함수에서 return은 강제 종료 역할

- talkIndex는 대화가 끝날 때 0으로 초기화

using TMPro;
using UnityEngine;
using UnityEngine.UI;


public class GameManager : MonoBehaviour
{
    public GameObject talkPanel;
    public TextMeshProUGUI talkText;
    public GameObject scanObject;
    public bool isAction; // 상호작용 중인지 아닌지 판단
    public int talkIndex; // 대화 인덱스

    public static GameManager instance = null;
    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    public void Action(GameObject scanObj)
    {
        isAction = true;
        scanObject = scanObj;
        ObjData objData = scanObject.GetComponent<ObjData>(); // 오브젝트의 데이터를 가져옴
        Talk(objData.id, objData.isNpc); // 대화
        //Debug.Log(objData.id + ", " + objData.isNpc);

        talkPanel.SetActive(isAction);
    }

    void Talk(int id, bool isNpc)
    {
        string talkData = TalkManager.instance.GetTalk(id, talkIndex);

        if (talkData == null) // 더이상 남아있는 문장이 없음
        {
            isAction = false;
            talkIndex = 0;
            return; // 대화 끝
        }
        if (isNpc)
        {
            talkText.text = talkData;
        }
        else
        {
            talkText.text = talkData;
        }

        isAction = true;
        talkIndex++;
    }

}

 

3. 초상화

- 초상화를 보여줄 Image UI 생성

- Image UI 접근을 위해 게임매니저에 변수 생성 & 할당

- NPC일 때만 Image가 보이도록 작성

- 초상화 데이터를 저장할 Dictionary 변수 생성

- 초상화 스프라이트를 저장할 배열 생성

- 지정된 초상화 스프라이트를 반환할 함수 생성

- NPC 표정은 문장과 1:1 매칭 -> 구분자와 함께 초상화 인덱스를 문장 뒤에 추가

- Split() : 구분자를 통하여 배열로 나눠주는 문자열 함수

- Parse() : 문자열을 해당 타입으로 변환해주는 함수

  - 문자열 내용이 타입과 맞지 않으면 오류 발생

using System.Collections.Generic;
using UnityEngine;

public class TalkManager : MonoBehaviour
{
    Dictionary<int, string[]> talkData;
    Dictionary<int, Sprite> portraitData;

    public Sprite[] portraitArr;

    public static TalkManager instance = null;
    void Awake()
    {
        if (instance == null)
            instance = this;

        talkData = new Dictionary<int, string[]>();
        portraitData = new Dictionary<int, Sprite>();
        GenerateData();
    }

    void GenerateData()
    {
        talkData.Add(1000, new string[] { "안녕?:2", "이 곳에 처음 왔구나?:1" });
        talkData.Add(2000, new string[] { "여어.:1", "이 호수는 정말 아름답지?:0", "사실 이 호수에는 무언가의 비밀이 숨겨져있다고 해.:1" });
        talkData.Add(100, new string[] { "평범한 나무상자다." });
        talkData.Add(200, new string[] { "누군가 사용했던 흔적이 있는 책상이다." });

        portraitData.Add(1000 + 0, portraitArr[0]);
        portraitData.Add(1000 + 1, portraitArr[1]);
        portraitData.Add(1000 + 2, portraitArr[2]);
        portraitData.Add(1000 + 3, portraitArr[3]);
        portraitData.Add(2000 + 0, portraitArr[4]);
        portraitData.Add(2000 + 1, portraitArr[5]);
        portraitData.Add(2000 + 2, portraitArr[6]);
        portraitData.Add(2000 + 3, portraitArr[7]);
    }

    public string GetTalk(int id, int talkIndex)
    {
        if (talkIndex == talkData[id].Length)
            return null; // 더이상 남아있는 문장이 없음
        else
            return talkData[id][talkIndex];
    }

    public Sprite GetPortrait(int id, int portraitIndex)
    {
        return portraitData[id + portraitIndex];
    }
}

 

using TMPro;
using UnityEngine;
using UnityEngine.UI;


public class GameManager : MonoBehaviour
{
    public GameObject talkPanel;
    public Image portraitImg;
    public TextMeshProUGUI talkText;
    public GameObject scanObject;
    public bool isAction; // 상호작용 중인지 아닌지 판단
    public int talkIndex; // 대화 인덱스

    public static GameManager instance = null;
    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    public void Action(GameObject scanObj)
    {
        isAction = true;
        scanObject = scanObj;
        ObjData objData = scanObject.GetComponent<ObjData>(); // 오브젝트의 데이터를 가져옴
        Talk(objData.id, objData.isNpc); // 대화
        //Debug.Log(objData.id + ", " + objData.isNpc);

        talkPanel.SetActive(isAction);
    }

    void Talk(int id, bool isNpc)
    {
        string talkData = TalkManager.instance.GetTalk(id, talkIndex);

        if (talkData == null) // 더이상 남아있는 문장이 없음
        {
            isAction = false;
            talkIndex = 0;
            return; // 대화 끝
        }
        if (isNpc)
        {
            talkText.text = talkData.Split(':')[0];

            portraitImg.sprite = TalkManager.instance.GetPortrait(id, int.Parse(talkData.Split(':')[1]));
            portraitImg.color = new Color(1, 1, 1, 1); // 알파값 1로 변경
        }
        else
        {
            talkText.text = talkData;
            portraitImg.color = new Color(1, 1, 1, 0);
        }

        isAction = true;
        talkIndex++;
    }

}