Unity에서 사용하는 코루틴(Coroutine)은 코드의 흐름을 제어할 수 있도록 해주는 기능입니다. 코루틴을 사용하면 특정 이벤트가 발생하거나 시간이 경과한 뒤에 실행될 작업을 손쉽게 처리할 수 있습니다. 오늘은 이 코루틴에 대한 개념과 원리에 대해 자세히 알아보도록 하겠습니다.
코루틴(Coroutine)이란?
함수를 호출하면 시작과 끝이 1 프레임 안에 수행됩니다. 하지만 게임을 개발하다 보면 특정 로직이 몇 프레임(혹은 몇 초) 동안 수행되어야 하는 순간들이 많습니다. 그래서 코루틴이라는 새로운 개념이 나타나게 되었습니다.
코루틴: 특정 작업(Task)를 다수의 프레임으로 분산시킬 수 있는 실행 기능
"Coroutine"은 "Co"와 "Routine"이 합쳐진 말로, 루틴(함수)들이 서로 협력하면서 실행된다는 의미를 가집니다. 즉, 함수들 간의 흐름을 조정하며 작업을 수행합니다.
코루틴 사용 방법
void Start()
{
// Test 코루틴 실행
StartCoroutine(nameof(Test));
// Test 코루틴 종료
StopCoroutine(nameof(Test));
// (TempManager가 호출한) 모든 코루틴 종료
StopAllCoroutines();
}
IEnumerator Test()
{
// 1초 후 아래 로직 수행
yield return new WaitForSeconds(1f);
// 1초 후 아래 로직 수행 (Time.deltaTime 영향을 받지 않음)
yield return new WaitForSecondsRealtime(1f);
// 다음 FixedUpdate 호출 까지 대기 후 실행
yield return new WaitForFixedUpdate();
// 모든 렌더링 작업이 완료되어 프레임이 끝날 때까지 대기 후 실행
yield return new WaitForEndOfFrame();
// 1프레임 후 아래 로직 수행
yield return null;
// 아래 구문이 수행되지 않고 빠져 나옴
yield break;
// {condition}이 참일 때까지 대기 후 실행
yield return new WaitUntil(() => condition);
// {isProcessing}이 거짓이 될 때까지 대기 후 실행
yield return new WaitWhile(() => isProccessing);
Debug.Log("End");
}
코루틴 실행 및 중단 방법은 다음과 같다.
- StartCoroutine(string name): {name} 코루틴 실행
- StopCoroutine(string name): 소유권이 Call인 {name} 코루틴 중단
- StopAllCoroutines(): 소유권이 Call인 모든 코루틴 중단
코루틴마다 소유권이 존재하며, Caller가 대상입니다.
코루틴 함수 작성 방법은 다음과 같습니다.
- 반환자로 Ienumerator을 선언합니다.
- yield return 구문을 통해 함수 실행 조건을 추가합니다.
- yield break 구문을 통해 함수를 중단합니다.
코루틴 작동 원리
Unity의 코루틴은 IEnumerator와 MoveNext() 함수를 통해 프레임 단위로 실행됩니다. 테스트로 IEnumerator 인터페이스를 통해 MoveNext() 함수를 직접 찍어보도록 하겠습니다.
void Start()
{
IEnumerator IEnum = Test();
while (IEnum.MoveNext())
{
object result = IEnum.Current;
Debug.Log("Result: " + result);
}
}
IEnumerator Test()
{
Debug.Log("시작");
yield return new WaitForSeconds(1f);
Debug.Log("1");
yield return new WaitForSeconds(1f);
Debug.Log("2");
yield return new WaitForSeconds(1f);
Debug.Log("3");
}
단순히 호출 형태로 구현하면 위와 같습니다. 하지만 위 방식으로는 1초 마다 수행되지 않습니다. 그래서 코루틴을 처리하기 위해선 Monobehaviour 클래스 내부의 StartCoroutine 함수를 호출해야 합니다.
StartCoroutine을 호출하면 Unity 엔진 내부 C++ 코드가 동작하는데, 여기서 코드를 제어하는 로직이 수행됩니다.
IEnumerator coroutine = Test();
while (coroutine.MoveNext())
{
object current = coroutine.Current;
if (current is WaitForSeconds wait)
{
Debug.Log($"Wait for {wait.Seconds} seconds");
}
}
Unity 엔진의 네이티브 C++은 비공개라 정확한 확인은 어렵지만, 아마도 위처럼 MoveNext() 함수를 수행할 때마다 현재 YieldInstruction에 따라 코드를 제어하는 것으로 보입니다. 이를 통해 개발자는 코루틴을 통해 원하는 순간에 로직을 처리할 수 있습니다.
YieldInstruction: 코루틴의 중단 및 행동을 제어하기 위해 사용되는 Base 클래스입니다. WaitForSeconds나 WaitForEndOfFrame 같은 클래스들이 이 클래스를 상속받습니다.
💡 코루틴은 진정한 비동기 처리가 아닙니다!
코루틴은 프레임 단위로 오직 메인 스레드 위에서 작업의 실행을 분할하여 로직을 처리합니다. 단순히 프레임 단위로 작업을 수행하는 조건을 체크하며 기다릴 뿐입니다. 따라서 코루틴은 작업 도중 발생하는 I/O 작업을 처리하는 비동기(asynchronous) 처리로 간주되진 않습니다.
정리
코루틴은 Unity에서 비동기 처리를 구현할 때 유용합니다. 코루틴은 특정 작업을 한 프레임이 아닌, 여러 프레임으로 분산하여 처리합니다. 코루틴을 제대로 사용하기 위해선 MoveNext 동작 원리를 이해하고, YieldInstruction을 통해 프레임을 분산하여 시스템을 설계할 필요가 있습니다.
추천글
https://docs.unity3d.com/kr/2022.3/Manual/Coroutines.html
'유니티(Unity) > 이론 정리' 카테고리의 다른 글
[Unity] .NET이란? (4) | 2024.11.17 |
---|---|
Unitask의 개념과 사용법 (0) | 2024.11.14 |
[Unity Korea] 객체 지향 설계 원칙 (SOLID) (0) | 2024.11.12 |
클래스 이름 짓기 [ 2편 ] + MVC 패턴 (2) | 2024.11.11 |
클래스 이름 짓기 [ 1편 ] (1) | 2024.11.10 |