이전 글에서는 yield return이 내부적으로 IEnumerator 인터페이스를 구현하는 클래스로 변환된다는 것을 알아보았습니다. 이번 글에서는 yield return이 Unity에서 코루틴과 어떻게 연결되는지 살펴보겠습니다.
StartCoroutine() 은 IEnumerator 객체를 실행한다
Unity 에서 yield return을 활용하려면 StartCoroutine() 을 사용해야 합니다.
StartCoroutine() 은 IEnumerator를 인자로 받아 이를 실행하는 역할을 합니다.
예제 코드를 보겠습니다
void Start()
{
StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
Debug.Log("Step 1");
yield return new WaitForSeconds(1f);
Debug.Log("Step 2");
yield return new WaitForSeconds(1f);
Debug.Log("Step 3");
}
이 코드를 실행하면 StartCoroutine(MyCoroutine()) 이 호출되면서 Unity가 내부적으로 IEnumerator 객체를 관리하게 됩니다. yield return new WaitForSeconds(1f) 를 만나면 실행이 멈추고 1초 후에 다시 실행됩니다.
Unity 의 내부적인 코루틴 실행 방식
Unity 는 StartCoroutine() 을 호출하면 IEnumerator 를 실행 리스트에 등록한 후, MoveNext() 를 주기적으로 호출합니다. 마지막에 MoveNext() 가 false를 반환하면, Enumerator 객체는 상태를 모두 순환했기 때문에 해당 코루틴은 실행이 끝나고 리스트에서 제거됩니다.
실제로 Unity의 코루틴 매니저는 다음과 같은 방식으로 동작합니다:
class CoroutineManager
{
private List<IEnumerator> coroutines = new List<IEnumerator>();
public void StartCoroutine(IEnumerator coroutine)
{
coroutines.Add(coroutine);
}
public void Update()
{
for (int i = coroutines.Count - 1; i >= 0; i--)
{
if (!coroutines[i].MoveNext())
{
coroutines.RemoveAt(i);
}
}
}
}
이와 같은 방식으로 Unity는 IEnumerator 객체를 리스트에 저장하고, 매 프레임마다 MoveNext()를 호출하여 실행을 이어갑니다. yield return을 만나면 그에 맞는 대기 로직을 수행한 후 다시 MoveNext()를 호출합니다.
WaitForSeconds() 는 어떻게 동작할까?
yield return new WaitForSeconds(1f) 를 사용하면 1초 동안 실행을 멈추게 되는데, 이는 Unity가 내부적으로 해당 시간만큼 기다렸다가 다시 MoveNext()를 호출하기 때문입니다. WaitForSeconds 는 IEnumerator 가 아니라 별도의 타이머 객체로 관리되며, Unity 의 코루틴 시스템이 이를 감지하여 적절한 타이밍에 다시 실행되도록 합니다.
정말 함수는 멈추는 것일까?
StartCoroutine()은 IEnumerator 객체를 실행하고, Unity가 이를 관리하며 MoveNext() 를 호출하여 실행을 이어간다고 하였습니다.
이를 조금 더 구체적으로 설명하면, yield return 을 만나면 실행이 멈추고, (정확히는 case ~ return; 으로 MoveNext() 함수를 빠져나갑니다.) 다음 프레임에 다시 진입하여, 현재 상태에 해당하는 case 문을 실행합니다.
여기서 어떤 조건, 예를 들면 WaitForSeconds( 3 ); 처럼 3초 대기를 사용하는 경우, 유니티는 매 프레임 코루틴이 수행되는 시점에서 시간 경과를 검사합니다. 그리고 3초가 지났다면 해당 yield return 은 완료된 것으로 간주되고, current 를 다음 상태로 넘겨줍니다. 이렇게 함으로써 다음 프레임의 코루틴이 수행되는 시점에 MoveNext() 함수는 다음 상태의 case 문으로 진입하게 됩니다.
즉, MoveNext() 에서는 현재 상태에서 조건을 판단해서 다음 상태로 넘어갈지 현재 상태에 머물지 판단을 하게 되는 것입니다. 겉으로는 보기에 yield return; 은 함수를 잠시 멈추는 것처럼 동작하지만, MoveNext() 함수를 들여다보면, 단순히 상태값을 저장하고 있다가 그 상태를 벗어나는 조건이 될 때 다음 상태로 넘어가는 switch ~ case 문에 지나지 않는다는 것을 알 수 있습니다.
마무리
이번 글에서는 yield return이 Unity의 코루틴과 어떻게 연결되는지 살펴보았습니다.
다음 글에서는 yield return을 활용하여 보다 복잡한 흐름을 제어하는 방법에 대해 알아보겠습니다.
'C#' 카테고리의 다른 글
5. yield return 에 대해 ( 활용 02 ) (0) | 2025.03.09 |
---|---|
4. yield return 에 대해 ( 활용 01 ) (0) | 2025.03.09 |
2. yield return 에 대해 (내부 동작) (0) | 2025.03.09 |
1. yield return 에 대해 (개념) (0) | 2025.03.09 |
Action 과 Func 의 형변환 살펴보기 (0) | 2025.03.08 |