본문 바로가기

유니티 게임 개발하기/2D 게임 개발일지

유니티(C#스크립팅) - 코루틴. 코루틴. Coroutine의 이해.

안녕하세요, 윈디입니다!
오늘은 코루틴의 개념에 대해서 좀더 자세하게 이해하게 되어서, 

정리할 겸 글을 쓰게 되었네요.

오늘 배운 내용으로 게임에 필요했던 메인 화면의 동작을 코루틴을 이용하여  제작해 보기도 했구요.



코루틴이란?

Coroutine, 


같이한다는 뜻의 접두사 Co와 작업을 뜻하는Routine의 합성어라고 할 수 있겠죠.


같이 하는 일. 

그러니까 유니티에서 코루틴이란 간단히 말하자면 코루틴을 호출한 함수와 
해당 코루틴이 같이 협동해서 문제를 해결하는 구문이란 뜻인 겁니다. 

코루틴은 항상 실행되는 것이 아니라 필요한 상황에서만 발생시킬수 있다는 점에서 매 프레임 실행되는 Update문보다 훨씬 강력하고 효율적인 기능을 발생시킬 수 있죠.

간단히 생각해서, 유니티에서 사용하는 여러 스크립트의 많은 Update함수에, 

어떤 조건 안에서만 발생하도록 검사하는 if문이 조금씩 들어가 있다면 매 프레임마다 해당 계산을 하는데 결과적으로 엄청난 양의 if문 검사가  필요하겠죠?


 이것을 코루틴으로 변환하여 정말 필요할 때만 함수를 실행시킨다면, 그러한 연산의 낭비가 최소한도로 줄어들 것이므로, 코루틴을 사용하여 매 프레임 실행되는 함수의 양을 줄일수록 게임과 프로그램의 성능 향상에 직결이 되는 것이라고 보시면 되겠습니다. 


예를 들면 오늘 제가 작성한 이 메인메뉴의 스크립트 함수의 일부를 보자면,

 첫 구문은 코루틴을 활용하였고, 두번째 부분은 똑같은 부분이지만 이용하지 않고 Update에서 조건을 주어서 매프레임 검사하도록 하였죠.

아래에 해당 코드가 있습니다.


void Start () //처음 한 번만 실행하는 함수. 

StartCoroutine (StartRutine ()); 
            //시작하자마자 실행되어야 하는 코루틴 시작. 


IEnumerator StartRutine() 

do { 

               if (Input.GetMouseButtonUp (0)) { 
                    //왼쪽버튼 누르면 스타트버튼으로 체킹 

  StartButton.GetComponent<Button> ().Select (); 

} else if (Input.GetMouseButtonUp (1)) { 
                     //오른쪽버튼 누르면 나가기버튼으로 체킹 
  QuitButton.GetComponent<Button> ().Select (); 


yield return null; 

} while(inMainRutine); 

yield return null; 


내용은 간단합니다. 마우스의 좌 우클릭에 따라 선택되는 버튼이 다른 것 뿐이지만, 이것은 딱 '메인 메뉴'가 처음 켜졌을 때만 필요한 것이기 때문에, 더이상 첫 화면이 아닌 메뉴에서 저런 조건을 검사해 봤자 쓸모가 없습니다.

 예를 들어 Extra메뉴로 들어가서 더 이상 버튼이 존재하지 않게 되었을 때는 저 구문이 전혀 필요가 없어지죠.

업데이트 문에서는 어떨까요?

 update문으로 위의 것을 만들면 이런 식이 되죠.

void Update() 

     if (Input.GetMouseButtonUp (0)) { 
                    //왼쪽버튼 누르면 스타트버튼으로 체킹 

  StartButton.GetComponent<Button> ().Select (); 

} else if (Input.GetMouseButtonUp (1)) { 
                     //오른쪽버튼 누르면 나가기버튼으로 체킹 
  QuitButton.GetComponent<Button> ().Select (); 




하지만 Update문은 말 그대로 매 프레임 실행되기 때문에, 
이 메뉴가 엑스트라 페이지로 넘어갔을 때도 클릭이 일어날 때마다 존재하지 않는 버튼을 선택하려는 함수들이 계속해서 실행될 것이고, 심지어 클릭하지 않았을 때에도 계속해서 마우스 버튼을 누르는지 계속해서 검사하는 구문이 실행되고 있을 겁니다.


시간이 지나면 지날수록, 스크립트가 많아질수록

 이런 것들은 점점 자원을 낭비하게 됩니다.


그렇다면 코루틴은 어떻게 사용하는가?

코루틴은 보통

 WaitForSecond (몇 초 기다림) 등 어떤 조건이 될 때까지 스크립트가 계속해서 

쓸모없는 검사를 하지 못하도록 사용하거나,


웹에서 파일을 다운로드 하면서 자신은 UI와 관련된 

다른 작업을 수행하는 비동기 작업에 사용이 됩니다.


사실 어떤 작업이나 조건이 될 때까지 기다린다는 점에서 사용법 자체는 동일하지만, yield 라는 코루틴이 해당 조건이 될 때까지  실행을 일시정지시키는 기능을 가진 구문을 사용하는 데 약간 차이가 생기게 되겠죠.


유니티 엔진에서 지원하는 yeild 문은 다음과 같습니다.


yield return null

다음 프레임까지 대기

yield return new WaitForSeconds(float)

지정된 초 만큼 대기

yield return new WaitForFixedUpdate()

다음 물리 프레임까지 대기

yield return new WaitForEndOfFrame()

모든 렌더링작업이 끝날 때까지 대기

yield return StartCoRoutine(string)

다른 코루틴이 끝날 때까지 대기

yield return new WWW(string)

웹 통신 작업이 끝날 때까지 대기

yield return new AsyncOperation

비동기 작업이 끝날 때까지 대기 ( 씬로딩 )

(출처 - http://www.unitystudy.net/bbs/board.php?bo_table=writings&wr_id=43)   

출처로 들어가면  코루틴에 대한 좀더 심도있는 설명을 얻으실 수 있습니다.



예를 들어, 코루틴 반복문 안에 yield return null 이 들어가 있다면, 이 코루틴은 다음 프레임에서 한번더 이 반복문을 실행하게 되는 겁니다.

 만약, 코루틴 안에 무한반복하는 반복문이 들어간다면 유니티는 1프레임 안에서 무한루프 작업을 해야 하니 유니티가 통째로 멈춰 버리는 사태가 발생할 수 있으니 꼭 주의해 주세요.

 코루틴 안에 계속해서 반복하는 함수를 넣고 싶다면, yield 문을 통해 매 프레임, 혹은 매 초마다 반복을 하도록 설계해주어야만 하셔야 할 겁니다.



지금까지는 코루틴이 익숙하지 않아, 필요 없는 스크립트를 오브젝트째로 deactivate 시켜서 이런 문제가 최대한 덜 발생하도록 노력했지만, 
앞으로는 코루틴을 최대한 많이 활용하여 필요할 때만 발생하도록 하면 
좀더 간단하고 직관적이게 개발을 할 수 있게 될 것 같네요.



물론, 코루틴으로 여러 함수를 작성해서 가독성이 좋아지고, 좀더 원하는 대로

순차적인 함수의 실행순서를 디자인할 수 있다는 것도 덤이라면 덤이겠죠.


시간이 난다면 지금까지 만든것들도 모두 코루틴 화해서 성능향상을 꾀해보는것도 좋을 것 같습니다.


모두 즐거운 주말 보내세요!