스터디

유니티 C# 테크닉 - Async, Update, Coroutine

장꼬미 2021. 7. 28. 22:08

일정 시간을 지연시킨후 어떤 동작을 하고 싶을때 유니티 C#에서는 몇가지 선택을 할 수 있다.

 

Async await, Update, Coroutine이 그것으로 각각 동작방식과 구현 방식 차이점등을 살펴보자.

 

먼저 가장 단순하고 예전부터 사용해 왔던 Update 방식이 있다.

 

    public float _TargetTime = 0f;
    float _deltaTime = 0f;
    bool _chkUpdate = false;
	
    /// <summary>
    /// Update 로직
    /// </summary>

    public void OnUpdateBtn()
    {
        _deltaTime = _TargetTime;
        _chkUpdate = true;
        Debug.Log("start of update");
    }

    private void Update()
    {
        if(_chkUpdate)
        {
            if (_deltaTime > 0)
            {
                _deltaTime -= Time.deltaTime;
            }
            if (_deltaTime <= 0)
            {
                _chkUpdate = false;
                Debug.Log("end of update");
            }
        }
    }

 유니티 MonoBehaviour의 라이프 사이클에 따라 매 프레임마다 호출되는 Update를 이용하여 이전 프레임 이후 지난 시간을 알려주는 Time.deltaTime을 사용, 이를 누적하여 일정 시간을 체크하여 동작을 수행한 뒤 종료시점을 확인하여 동작을 수행하는 간단한 로직이다.

 

 이를 활용하면 특정 시점을 비교적 정확하게 타겟팅 하여 동작을 수행하게 할 수 있지만

1. 필드가 복잡해지고

2. 로직을 파악하는데 직관성이 떨어지며

3. 시간과 무관한 동작의 종료를 체크하기 위해서는 추가적인 로직이 필요해지게 된다.

4. 로직이 _deltaTime 변수 하나에 의존하고 있기 때문에 하나의 클래스 안에서 이를 여러번 동작시킬수 없다. 

 

두번째는 많이 사용해서 익숙할 코루틴이다.

    Coroutine _coroutine = null;
    
    /// <summary>
    /// Coroutine 로직
    /// </summary>

    public void OnCoroutineBtn()
    {
        if(_coroutine != null) StopCoroutine(_coroutine);
        _coroutine = StartCoroutine(CoDelayTest(2));
    }

    IEnumerator CoDelayTest(float delay)
    {
        Debug.Log("start coroutine");
        yield return new WaitForSeconds(delay);
        Debug.Log("end of coroutine");

참고로 StopCoroutine을 삭제하면 CoDelayTest를 여러번 동작시킬 수 있다.

 

코루틴은 WaitForSecond, WaitForFixedUpdate, WaitForEndOfFrame, null, WaitUntil 등 비교적 다양한 조건과 함수형 결과에 대하여 yield를 걸 수 있어서 Update보다 강력하게 지연을 수행할 수 있다. 특히 waitUntil/WaitWhile등에 람다식을 붙이면 훨씬 강력한 지연 조건을 설정 할 수 있다.

 

또 그렇게 사용한 경험이 많지는 않지만 한 객체 안에서 코루틴 함수를 여러번 실행하여도 개별 딜레이에 맞춰 실행되기 때문에 인자를 사용하여 다양한 방식으로 사용할 수 있다. 

 

 

세번째는 시간이 아닌 특정 완료 시점을 대기하는 Async/Await이다.

    /// <summary>
    /// Async 로직
    /// </summary>
    public void OnAsyncBtn()
    {
        FirstTask();
    }

    async void FirstTask() 
    {
        Debug.Log("go first"); 
        await Task.Delay(3000); 
        Debug.Log("go after"); 
        await SecondTask();
        Debug.Log("all done");
    }

    async Task SecondTask()
    {
        Debug.Log("second job start");
        await Task.Delay(2000);
        Debug.Log("job's done");
    }

 

 Async / Await은 await의 종료까지 대기하여 순차적으로 동작을 수행하는데 각각 별도로 동작하기 때문에 OnAsyncBtn을 여러번 수행하여 FirstTask를 여러번 실행해도 Coroutine처럼 각각 별도의 스케쥴을 가지고 수행된다. 

 사용에 편리한 점으로는 수행시점에 대한 시간이나 조건 체크등이 필요한 Coroutine과는 달리 Async 함수를 await 예약어를 통해 실행하면 함수 종료시까지 다음 라인으로 넘어가지 않고 대기하기 때문에 별도의 조건을 코드에서 체크해줄 필요가 없다. 

 

다음 포스팅에서는 각각의 특징과 사용법 동작 구조를 살펴보자.