#region
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
#endregion
public class DialogSystem : MonoBehaviour
{
[SerializeField] Speaker[] speakers;
[SerializeField] DialogData[] dialogs;
[SerializeField] bool isAutoStart = true;
bool isFirst = true;
int currentDialogIndex = -1;
int currentSpeakerIndex;
const float TypingSpeed = 0.1f;
bool isTypingEffect;
bool isClicked;
void Setup()
{
for (int i = 0; i < speakers.Length; ++i)
{
SetActiveObjects(speakers[i], false);
speakers[i].imageDialog.GetComponent<Button>().onClick.AddListener(() => isClicked = true);
speakers[i].speakerImage.gameObject.SetActive(true);
}
Debug.Log("Dialog SetUp");
}
public bool UpdateDialog()
{
if (isFirst)
{
Setup();
if (isAutoStart) SetNextDialog();
isFirst = false;
}
if (!isClicked) return false;
isClicked = false;
if (isTypingEffect)
{
isTypingEffect = false;
StopCoroutine("OnTypingText");
speakers[currentSpeakerIndex].textDialogue.text = dialogs[currentDialogIndex].dialogue;
return false;
}
if (dialogs.Length > currentDialogIndex + 1) { SetNextDialog(); }
else
{
EndDialog();
return true;
}
return false;
}
void SetNextDialog()
{
SetActiveObjects(speakers[currentSpeakerIndex], false);
currentDialogIndex++;
currentSpeakerIndex = dialogs[currentDialogIndex].speakerIndex;
// SoundManager.Instance.PlayOnlyOneEffect(dialogs[currentDialogIndex].effectSound);
// SoundManager.Instance.PlayBackgroundMusic(dialogs[currentDialogIndex].backgroundSound);
SetActiveObjects(speakers[currentSpeakerIndex], true);
if (speakers[currentSpeakerIndex].textName is not null) speakers[currentSpeakerIndex].textName.text = dialogs[currentDialogIndex].name;
StartCoroutine("OnTypingText");
}
void SetActiveObjects(Speaker speaker, bool visible)
{
speaker.imageDialog.gameObject.SetActive(visible);
speaker.textName?.gameObject.SetActive(visible);
speaker.textDialogue.gameObject.SetActive(visible);
Color color = speaker.speakerImage.color;
color.a = visible ? 1 : 0f;
speaker.speakerImage.color = color;
}
IEnumerator OnTypingText()
{
int index = 0;
isTypingEffect = true;
while (index < dialogs[currentDialogIndex].dialogue.Length)
{
speakers[currentSpeakerIndex].textDialogue.text = dialogs[currentDialogIndex].dialogue.Substring(0, index);
index++;
yield return new WaitForSeconds(TypingSpeed);
}
isTypingEffect = false;
}
void EndDialog()
{
foreach (Speaker speaker in speakers)
{
SetActiveObjects(speaker, false);
speaker.speakerImage.gameObject.SetActive(false);
}
// SoundManager.Instance.StopBackgroundMusic();
}
}
[Serializable] public struct Speaker
{
public Image speakerImage;
public Image imageDialog;
public TextMeshProUGUI textName;
public TextMeshProUGUI textDialogue;
}
[Serializable] public struct DialogData
{
public int speakerIndex;
public string name;
[TextArea(3, 5)]
public string dialogue;
public AudioClip effectSound;
public AudioClip backgroundSound;
}
사용 방법
아래의 사진과 같이 화자(Speakers)들을 생성한 뒤, 다이얼로그에 화자의 인덱스를 맞게 생성하면 된다.
배경음악이나 효과음을 사용하고 싶다면, 해당 다이얼로그에 오디오 클립을 넣어주고 주석처리된 부분처럼 Sound Player를 연결해 오디오를 재생할 수 있다.
다이얼로그가 끝나는 시점은 UpdateDialog() 메서드가 true를 반환 할 때로,
IEnumerator Dialog() { yield return new WaitUntil(() => dialogSystem.UpdateDialog()); }
이처럼 코루틴으로 끝날 때 까지 대기할 수 있다.
타이핑 효과로 글자가 전부 출력되지 않은 상황에 Image Dialog를 클릭 시 타이핑 효과는 종료되고 즉시 텍스트가 전부 출력된다.
주의 사항
Image Dialog는 Button 컴포넌트를 장착해야 한다.
테스트
'Unity > Tip' 카테고리의 다른 글
Unity 인터넷 연결 확인하기 (0) | 2024.05.31 |
---|---|
Google Mobile Ads Unity Plugin v9.0.0 업데이트 정보 (0) | 2024.05.23 |
Unity 코드 문서화 : summary 태그 활용 (2) | 2024.04.26 |
뷰포트 영역 안에 있는지 검사하는 방법 (0) | 2024.03.21 |
콜라이더 충돌 검사 : Physics.Overlap vs NonAlloc (0) | 2024.01.05 |