유니티 일기
탑다운 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++;
}
}