유니티 RPG - 43. NPC 대화창 UI 구현
Intro
이번 포스팅에서는 NPC를 만들고 NPC와 상호작용하면 대화가 나오는것을 구현해보았습니다.
대화창 GUI
대화창 GUI는 TMP(Text Mesh Pro)를 사용하였습니다.
TMP를 사용하면 한글이 깨지는 문제가 발생하는데, 저는 무료 폰트하나를 다운로드 받아와 TMP 에셋으로 만들어주었습니다.
Window - Text Mesh Pro - Font Asset Creator 를 열어줍니다.
- Source Font File : 다운로드 받은 폰트파일(.ttf)
- Sampling Point Size : Custom Size 자유롭게 설정 => 이후 깨짐현상이 발생하면 적절히 조절
- Atlas Resolution : 해상도가 높을수록 좋으나, 성능 이슈 고려
- Character Set : 한글을 사용하기위해 Unicode Range(Hex)
- Unicode Range 를 선택하면 아래에 Character Sequence(유니코드 범위) 를 지정할 수 있음
- 가장 기본 한글 음절범위(AC00 ~ D7A3)를 입력해주면 됨.
- Render Mode : SDFAA
Generate Font Atlas 를 누르면 아래에 결과가 나오고 Save 버튼을 눌러 TMP 에셋을 만들 수 있습니다.
DialogueDataSO
DialogueDataSO 클래스는 NPC 이름과 NPC의 대화내용을 SO로 저장합니다.
using UnityEngine;
[CreateAssetMenu(fileName = "NewDialogue", menuName = "Datas/DialogueData")]
public class DialogueDataSO : ScriptableObject
{
public string npcName; // NPC 이름
[TextArea(2, 10)]
public string[] sentences; // 대화 내용
}
NPC
NPC는 우선 간단하게 큐브로 대체하였습니다.
플레이어가 가까이 왔을 때 상호작용할 수 있도록 하였습니다.
using UnityEngine;
public class NPC : MonoBehaviour
{
[SerializeField] private DialogueDataSO dialogueData;
[SerializeField] private GameObject dialogueUI;
private bool isPlayerInRange = false; // 플레이어가 범위안에 있는지 여부
private BoxCollider rangeCol; // 상호작용 범위
private void Awake()
{
rangeCol = GetComponent<BoxCollider>();
}
private void Update()
{
if(isPlayerInRange && Input.GetKeyDown(KeyCode.G) && !dialogueUI.activeSelf)
{
DialogueManager.Instance.StartDialogue(dialogueData);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
isPlayerInRange = true;
}
}
private void OnTriggerExit(Collider other)
{
if(other.CompareTag("Player"))
{
isPlayerInRange = false;
}
}
}
- BoxCollider 를 사용하여 플레이어가 가까이 왔을 때 상호작용 할 수 있도록 하였습니다.
- 상호작용시 해당 NPC의 대화 데이터를 DialogueManager에 넘겨줍니다.
DialogueManager
DialogueManager 클래스는 대화 또는 대사가 사용되는 모든곳에서 호출되고 대화의 흐름을 관리합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class DialogueManager : Singleton<DialogueManager>
{
[SerializeField] private GameObject dialogueUI; // 대화창 오브젝트
[SerializeField] private TMP_Text npcNameText; // NPC 이름 텍스트
[SerializeField] private TMP_Text dialogueText; // 대화 텍스트
private Queue<string> pages = new Queue<string>();
private bool isTypipng = false; // 대화 타이핑 효과
private float typingSpeed = 0.05f; // 타이핑 속도
private void Update()
{
if(dialogueUI.activeSelf && Input.GetKeyDown(KeyCode.G))
{
DisplayNextPage();
}
}
// 대화시작
public void StartDialogue(DialogueDataSO dialogue)
{
dialogueUI.SetActive(true); // 대화창 UI 활성화
npcNameText.text = dialogue.npcName;
pages.Clear(); // 이전 대화 내용 초기화
// 각 문장을 "--" 기준으로 나누어 페이지 저장
foreach(string sentence in dialogue.sentences)
{
SplitSentenceToPages(sentence);
}
DisplayNextPage();
}
// 다음페이지 대화내용 출력
public void DisplayNextPage()
{
if (isTypipng) return;
if(pages.Count == 0)
{
EndDialogue();
return;
}
string page = pages.Dequeue();
StopAllCoroutines();
StartCoroutine(TypePage(page));
}
// 기호 "--"를 기준으로 대화 페이지 나누기
private void SplitSentenceToPages(string sentence)
{
string[] pagesArray = sentence.Split(new string[] { "--" }, System.StringSplitOptions.RemoveEmptyEntries);
foreach (string page in pagesArray)
{
pages.Enqueue(page.Trim()); // 공백 제거 후 큐에 저장
}
}
// 타이핑 효과
private IEnumerator TypePage(string page)
{
isTypipng = true;
dialogueText.text = "";
foreach(char letter in page.ToCharArray())
{
dialogueText.text += letter;
yield return new WaitForSeconds(typingSpeed);
}
isTypipng = false;
}
// 대화창 비활성화
private void EndDialogue()
{
dialogueUI.SetActive(false);
}
}
- 대화는 Queue 자료형을 이용하여 저장하였습니다.
- SO 데이터의 대화내용텍스트의 기호 “–” 를 기준으로 페이지를 나누어, 긴 텍스트의 경우 여러 페이지에 걸쳐 출력되도록 하였습니다.
- 코루틴을 사용하여 텍스트가 한번에 출력되는것이 아닌, 한글자씩 빠르게 출력되도록하는 타이핑 효과를 추가해보았습니다.
대화를 구현해보았으며, NPC의 경우 다양한 NPC가 존재하고 각 NPC 마다의 기능이 다르기때문에(상점, 퀘스트 등) NPC 클래스를 추상클래스로 만들어 개별 NPC들을 만드는것이 좋을것 같습니다.
댓글남기기