저번 프로젝트의 경우 원래는 게임의 스토리 및 세계관을 다룬 다이얼로그가 적용되었어야 했다.
여기서 다이얼로그란 이른바 미연시 같은 데서 텍스트로 대사를 출력한다든지, 스토리 및 혼잣말 등을 출력하는 것을 말한다.
이런 식으로 일지, 대사에 대한 정보도 있었지만 결국엔 시간상 적용하지 못했던 부분이다.
실제로 다른 팀원이 원래는 대사 작업하려고 만들어 둔 UI다. 결국에는 써먹지 못했지만, 아쉬운 마음에 만약 대사를 구현했더라면 어떻게 구현했을까 고민해보는 시간을 가지기로 했다.
다이얼로그 : 대화, 또는 사용자 인터페이스에서 특정 동작이나 정보 표시를 위해 나타나는 작은 창
다이얼로그는 처음 만들어보는 기능이 될 것 같지만, 사실 어디까지나 '구현' 자체는 그렇게 어려울 것 같지 않다. 다만 어떻게 구성할 건지에 대한 고민이 필요해 보인다.
여러 가지 방법을 찾아봤을 때 우선 참고할 만한 내용으로 아래 글을 참고했다.
다만 여기서 나는 내용을 어느 정도 참고하되 캐릭터의 이미지까지 바꾸는 효과를 줄 수 있는 방법에 대해 생각해보면서 아래와 같이 데이터를 구성했다.
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Dialog_Info
{
public string Name;
[TextArea] public string Context;
public Sprite ChararcterImage;
public bool IsDialogReaded;
}
[System.Serializable]
public class Dialog_Cycle
{
public string CycleName;
public int CycleIndex;
public List<Dialog_Info> Info = new List<Dialog_Info>();
public bool IsCycleReaded;
}
이 부분은 우선 초기 테스트를 위해 이와 같이 디자인하였고, 추후에 Name과 Character Image에 해당하는 부분은 캐릭터 데이터로 따로 구성하는 방식을 취해보려고 생각 중이다.
그 다음 DialogEvent에 해당하는 싱글톤 관리자를 다음과 같이 구성했다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DialogEvent : MonoBehaviour
{
public static DialogEvent Instance { get; private set; }
[SerializeField] private List<Dialog_Cycle> _dialogueCycle = new List<Dialog_Cycle>();
private Queue<string> _textSequence = new Queue<string>();
private string _context;
[SerializeField] private Text _nameText;
[SerializeField] private Text _contextText;
[SerializeField] private SpriteRenderer _spriteRenderer;
[SerializeField] private Button _nextButton;
[SerializeField] private GameObject _dialogUI;
IEnumerator _sequence;
IEnumerator _skipSequence;
[SerializeField] private float _delay;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public IEnumerator DialogSystem_Start(int index)
{
foreach(Dialog_Info info in _dialogueCycle[index].Info)
{
_textSequence.Enqueue(info.Context);
}
_dialogUI.gameObject.SetActive(true);
for(int i = 0; i < _dialogueCycle[index].Info.Count; i++)
{
_nameText.text = _dialogueCycle[index].Info[i].Name;
_context = _textSequence.Dequeue();
_spriteRenderer.sprite = _dialogueCycle[index].Info[i].ChararcterImage;
_nextButton.onClick.AddListener(() => {DisplayNext(index, i); });
_sequence = Sequence_Sentence(index, i);
StartCoroutine(_sequence);
yield return new WaitUntil(() =>
{
if (_dialogueCycle[index].Info[i].IsDialogReaded)
{
return true;
}
else
{
return false;
}
});
}
_nextButton.onClick.RemoveAllListeners();
_dialogueCycle[index].IsCycleReaded = true;
}
public void DisplayNext(int index, int number)
{
if(_textSequence.Count == 0)
{
_dialogUI.gameObject.SetActive(false);
}
StopCoroutine(_sequence);
_dialogueCycle[index].Info[number].IsDialogReaded = true;
}
public IEnumerator Sequence_Sentence(int index, int number)
{
WaitForSeconds delay = new WaitForSeconds(_delay);
_skipSequence = TouchWait(_sequence, index, number);
StartCoroutine(_skipSequence);
_contextText.text = "";
foreach(char letter in _context.ToCharArray())
{
_contextText.text += letter;
yield return delay;
}
StopCoroutine(_skipSequence);
IEnumerator next = TouchNext(index, number);
StartCoroutine(next);
}
public IEnumerator TouchWait(IEnumerator sequence, int index, int number)
{
yield return new WaitForSeconds(0.3f);
yield return new WaitUntil(() => Input.GetMouseButtonDown(0));
StopCoroutine(_sequence);
_contextText.text = _context;
IEnumerator next = TouchNext(index, number);
StartCoroutine(next);
}
public IEnumerator TouchNext(int index, int number)
{
StopCoroutine(_sequence);
StopCoroutine(_skipSequence);
yield return new WaitForSeconds(0.3f);
yield return new WaitUntil(() => Input.GetMouseButtonDown(0));
DisplayNext(index, number);
}
public bool DialogIsReaded(int dialogIndex)
{
if (!_dialogueCycle[dialogIndex].IsCycleReaded)
{
return true;
}
return false;
}
}
화면은 우선 이와 같이 구성했다.
그 다음 테스트는 아래와 같이 간단하게 작성하고 실행해보았다.
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
IEnumerator _dialogCoroutine;
void Start()
{
_dialogCoroutine = DialogEvent.Instance.DialogSystem_Start(0);
StartCoroutine(_dialogCoroutine);
}
}
주말 일정이 있었던 탓에 공부를 그다지 못했고, 다음 공부할 내용만 우선 아래에 링크로 첨부해두고자 한다.