유니티의 코루틴 시스템이 **WaitForSeconds(2)**를 만나면, 유니티 엔진의 내부 스케줄링 시스템에 의해 작동하게 된다.
구체적으로는 어떤 일이 일어나는지 그 과정에 대해서 알아보자.
1. WaitForSeconds는 유니티의 대기 시간 요청을 표현
WaitForSeconds는 유니티에서 제공하는 클래스로, 특정 시간만큼 코루틴을 일시 중단하고, 그 시간이 지나면 다시 코루틴이 실행되도록 합니다.
코루틴 함수가 yield return new WaitForSeconds(2)를 만나면, 유니티는 그 코루틴의 실행을 2초 동안 대기 상태로 전환합니다.
2. 유니티의 코루틴 스케줄러
유니티 엔진은 코루틴 스케줄러를 통해 매 프레임마다 코루틴을 관리합니다.
코루틴이 WaitForSeconds와 같은 대기 객체를 반환하면, 그 코루틴은 대기 상태로 등록되고, 유니티는 해당 코루틴을 프레임마다 상태를 확인하면서 일정 시간이 지나면 다시 실행되도록 합니다.
3. WaitForSeconds와 프레임 시간 관리
유니티의 프레임 시간은 매 프레임마다 업데이트됩니다. WaitForSeconds(2)를 만나면, 유니티는 해당 코루틴을 2초 후에 다시 실행하도록 예약합니다.
이 예약 작업은 유니티의 프레임 업데이트 시스템에서 처리됩니다.
각 프레임에서 유니티는 대기 중인 코루틴을 확인하면서, 해당 코루틴이 다시 실행될 시점이 되었는지 판단합니다.
유니티의 내부 시스템이 현재 경과된 시간을 추적하고, 대기 중인 코루틴의 대기 시간이 다 되었는지 체크합니다.
시간이 다 되면, 유니티는 해당 코루틴의 MoveNext() 메서드를 호출하여 다음 프레임부터 실행을 재개합니다.
4. 프레임 기반으로 시간이 계산됨
유니티에서 시간은 프레임 기반으로 계산됩니다.
즉, WaitForSeconds(2)는 2초 동안 몇 프레임이 지나갔는지를 추적하고, 해당 프레임 수가 모두 지나면 다시 코루틴을 재개합니다.
예를 들어, 유니티가 초당 60프레임(60 FPS)으로 실행 중이라면, 2초 동안 약 120 프레임이 경과합니다.
yield return new WaitForSeconds(2)를 만나면 유니티는 현재 시간을 기록하고, 해당 코루틴을 중단합니다.
유니티의 프레임 업데이트마다 현재 시간과 기록된 시간을 비교하면서 2초가 지났는지 체크합니다.
2초가 지나면 유니티는 코루틴을 다시 실행 대기 목록에 추가하고, 다음 프레임부터 해당 코루틴의 MoveNext()를 호출하여 실행을 이어나갑니다.
5. 내부에서 MoveNext() 메서드로 코루틴 재개
2초 후, 유니티는 해당 코루틴의 MoveNext() 메서드를 다시 호출하여 이전 상태에서 멈춘 지점부터 코드를 이어서 실행합니다.
유니티는 상태 머신을 사용하여 코루틴이 멈춘 지점을 기억하고 있기 때문에, 다시 진입하면 그 지점에서부터 이어서 실행됩니다.
IEnumerator MyCoroutine()
{
Debug.Log("첫 번째 작업 시작");
// 2초 대기
yield return new WaitForSeconds(2);
Debug.Log("2초 후 작업 재개");
}
결론
유니티의 코루틴 시스템은 WaitForSeconds와 같은 대기 요청을 처리할 때, 내부적으로 프레임 기반으로 시간을 추적하여 코루틴의 실행을 중단하고, 일정 시간이 지나면 다시 실행을 재개합니다.
이 과정은 유니티 엔진의 스케줄링 시스템에 의해 자동으로 관리되며, 개발자는 코루틴을 통해 복잡한 시간 기반 작업을 쉽게 처리할 수 있습니다.
이런 구조 덕분에 코루틴은 비동기적이고 프레임 기반으로 작업을 수행할 수 있는 강력한 도구로 사용됩니다.
부가 설명 1
유니티에서는 IEnumerator를 이용해 코루틴(Coroutine)을 구현할 수 있습니다.
코루틴은 시간 지연이나 여러 프레임에 걸친 작업을 쉽게 처리할 수 있도록 도와줍니다.
유니티에서의 IEnumerator 예
public class CoroutineExample : MonoBehaviour
{
void Start()
{
// 코루틴 시작
StartCoroutine(MyCoroutine());
}
// IEnumerator를 사용한 코루틴
IEnumerator MyCoroutine()
{
Debug.Log("첫 번째 작업 시작");
// 2초 대기
yield return new WaitForSeconds(2);
Debug.Log("2초 후 작업 재개");
}
}
컴파일러가 이 코드를 처리할 때, yield를 만날 때마다 코루틴의 실행 상태를 기록하고, 이후에 다시 호출되면 그 상태에서 코드를 재개하는 구조를 만듭니다.
이렇게 해서 IEnumerator가 내부적으로 반환되며, MoveNext() 메서드를 통해 단계별로 실행됩니다.
언듯보면,
MyCoroutine()이 직접적으로 IEnumerator를 반환하지 않는 것처럼 보이지만, yield return 구문이 자동으로 IEnumerator 객체를 생성해줍니다.
이는 C#의 코루틴 작동 방식이며, yield 키워드가 IEnumerator의 구현을 책임지기 때문에 우리가 따로 반환할 필요가 없는 것입니다.
부가 설명 2
코루틴은 실행 순서
코루틴은 yield return 조건에 맞게 내부에서 처리된다.
https://docs.unity3d.com/kr/2021.3/Manual/ExecutionOrder.html#FirstSceneLoad
유니티 내부 실행 순서를 살펴보면...
FixedUpdate() 후에 yield WaitForFixedUpdate 가 처리되는 것을 알 수 있다.
그리고 흐름이 계속 이어지다가...
Update() 후에
yield null, WaitForSeconds, WWW, StartCoroutine 이 실행됨을 알 수 있다.
대표적인 예로 yield return null 은 Update() 실행 후, 재진입하는 코루틴이기 때문에 해당 위치에서 호출됨을 확인할 수 있다.
그리고 또 계속 실행되다가...
yield WaitForEndOfFrame 이 처리되고 있다.
이와 같이 코루틴은 yield 조건문에 따라 해당 위치에서 옳바르게 재진입하고 있다는 것을 보여준다.
코루틴의 스케줄링 방식
코루틴은 yield return을 통해 일시 중단되고, 유니티의 프레임 루프에서 매 프레임마다 중단된 코루틴의 상태를 점검한 후, 조건이 충족되면 다시 실행됩니다.
이 과정에서 코루틴은 특정 프레임에서만 실행되므로, Update()처럼 매 프레임 실행되는 것은 아닙니다.
예를 들어
yield return new WaitForSeconds(2)는 2초 후에 다시 실행됩니다.
yield return null은 다음 프레임에 다시 실행됩니다.
정리하면
코루틴은 유니티의 스케줄링 시스템에 의해 조건이 충족되면 실행됩니다.
예를 들어, 특정 시간이 지나거나 특정 조건이 만족될 때 실행됩니다.
유니티는 Update()와 코루틴을 서로 다른 타이밍에 실행하여 게임 로직과 비동기 작업을 효율적으로 처리할 수 있게 합니다.
'프로그래밍 > Unity' 카테고리의 다른 글
StartCoroutine (1) | 2024.10.12 |
---|---|
편집 도구는 어떤걸 선택해야 하나? (Visual Studio, Visual Studio Code) (2) | 2024.10.10 |
ScriptableObject (0) | 2024.10.02 |
MonoBehaviour 를 상속받은 Class 주의점 (0) | 2024.09.18 |
자체 UI 크기 설정 (0) | 2024.09.16 |