Coroutine(코루틴)은 작업을 다수의 프레임에 분산하여 처리하는 방식으로시간이 걸리는 작업을 프레임을 나눠서 처리하거나, 일정 시간 후에 무언가를 실행하고 싶을 때 사용합니다.
[SerializeField] Rigidbody rigid;
[SerializeField] float jumpPower;
private Coroutine coroutine;
private void Update()
{
if (Input.GetKeyDown(KeyCode.G))
{
if (coroutine == null)
{
coroutine = StartCoroutine(Routine());
}
}
if (Input.GetKeyDown(KeyCode.H))
{
if (coroutine != null)
{
StopCoroutine(coroutine);
coroutine = null;
}
}
}
IEnumerator Routine() // 설정한 시간 만큼 대기했다가 수행하도록 만든다.
{
Debug.Log("점프 대기");
yield return new WaitForSeconds(1f); // 1초 대기
Debug.Log("점프");
rigid.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
또한 코드를 보시면 yield return을 보실 수 있는데
yield는 특정 지점에서 실행을 일시 중단하고 다시 재개할 수 있습니다!
IEnumerator Routine()
{
WaitForSeconds delay = new WaitForSeconds(1f);
Debug.Log("좀비선택 까지 5초");
yield return delay;
Debug.Log("좀비선택 까지 4초");
yield return delay;
Debug.Log("좀비선택 까지 3초");
yield return delay;
Debug.Log("좀비선택 까지 2초");
yield return delay;
Debug.Log("좀비선택 까지 1초");
yield return delay;
Debug.Log("또 그들이 온다...");
coroutine = null;
}
이렇게 카운트다운이 필요한 경우로 응용도 가능합니다.
단, 코루틴을 실행하면 클래스 객체가 생성되며 힙 메모리가 할당됩니다.
이를 해제하지 않는 상태로 반복되고 누적 될 경우, 메모리 누수가 발생할 수 있습니다.
그래서 마지막에 꼭 null로 만들어야 합니다!
coroutine = null;
또한 변수를 선언해 미리 힙에 할당해놓고, 해당 변수에 생성되는 코루틴 객체를 할당하는 방식을 사용한다면 메모리 공간을 절약하여 메모리 단편화를 방지할 수 있습니다.
WaitForSeconds delay = new WaitForSeconds(1f);
코루틴을 멈추고 싶으면 StopCoroutine, StopAllCoroutines 통하여 멈출 수 있습니다.
private void OnDisable()
{
StopCoroutine(coroutine);
StopAllCoroutines(); // 진행중인 모든 코루틴 종료
}
한 번에 끝내면 너무 빠르거나 끊기는 느낌을 주기 때문에
여러 프레임에 나눠 부드럽게 처리할 수 있도록 구현할 수도 있습니다.
IEnumerator MoveOverTime()
{
for (int i = 0; i < 100; i++)
{
transform.position += Vector3.right * 0.1f;
yield return null; // 다음 프레임까지 기다림
}
}
yield return null;은 다음 프레임으로 넘어갈 때까지 실행을 일시 중지시킵니다.
어떤 조건을 걸어서 실행하도록 응용도 가능합니다.
IEnumerator WaitUntilExample()
{
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));
Debug.Log("작업 진행하겠습니다");
}
스페이스바를 누르면 작업이 진행하도록 만들 수도 있습니다.
앞에서 여러 예시를 통해 코루틴의 함수를 살펴봤는데 한번 더 정리하겠습니다.
IEnumerator CoroutineWait()
{
yield return null;
yield return new WaitForSeconds(1f);
yield return new WaitForSecondsRealtime(1f);
yield return new WaitForFixedUpdate();
yield return new WaitForEndOfFrame();
yield return new WaitUntil(() => true);
}
다음 프레임까지 기다리는 구문으로 하나의 프레임을 쉬고 다음 프레임에서 다시 실행됩니다.
IEnumerator Example()
{
Debug.Log("작업 시작");
yield return null;
Debug.Log("다음 프레임에서 시작");
}
즉, Update( ) 한 번 돌고 나서, 다음 프레임의 Update( ) 직전에 다시 실행됩니다.
게임 시간 기준으로 지정한 시간(초)만큼 기다립니다.
Time.timeScale의 영향을 받기 때문에 Time.timeScale = 0f으로 설정하면 동작하지 않습니다.
실제 시간 기준으로 지정한 시간(초)만큼 기다립니다.
실제 시간 기준으로 동작하여 Time.timeScale의 영향을 받지 않습니다.
이는 게임을 일시정지하는 중에 타이머가 필요한 경우 유용합니다.
다음 FixedUpdate( ) 실행 전까지 기다립니다.
FixedUpdate( )가 보통 Rigidbody 즉, 물리연산을 처리할 때 사용합니다.
물리 연산에 맞춘 고정된 시간 간격으로 실행되는거죠
프레임의 끝까지 기다립니다.
모든 렌더링, LateUpdate(), UI 처리가 끝난 뒤에 실행됩니다.
조건을 충족하기 전까지 기다립니다.