본문 바로가기
유니티 최고/유니티 구현

[Unity] 유니티 상호작용이 가능한 대화 시스템 구현[TaskCompletionSource를 이용한]

by Lee_story_.. 2024. 10. 17.
728x90

 

글들이 점점 뜸해지네요... 

적을 것들은 많아지는데! 진행하는 프로젝트가 점점 길어져서..

 

그래서 오늘은 끄적끄적거릴 준비 해왔습니다!

 

이번에 제작중인 게임은 약간의 스토리가 있는 공포게임을 계획 중이라 그에 대한 다양한 기능들이 많이 필요했습니다...

 

그중 이번 글에서는 상호작용이 가능한 대화시스템을 만들어 보려고 합니다.

물론 상호작용이라는 것이 대화가 통한다는 게 아닌, 값을 전달하여 답을 받아볼 수 있는 그런 시스템!

 

 

저는 간단하게 대화가 진행되고, space키를 이용해서 다음 대화로 넘어가게끔 구성해 보았습니다. 

 

 

 

 

 

 

 

그럼 시작!


 

가장 먼저 위와 같이 구현하기 위해서는 2가지 정도 기능이 필요할 것으로 보입니다. 

1. 대화텍스트를 구성하는 기능

2.  사용자의 입력에 따라 다음 상호작용을 진행하는 기능

 

 

물론 위와 같이 space를 가지고만 진행한다면 배열에 텍스트를 넣어놓고 대충 카운트 해준 뒤 하나씩 출력해 주면....

int count=0;
public string[] texts = {"a", "234", "4444", "asdf", "ffff", "eee",....};

if (Input.GetKeyDown(KeyCode.Space)) // 텍스트 처리
        {
            text.text=texts[count];
            count+=1;
            return;
        }

 

위처럼 쉽게 구성할 수 있겠지만... 이경우는 음....  약간 부족한 느낌....

그래서 이번에는 Task를 이용하여 작성해 보았습니다!

 

 

Task!

Task는 뜻 자체로 "작업"을 뜻하고,

함수가 완료될 때, 값이 반환될 때, 등등의 상황에서 그 상황을 작업으로 할당하여

그 작업을 기다리거나, 따로 진행할 때 유용하게 사용됩니다. 

 

이와 관련된 많은 함수들이 존재하고, 처리를 위한 자료형들도 존재하지만!

 

이번에는 

TaskCompletionSource ==> 작업이 완료되었는지를 판별 및 대기

 

정확히는 TaskCompletionSource의 결괏값이 true가 될 때까지 대기하는 객체로,

아래와 같이 설정해 줄 수 있습니다. 

TaskCompletionSource.SetResult(true);
TaskCompletionSource.SetResult(false);

 

...... 설명할게 많이 없네요...

일단 여기까지 알아보고! 

 

TaskCompletionSource를 사용해 보도록 하겠습니다. 

(다른 부분들은 추후에 공부해 오도록 하겠습니다..)

 

 

 

그럼 구현!


코드 자체는 매우 매우 간단하게 구성할 수 있었습니다. 

 

 

가장 먼저 텍스트를 출력하는 부분은 아래처럼 구성해 주었습니다. 

using System.Threading.Tasks;


public async Task MakeText(Text text, string textStr)
    {
        string fullText = textStr;
        text.text = ""; // 초기화
        float delay = 3f / fullText.Length;

        for (int i = 0; i <= fullText.Length; i++)
        {
            text.text = fullText.Substring(0, i);
            await Task.Delay((int)(delay * 1000)); // 지연 시간
        }
    }

 

 

그리고 Task를 관리하는 부분은 아래처럼 구성해 주었습니다. 

    [HideInInspector]
    public TaskCompletionSource<bool> ChoiceCompletionSource_Text;


    public async Task TextWait()
    {
        StageGame_Scr.ChoiceCompletionSource_Text = new TaskCompletionSource<bool>();
        await StageGame_Scr.ChoiceCompletionSource_Text.Task;
    }
    
    
    public void CompleteChoice_Text()
    {
        // TaskCompletionSource의 작업 완료 처리
        if (ChoiceCompletionSource_Text != null)
        {
            ChoiceCompletionSource_Text.SetResult(true);
            ChoiceCompletionSource_Text = null;
        }
    }

 

코드는 끝...!

 

이제 함수를 사용하고자 하는 위치에 추가해 주고,

 

 

사용자 입력 처리부 Update 문에 추가해주면

 

텍스트 입력을 위한 MakeText() 함수,

사용자의 입력을 위해 대기하는 TextWait()  함수    >>  * TaskCompletionSource를 생성해 주면 기본값은 false!

그리고 TextWait을 끝내줄 CopleteChoice_Text() 함수

 

 

이 3가지의 함수를 이용하여,

npc의 설명/답  >> [사용자의 입력]  >> npc의 설명/ 답 >> [사용자의 입력] >>.....

순으로 진행할 수 있게 됩니다!

 

 

물론 TextWait함수를 MakeText함수 내부로 넣어주면 텍스트를 출력하며 대기하겠지만,

저는 대기하지 않는 텍스트 들도 있어서 위처럼 구현해 보았습니다. 

 

 

+ 그리고 추가적으로, 

Dotween을 사용한다면 아래와 같은 함수로, TextMeshPro를 구성해 주고,

완료함과 동시에 똑같이 대기 상태로 만들어 줄 수도 있습니다!

    public async static Task TMPDOText(TextMeshProUGUI text,string temp, float duration,bool TextComplete=false)
    {
        text.maxVisibleCharacters = 0;
        text.text = temp;

        await DOTween.To(() => text.maxVisibleCharacters, x => text.maxVisibleCharacters = x, text.text.Length, duration)
                 .AsyncWaitForCompletion();

        if (TextComplete) // 기다림이 필요할 경우 스페이스바를 통해 다음 글로 넘어감  >> 설명할때만? ㅇㅇ 
        {
            ChoiceCompletionSource_Text = new TaskCompletionSource<bool>();
            await ChoiceCompletionSource_Text.Task;
        }
    }

 

 

끝!


코드가 길지는 않았지만,

게임을 플레이하며 플레이어가 좀 더 대화에 참여하는 느낌을 줄 수 있어서 좋았습니다.

 

물론 위에서는 단순 space만을 이용하여 구성했지만,

사용자의 선택지가 필요하거나, 대기 시간이 필요할 경우에 유용하게 사용할 수 있을 것 같습니다. 

 

 

최근 프로젝트를 진행하며, async / await가 정확히 어떤 식으로 사용되는지,

얼마나 유용한지에 대해 깨닫고 있는 중인데... 

 

Threading / Task / TaskCompletionSource까지 할게 점점 많아져서....

좋은 것 같습니다! 

 

 

 

 

 

다음글은 좀 더 빨리  많은 글들과 돌아오겠습니다!

 

 

 

 

댓글