본문 바로가기
프로그래밍/Unity

Unity - Coroutine(코루틴) - Reference Post

by 리나그(ReenAG) 2021. 8. 13.
728x90
반응형

Coroutine - 코루틴 : 개념파트

 

Coroutine은 Unity에서 지원하는 멀티태스킹 방법중에 하나이다. python에서도 비슷한 기법이 있다. generator expression에서 yield를 이용해서 어떤 특정한 값을 리턴하게 만드는 것이다. 그럼 yield문에서 당장의 컨트롤은 그 함수 밖으로 나가지만, 컴퓨터는 그 함수가 어디까지 진행되었는지를 기억해둔다. 다시 함수를 불렀을 때, yield문의 다음 줄을 시행해서 동시에 어떤 작업을 하는 것처럼 보이게 만드는 것이다.

 

이를 그림으로 표현하면 다음과 같다 :

코루틴을 대략적으로 표현한 그림이다. 빨간색 선은 컨트롤(실행하는 위치)가 왔다 갔다하는 것을 나타낸다.

코루틴이 실행되는 것을 이미지를 이용해서 이렇게 정리할 수 있다.

 

1. Coroutine(Functon)이 불러 코루틴으로 쓰일 수 있는 함수가 불린다.

2. Coroutine은 yield문이 실행될 때 까지 쭉~ 실행된다.

-> 그림에서는 init 부터 while문 안의 yield Delay(1)까지 진행할 것이다.

3. yield문이 실행되면서 컨트롤이 다시 Animate()로 옮겨가며, 이때 Coroutine이 어디까지 진행되었는지를 기억한다.(함수가 정말로 종료되는게 아니라 일시정지를 했다고 생각하면 편하다.)

---------------------------------실제로 애니메이션이 일어나는 부분 시작

   4. yield의 Delay(1)이기 때문에, 1밀리초 동안 그외 작업을 진행한다.

  ->여기에서 다양한 함수가 있을 수 있다. Start, Update일 수도.

   5. 다시 Coroutine 함수로 돌아와서 while문에 있는 것이 실행되기 시작한다.

   6. 전부 실행이 끝났으면 다시 yield문을 실행한다.

   7. 4~6을 그렇게 while문의 조건이 false가 될때 까지 반복한다.

---------------------------------실제로 애니메이션이 일어나는 부분 끝

8. while문의 조건이 false(그림에서는 애니메이션이 끝남)이 되었으므로 마지막 뒤처리를 하고, 코루틴 함수를 종료한다.

 

 이는 개념적으로는 멀티스레딩 / 차일드 프로세싱 등과는 많이 다른 성격을 가졌는데, 그 이유는 실제로 싱글 프로세스 / 싱글 스레드로 진행하기 때문이다. 다만 그것과 비슷한 동작을 수행할 수 있을 뿐인 것으로 알고 있다. 따라서 Java의 Thread 혹은 Android의 Handler와는 좀 다른 성격을 지닐 것으로 예측된다.

 

사실, 이러나 저러나 이론을 적어 놓았지만, 사용방법은 이와 전혀 딴판인 경우도 많다. c#에서도 Photon등의 라이브러리가 AsyncTask가 가능한걸 보면 아예 멀티스레딩을 못하는건 아닌가보다.

Coroutine - 코루틴 : 실전파트

다른 사람은 모르겠지만, 나는 이 코루틴을 애니메이션을 만드는데 주로 이용했다. 유니티 자체에 애니메이션 기능이 있기는 있는데, 꼭 편리한 것 만은 아니고 결국 스프라이트 중노동이 필요한 경우가 많다. 그리고 도무지 중간에 컬러를 바꾼다거나 alpha를 바꾼다거나 하는 방법은 터득할 수 없었는데, 그 대안으로 coroutine을 이용했다. 런타임에 새로운 루틴을 작성하는 건 부담을 주는 것일 수도 있겠지만, 뭐 애니메이션 하나 만드는데 뭐 그리 대수랴...

1. WaitForSeconds를 이용해서 깜빡이 만들기

 display를 Unity.Text라고 생각하고 봐보자. Hello!라는 글씨가 나타났다 사라질 것이다.

IEnumerator MoveToPos(Vector2 to, Vector2 from){
    WaitForSeconds interval = new WaitForSeconds(1.0f);
	
    display.text = "Hello!";
    yield return interval;
    display.text = "";
    yield return interval;
    display.text = "Hello!";
}

2. Mathf.Lerp를 이용해서 자연스러운 애니메이션 연출하기

 아래의 코드는 from -> to로 자연스럽게 rectTransform의 좌표를 옮기는 코드이다.

float speed = 2.5f;

IEnumerator MoveToPos(Vector2 to, Vector2 from){
    WaitForSeconds interval = new WaitForSeconds(0.001f);
    Vector2 moving_pos = new Vector2(to.x, to.y);
    float dx = from.x, dy = from.y;
    float t = 0.0f;
    is_drag_animating = true;

    while(Mathf.Abs(dx - to.x) > Mathf.Epsilon || Mathf.Abs(dy - to.y) > Mathf.Epsilon){
        yield return interval;
        dx = Mathf.Lerp(dx, to.x, t);
        dy = Mathf.Lerp(dy, to.y, t);
        moving_pos.x = dx;
        moving_pos.y = dy;

        t += speed * Time.deltaTime;

        rectTransform.anchoredPosition = moving_pos;
    }
    rectTransform.anchoredPosition = to;
    display_offset = to;

    is_drag_animating = false;
}

Mathf.Lerp는 내가 알기로 map()함수와 매우~ 유사한데, 첫 두개의 입력값으로 start와 end를 잡고, 마지막 세번째 입력은 0.0f~1.0f 사이의 실수이다. 따라서 위의 코드처럼 굳이 t에 speed * Time.deltaTime을 더할 필요는 없고, 0.01f같은 고정 (적어도 몇 배했을 때 1.0f에 도달할) 실수값을 더해도 된다. 다만 Time.deltaTime이 더 나은 점은, 렉이 걸리더라도 일정하게 오브젝트를 움직일 수 있다는 점이 아닐까... 생각해본다. (확실하지는 않음, 아래의 링크에서도 왜 굳이 이래야하는지 모르겠다고 함.)

 

참고하면 매우 좋은 글 :

https://gamedevbeginner.com/the-right-way-to-lerp-in-unity-with-examples/

 

The right way to Lerp in Unity (with examples)

Learn to animate buttons, move objects and fade anything - the right way - in my in-depth guide to using LERP in Unity (including copy / paste examples).

gamedevbeginner.com

 

이거 원래 1달 전인가 2달전인가 만들어 놓은 임시 저장글이었는데 이제서야 완성;; 역시 글 쓰는건 어렵다.

728x90
반응형