코스모스 공작소

Unity 다이얼로그(대화창) 만들기 본문

프로그래밍/Unity

Unity 다이얼로그(대화창) 만들기

cosmos_studio_ 2019. 12. 5. 22:19
반응형

게임을 만들다 보면 자연스럽게 만들어야되는 부분이 다이얼로그(대화창)이다.

 

안드로이드 환경을 타겟으로 제작할 것이고 대화창이 떠있는 동안 게임의 일정 행동을 컨트롤하는 모듈도 추가하는 방식으로 진행하겠다. 

  1. canvas에 각 obj배치
  2. 기본 코드 작성
    1. 데이터로 이용할 클래스
    2. dialog를 출력하고 관리하는 클래스
      1. text 연속출력
      2. 출력 도중 터치시 완성된 문장 출력
      3. 완성된 문장 출력시 next 메세지 구석에 출력
      4. 완성된 문장 출력 뒤 터치시 다음 문장 출력
      5. 다이얼로그 출력시 현재 진행중이 코루틴 정지
  3. 코드 적용 인터페이스 
  4. 코드 테스트

 

1. Canvas에 배치

2. 코드

 1) 데이터로 이용할 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[System.Serializable]
public class dialog_info
{
    public string name;
    [TextArea(3,5)]
    public string content;
    public bool check_read;
}
 
[System.Serializable]
public class Dialog_cycle
{
    public string cycle_name;
    public List<dialog_info> info = new List<dialog_info>();
    public int cycle_index;
    public bool check_cycle_read;
}
cs

 dialog_info : 하나의 대화창에 속한 정보를 담은 클래스 

 dialog_cycle : 여러 연속적인 대화를 담은 하나의 묶음

 

 2) dialog를 출력하고 관리할 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class dialog : MonoBehaviour
{
    [SerializeField]
    public static dialog instance = null;
    public List<Dialog_cycle> dialog_cycles = new List<Dialog_cycle>(); //대화 지문 그룹
    public Queue<string> text_seq = new Queue<string>();                //대화 지문들의 내용을 큐로 저장한다.(끝점을 쉽게 판단하기 위해)
    public string name_;                                                //임시로 저장할 대화 지문의 이름
    public string text_;                                                //임시로 저장할 대화 지문의 내용
 
    public Text nameing;                                                //대화 지문 오브젝트에 있는 것을 표시할 오브젝트
    public Text DialogT;                                                //대화 지문 내용 오브젝트
    public Button Next_b;                                               //다음 버튼
    public GameObject dialog_obj;                                       //대화 지문 오브젝트
 
    IEnumerator seq_;
    IEnumerator skip_seq;
 
    public float delay;
    void Awake()    //싱글톤 패턴으로 어느 씬에서든 접근 가능하게 한다.
    {
        if (instance == null)
            instance = this;
 
        else if (instance != this)
            Destroy(gameObject);
 
        DontDestroyOnLoad(gameObject);
    }
    public IEnumerator dialog_system_start(int index)//다이얼로그 출력 시작
    {
        nameing = dialog_obj.GetComponent<parameter>().name_text;   //다이얼로그 오브젝트에서 각 변수 받아오기
        DialogT = dialog_obj.GetComponent<parameter>().content;
        Next_b = dialog_obj.GetComponent<parameter>().next_btn;
 
        foreach (dialog_info dialog_temp in dialog_cycles[index].info)  //대화 단위를 큐로 관리하기 위해 넣는다.
        {
            text_seq.Enqueue(dialog_temp.info);
        }
 
        dialog_obj.gameObject.SetActive(true);
        for (int i = 0; i < dialog_cycles[index].info.Count; i++//대화 단위를 순서대로 출력
        {
 
            nameing.text = dialog_cycles[index].info[i].name;
 
            text_ = text_seq.Dequeue();                                  //대화 지문을 pop
            Next_b.onClick.AddListener(() => { DisplayNext(index, i); });//다음으로 버튼에 함수 등록
            seq_ = seq_sentence(index, i);                               //대화 지문 출력 코루틴
            StartCoroutine(seq_);                                        //코루틴 실행
 
 
            yield return new WaitUntil(() =>
            {
                if (dialog_cycles[index].info[i].check_read)            //현재 대화를 읽었는지 아닌지
                {
                    return true;                                        //읽었다면 진행
                }
                else
                {
                    return false;                                       //읽지 않았다면 다시 검사
                }
            });
        }
 
 
        Next_b.onClick.RemoveAllListeners();                            
 
        dialog_cycles[index].check_cycle_read = true;                   //해당 대화 그룹 읽음
 
    }
 
    public void DisplayNext(int index, int number)                      //다음 지문으로 넘어가기
    {
        if (text_seq.Count == 0)                                        //다음 지문이 없다면
        {
            dialog_obj.gameObject.SetActive(false);                     //다이얼로그 끄기
        }
        StopCoroutine(seq_);                                            //실행중인 코루틴 종료
 
        dialog_cycles[index].info[number].check_read = true;            //현재 지문 읽음으로 표시
    }
 
    public IEnumerator seq_sentence(int index, int number)              //지문 텍스트 한글자식 연속 출력
    {
        skip_seq = touch_wait(seq_, index, number);                     //터치 스킵을 위한 터치 대기 코루틴 할당
        StartCoroutine(skip_seq);                                       //터치 대기 코루틴 시작
        DialogT.text = "";                                              //대화 지문 초기화
        foreach (char letter in text_.ToCharArray())                    //대화 지문 한글자씩 뽑아내기
        {
            DialogT.text += letter;                                     //한글자씩 출력
            yield return new WaitForSeconds(delay);                     //출력 딜레이
        }
 
        StopCoroutine(skip_seq);                                        //지문 출력이 끝나면 터치 대기 코루틴 해제
        IEnumerator next = next_touch(index, number);                   //버튼 이외에 부분을 터치해도 넘어가는 코루틴 시작
        StartCoroutine(next);
    }
 
    public IEnumerator touch_wait(IEnumerator seq, int index, int number)//터치 대기 코루틴
    {
        yield return new WaitForSeconds(0.3f);
        yield return new WaitUntil(() => Input.GetMouseButton(0));
        StopCoroutine(seq);                                              //대화 지문 코루틴 해제
        DialogT.text = text_;                                            //스킵시 모든 지문 한번에 출력
        IEnumerator next = next_touch(index, number);                    //대화 지문 코루틴 해제 됬기 때문에 다음 지문으로 가는 코루틴 시작
        StartCoroutine(next);                                                   
    }
 
    public IEnumerator next_touch(int index, int number)    //터치로 다음 지문 넘어가는 코루틴
    {
        StopCoroutine(seq_);
        StopCoroutine(skip_seq);
        yield return new WaitForSeconds(0.3f);
        yield return new WaitUntil(() => Input.GetMouseButton(0));
        DisplayNext(index, number);
    }
    
    public bool dialog_read(int check_index)          //index의 부분을 읽었는지 확인하는 함수
    {
        if (!dialog_cycles[check_index].check_cycle_read)
        {
            return true;
        }
        
        return false;
    }
}
cs

3. 코드 적용 인스펙터 화면

4. 대화 넣기

 

5. 테스트 코드

 조건이 있다. dialog_system_start가 코루틴이기 때문에 기본으로 진행되는 코루틴에만 삽입이 가능하다. 예를 들어 설명하겠다. 타이머를 만들어서 100부터 0까지 감소시킬 것이다. 그 중간에 90이 되는 순간에 대화로 일시정지하도록 하겠다. 또한 60초에 다음 대화 그룹을 출력하겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class test : MonoBehaviour
{
    public float time =100f;
    public float target;
    public float target_2;
    public Text time_text;
    public bool test_1;
    void Start()
    {
        if (test_1)
        {
            StartCoroutine("timer");
        }
        else
        {
            StartCoroutine("timer_action");
        }
        
    }
 
 
    IEnumerator timer()
    {
        yield return new WaitUntil(() => {
            if (time <= 0)
            {
                return true;
            }
            else
            {
 
                if (time <= target)
                {
                    if (dialog.instance.dialog_read(0&& !dialog.instance.running)
                    {
                        IEnumerator dialog_co = dialog.instance.dialog_system_start(0);
                        StartCoroutine(dialog_co);
                        
                        if (dialog.instance.dialog_read(0))
                        {
                            return false;
                        }
 
                    }else if (!dialog.instance.dialog_read(0&& !dialog.instance.running)
                    {
                        time -= Time.deltaTime;
                        time_text.text = time.ToString();
                    }
                }
                else
                {
                    time -= Time.deltaTime;
                    time_text.text = time.ToString();
                }
                
                return false;
            }
        });
    }
 
    IEnumerator timer_action()
    {
        yield return new WaitUntil(() => {
            if (time <= 0)
            {
                return true;
            }
            else
            {
 
                if (time <= target && time>=target_2)
                {
                    if (dialog.instance.dialog_read(0&& !dialog.instance.running)
                    {
                        IEnumerator dialog_co = dialog.instance.dialog_system_start(0);
                        StartCoroutine(dialog_co);
 
                        if (dialog.instance.dialog_read(0))
                        {
                            return false;
                        }
 
                    }
                    else if (!dialog.instance.dialog_read(0&& !dialog.instance.running)
                    {
                        time -= Time.deltaTime;
                        time_text.text = time.ToString();
                    }
 
                }else if(time <= target_2)
                {
                    if (dialog.instance.dialog_read(1&& !dialog.instance.running)
                    {
                        IEnumerator dialog_co = dialog.instance.dialog_system_start(1);
                        StartCoroutine(dialog_co);
 
                        if (dialog.instance.dialog_read(1))
                        {
                            return false;
                        }
 
                    }
                    else if (!dialog.instance.dialog_read(1&& !dialog.instance.running)
                    {
                        time -= Time.deltaTime;
                        time_text.text = time.ToString();
                    }
                }
                else
                {
                    time -= Time.deltaTime;
                    time_text.text = time.ToString();
                }
 
                return false;
            }
        });
    }
}
cs

 

대화 추가

 

 

지문 추가와 입력

 

지문 삭제

 

 

다이얼로그 삭제

 

중간 테스트

 

 

-샘플 프로젝트는 github에서 받아 보실수 있습니다.

https://github.com/wjs991/dialog

 

wjs991/dialog

Contribute to wjs991/dialog development by creating an account on GitHub.

github.com


** 2019.12.09 유티니 커스텀 에디터 추가

** 2022.10.13 데이터 클래스 변수 오표기 수정

반응형
Comments