코루틴은 유니티에서 시간의 흐름에 따라 작업을 분할하여 처리할 수 있는 기능이다.
한 번에 끝나지 않고, 여러 프레임에 걸쳐서 코드를 조금씩 실행할 수 있게 해준다.
이걸 활용하면, 예를 들어 몇 초 대기 후에 동작을 실행하거나, 특정 조건이 충족될 때까지 반복 작업을 쉽게 구현할 수 있다.
유니티에서 시간 지연이나 반복 작업을 구현할 때,
만약 코루틴이 없다면 Update()에서 타이머 변수를 직접 관리하면서 코드를 작성해야 한다.
하지만 코루틴을 사용하면 복잡한 타이머 코드 없이,
마치 스크립트처럼 "A 하고, 3초 기다렸다가 B 하고, 다시 C 실행" 이런 식으로 직관적으로 작성할 수 있다.
IEnumerator ExampleCoroutine()
{
Debug.Log("코루틴 시작");
yield return new WaitForSeconds(2f); // 2초 대기
Debug.Log("2초 후 실행");
yield return null; // 한 프레임 대기
Debug.Log("한 프레임 후 실행");
yield break; // 명시적으로 종료 (생략 가능)
}
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator RepeatAction()
{
while(true)
{
Debug.Log("3초마다 반복");
yield return new WaitForSeconds(3f);
}
}
IEnumerator WaitForPlayerDead()
{
yield return new WaitUntil(() => player.hp <= 0);
Debug.Log("플레이어 사망 감지");
}
yield return은 코루틴 함수 내부에서 코드 실행을 일시정지하고, 나중에 다시 이어서 실행하게 만드는 키워드다.
쉽게 말해, "여기서 잠깐 멈췄다가, 조건이 충족되면 그 다음 줄부터 다시 실행하겠다" 라고 할 수 있다.
C#의 일반 메서드는 return을 만나면 함수가 완전히 끝나지만,
코루틴에서 yield return은 일종의 "임시 저장 지점(checkpoint)"을 만든다.
다음 프레임, 혹은 어떤 조건이 만족될 때, 거기서부터 이어서 실행하는 식이다.
코루틴 함수(IEnumerator)는 상태를 저장하는 이터레이터다.
yield return이 호출될 때마다, 유니티는 코루틴의 현재 상태를 저장하고 실행을 잠시 멈춘다. 그리고 조건이 충족(예: 시간이 지남, 특정 조건 만족 등)되면 저장된 상태에서부터 다시 함수가 재개된다.
이 덕분에, 한 번에 실행되지 않고, 여러 프레임에 걸쳐서 코드를 실행할 수 있다.
yield return null
→ 한 프레임 쉬고 다음 코드 실행
yield return new WaitForSeconds(float time)
→ 지정한 시간(초)만큼 대기
yield return new WaitForEndOfFrame()
→ 해당 프레임이 모두 끝난 후 실행
yield return new WaitUntil(() => 조건식)
→ 특정 조건이 true가 될 때까지 대기
yield return new WaitWhile(() => 조건식)
→ 특정 조건이 false가 될 때까지 대기
yield break
→ 코루틴 즉시 종료
예시
IEnumerator Example()
{
Debug.Log("A"); // 즉시 실행
yield return new WaitForSeconds(2f); // 2초 대기
Debug.Log("B"); // 2초 후 실행
yield return null; // 한 프레임 쉬기
Debug.Log("C"); // 또 그 다음 프레임에 실행
}
코루틴은 메인 스레드(메인 루프)에서 돌아간다.
단순히 실행 순서를 쪼개서(일시정지-재개 반복) 여러 프레임에 걸쳐 나눠 실행할 뿐,
백그라운드에서 따로 실행되는 게 아니다.
따라서, 동시성 문제나 스레드 세이프티(thread safety) 문제는 발생하지 않는다.
하지만, 무거운 작업(복잡한 계산, 대용량 파일 I/O, 네트워크 통신 등)을 코루틴에 넣는다고 해서 렉 없이 자동으로 빨라지거나 비동기 처리가 되는 게 아니다.
이런 경우에는 실제로 비동기 처리를 해야한다.
또 코루틴은 오브젝트의 라이프사이클에 의존한다. 코루틴을 실행한 MonoBehaviour가 비활성화(Disable)되거나, 파괴(Destroy)되면 코루틴도 강제로 중단된다.
특정 상황에서 코루틴이 예상치 못하게 중단되는 경우가 있으니
중요한 작업은 중단되었을 때 대처 로직을 넣어주는 것이 안전하다.
yield return의 시간 계열 대기는 Time.timeScale 영향을 받는다
yield return new WaitForSeconds(3f)처럼 대기하는 경우
게임의 Time.timeScale 값이 바뀌면(슬로우, 일시정지 등) 실제 대기 시간도 달라진다. 실시간(Real Time) 기준 대기가 필요하다면
WaitForSecondsRealtime을 써야 한다.
그리고 가장 흔하게 하는 실수는 코루틴의 중복 호출이다.
같은 코루틴을 반복적으로 StartCoroutine하면 여러 개의 코루틴이 동시에 실행될 수 있다. (예: 버튼 클릭 시마다 StartCoroutine 호출 → 여러 번 동작)
보통은 Coroutine 타입 변수에 코루틴을 저장해서 필요할 때 중복 실행을 막거나, 기존 코루틴을 중단(StopCoroutine)하는 방식으로 관리한다.
즉, 코루틴은 강력하지만, 내부 동작 원리와 라이프사이클, 한계와 장단점을 명확히 이해해야 버그 없는 안정적인 게임 로직을 구현할 수 있다.