코루틴을 사용하면 작업을 다수의 프레임에 분산할 수 있습니다. 코루틴은 실행을 일시 정지하고 제어를 Unity에 반환하지만 중단한 부분에서 다음 프레임을 계속할 수 있는 메서드입니다.
대부분의 경우 메서드를 호출하면 실행을 완료한 뒤 호출한 메서드에 제어와 선택적 반환 값을 반환합니다. 즉 메서드 내에서 발생한 모든 행동은 단일 프레임 업데이트 내에서 발생해야 합니다.
시간의 흐름에 따른 이벤트의 시퀀스나 절차적 애니메이션을 포함하기 위해 메서드 콜을 사용하고자 하는 상황에서 코루틴을 사용할 수 있습니다.
참고: 코루틴은 스레드가 아닙니다. 코루틴 내에서 실행되는 동기식 연산은 여전히 메인 스레드에서 실행됩니다. 메인 스레드에 소비하는 CPU 시간을 줄이려면 다른 스크립트 코드와 마찬가지로 코루틴에서 작업을 차단하지 않는 것이 중요합니다. Unity에서 멀티 스레드 코드를 사용하려면 다음 옵션을 사용하십시오.
Awaitable 지원HTTP 전송, 에셋 로드, 파일 I/O 완료 등을 기다리는 것과 같이 긴 비동기 작업을 처리해야 하는 경우 코루틴을 사용하는 것이 가장 좋습니다.
한 가지 예로 오브젝트의 알파(불투명도) 값을 다음과 같이 보이지 않을 때까지 점차 줄이는 작업을 고려해 볼 수 있습니다.
void Fade()
{
Color c = renderer.material.color;
for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
{
c.a = alpha;
renderer.material.color = c;
}
}
이 예시에서 Fade 메서드는 예상한 효과를 내지 못합니다. 페이드 효과가 보이게 하려면 Unity가 렌더링하는 중간 값을 표시하기 위해 프레임 시퀀스에 걸쳐 페이드의 알파를 줄여야 합니다. 하지만 이 예시 메서드는 단일 프레임 업데이트 내에서 완전히 실행됩니다. 중간 값은 표시되지 않으며, 오브젝트는 즉시 사라집니다.
이 상황을 해결하기 위해 프레임 단위로 페이드를 실행하는 Update 함수에 코드를 추가할 수 있습니다. 하지만 이러한 종류의 작업에는 코루틴을 사용하는 것이 더 편리할 수 있습니다.
C#에서 다음과 같이 코루틴을 선언합니다.
IEnumerator Fade()
{
Color c = renderer.material.color;
for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
{
c.a = alpha;
renderer.material.color = c;
yield return null;
}
}
코루틴은 IEnumerator 반환 유형과 본문에 포함된 yield 반환 문으로 선언하는 메서드입니다. yield return null 라인은 다음 프레임에서 실행이 일시 중지되고 재개되는 지점입니다. 코루틴 실행을 설정하려면 StartCoroutine 함수를 사용해야 합니다.
void Update()
{
if (Input.GetKeyDown("f"))
{
StartCoroutine(Fade());
}
}
Fade 함수의 루프 카운터는 코루틴의 수명 동안 올바른 값을 유지하며 다른 변수나 파라미터는 yield 문 간에 보존됩니다.
기본적으로 Unity는 yield 문 이후 프레임의 코루틴을 재개합니다. 시간 지연을 추가하려면 다음과 같이 WaitForSeconds를 사용하십시오.
IEnumerator Fade()
{
Color c = renderer.material.color;
for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
{
c.a = alpha;
renderer.material.color = c;
yield return new WaitForSeconds(.1f);
}
}
WaitForSeconds를 사용하여 한동안 효과를 전파할 수 있고 Update 메서드 안의 작업을 포함하는 대신으로 사용할 수 있습니다. Unity는 초당 여러 번 Update 메서드를 호출하며 작업을 자주 반복할 필요가 없는 경우 코루틴에 넣어 정기적으로 업데이트를 할 수는 있지만 모든 단일 프레임을 업데이트할 수는 없습니다.
예를 들어 애플리케이션에 다음의 코드를 사용하여 적이 근처에 있을 경우 경고를 하는 알람을 넣을 수도 있습니다.
bool ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
return true;
}
}
return false;
}
적이 많은 경우 이 함수를 모든 프레임에 호출하면 상당한 오버헤드가 발생할 수 있습니다. 하지만 코루틴을 사용하여 0.1초마다 호출할 수 있습니다.
IEnumerator DoCheck()
{
for(;;)
{
if (ProximityCheck())
{
// Perform some action here
}
yield return new WaitForSeconds(.1f);
}
}
이렇게 하면 게임플레이에 눈에 띄는 영향 없이 수행되는 검사 수를 줄입니다.
코루틴을 중지하려면 StopCoroutine와 StopAllCoroutines를 사용하십시오. 코루틴이 연결된 게임 오브젝트를 비활성화하기 위해 SetActive를 false로 설정한 경우에도 코루틴이 중지됩니다. example이 MonoBehaviour 인스턴스인 Destroy(example)를 호출하면 즉시 OnDisable이 트리거되고 Unity가 코루틴을 처리하여 효과적으로 중지합니다. 마지막으로 프레임 끝에 OnDestroy가 호출됩니다.
참고: enabled를 false로 설정하여 MonoBehaviour를 비활성화한 경우 Unity는 코루틴을 중지하지 않습니다.
코루틴은 다른 스크립트 코드와 다르게 실행됩니다. 대부분의 Unity 스크립트 코드는 단일 위치의 성능 트레이스 내, 특정 콜백 호출 아래에 나타납니다. 반면, 코루틴의 CPU 코드는 항상 트레이스의 두 곳에서 나타납니다.
코루틴의 모든 시작 코드(코루틴 메서드의 시작부터 첫 번째 yield 문까지)는 Unity가 코루틴을 시작할 때마다 트레이스에 나타납니다. 시작 코드는 StartCoroutine 메서드가 호출될 때마다 나타납니다. Unity 콜백(IEnumerator를 반환하는 Start 콜백 등)에서 생성된 코루틴은 각 Unity 콜백에서 최초로 나타납니다.
코루틴의 나머지 코드(즉 다시 시작하는 시점에서부터 실행이 종료될 때까지의 코드)는 Unity 메인 루프에 있는DelayedCallManager 라인에서 나타나게 됩니다.
이는 Unity가 코루틴을 실행하는 방식 때문입니다. C# 컴파일러는 코루틴을 백업하는 클래스의 인스턴스를 자동으로 생성합니다. 그런 다음 Unity는 이 오브젝트를 사용하여 단일 메서드를 여러 번 호출하는 동안 코루틴의 상태를 추적합니다. 코루틴에서 로컬 범위 변수가 yield 호출이 진행되는 동안 유지되어야 하기 때문에 Unity는 로컬 범위 변수를 생성된 클래스로 옮깁니다. 이렇게 하면 코루틴이 작동되는 동안 힙에 할당된 상태로 남아 있습니다. 이 오브젝트는 또한 코루틴의 내부 상태를 추적하여 yield 호출 이후에 코루틴이 코드의 어느 부분부터 다시 시작해야 하는지를 기억합니다.
그렇기 때문에 코루틴을 시작할 때 메모리 사용량은 고정된 오버헤드 할당에 로컬 범위 변수의 크기를 합한 양과 동일합니다.
코루틴을 시작하는 코드는 오브젝트를 생성하고 호출하며 그 이후 Unity의 DelayedCallManager가 코루틴의 yield 조건이 만족될 때마다 다시 오브젝트를 호출합니다. 코루틴은 보통 다른 코루틴의 외부에서 시작하기 때문에 이는yield 호출과 DelayedCallManager 사이에서 실행 오버헤드를 나눕니다.
Unity 프로파일러를 사용하여 Unity가 애플리케이션에서 코루틴을 실행하는 위치를 검사하고 파악할 수 있습니다. 이렇게 하려면 스크립트 코드의 모든 부분을 프로파일링하고 모든 함수 호출을 기록하는 Deep Profiling을 활성화하여 애플리케이션을 프로파일링하십시오. 그런 다음 CPU Usage Profiler 모듈을 사용하여 애플리케이션의 코루틴을 조사할 수 있습니다.
일련의 작업을 최대한 적은 개별 코루틴 수로 압축하는 것이 가장 좋습니다. 중첩된 코루틴은 코드 명확성 및 유지 관리에 유용하지만 코루틴이 오브젝트를 추적하기 때문에 메모리 오버헤드가 더 많이 소모됩니다.
코루틴이 매 프레임마다 실행되고 오래 실행되는 작업에서 yield되지 않는 경우 Update 또는 LateUpdate 콜백으로 대체하는 것이 더 효과적입니다. 이는 오래 실행되거나 무한 루프되는 코루틴의 경우에 유용합니다.