[C#] 코루틴 (Coroutine)

dain·2024년 7월 15일

C#

목록 보기
5/5
post-thumbnail

코루틴

  • 열거자(Enumerator)를 이용해 여러 프레임에 걸쳐 실행할 수 있는 메서드

    💛 C#에서 일반적인 메서드는 단일 프레임 내에서 실행된다.
    행된다.

  • 업데이트 메서드와는 다르게, 코루틴은 실행을 일시 정지한 후 메서드 제어권을 유니티에게 반환하고, 특정 조건이 되면 다음 프레임부터 다시 진행할 수 있다.
  • 시간의 흐름에 따른 이벤트, 애니메이션, 비동기 작업 처리 등에 사용된다.
  • 코루틴 사용 조건
    • 코루틴을 사용할 클래스는 Monobehaviour을 상속받아야 한다.
    • IEnumerator를 반환해야 한다.
    • yield return (조건)을 사용해야 한다.


코루틴 함수 정의

  • 코루틴 함수의 기본 형태
    IEnumerator 함수이름() 
    {
       yield return (조건);
    }
  • 반환형: IEnumerator

    🩵 열거자 (Enumerator)

    • 배열, 컬렉션을 단순히 열거 및 순회하기 위해 만들어진 것
      (foreach 문에서 사용된다.)
    • 한 프레임 내에 실행된다.
    • 코루틴은 열거자를 기반으로 만들어졌지만, 열거할 때마다 원하는 특정 프레임에 실행되도록 작동한다.
  • yield return

    • 해당 지점에 도착할 경우, 코루틴은 코드의 동작을 중지하고 대기한다.
      = 코드의 제어권을 유니티에 다시 돌려준다.
      = 실행되는 프레임이 달라진다.

    • 대기 조건이 충족되면 다시 코루틴이 다음 줄부터 실행된다.

    • 종류

      // null : 다음 Update 호출까지 기다림 (한 프레임 동안 대기)
      yield return null;
               
      // WaitForSeconds: 매개변수(초, 유니티 시간) 동안 기다림
      yield return new WaitForSeconds(1f);
               
      // WaitForSecondsRealtime : 매개변수(초, 실제 시간) 동안 기다림
      yield return new WaitForSecondsRealtime(1f);
               
      // WaitForFixedUpdate : 다음 Fixed Update 호출까지 기다림
      yield return new WaitForFixedUpdate();
               
      // WaitForEndOfFrame : 모든 렌더링 작업이 완료될 때까지 기다림
      yield return new WaitForEndOfFrame();
      
      // WaitUntil : 조건이 참이 될 때까지 기다림
      yield return new WaitUntil(() => (조건));

      🩵 Realtime
      : 유니티의 time scale의 영향을 받지 않는 실제 시간

  • yield break

    • 해당 지점에 도착할 경우, 코루틴이 완전히 멈추고 제어권이 유니티 엔진에게 넘어간다.

    • 사용 예
      : 코루틴 내부에서 특정 조건을 만족할 경우 코루틴을 멈추고자 할 때



코루틴 함수 호출 방법

: StartCoroutine 이용

  1. 코루틴 함수 직접 호출
    • 여러 개의 매개변수 전달 가능
    • IEnumerator TestCoroutine(int count)
      {
      }
      StartCorountine(TestCorountine(5));
  1. 코루틴 함수의 이름으로 호출
    • 하나의 매개변수만 전달 가능
    • StartCorountine("TestCorountine", 5);


코루틴 함수 중단

  • StopCoroutine 이용

    1. 코루틴 함수 직접 중단
      • 잘못된 예시
        StartCoroutine(TestCoroutine());
        StopCoroutine(TestCoroutine());
        : 위와 같은 방식으로 코루틴 함수를 실행시키고 중지시키는 경우, 각 TestCoroutine 함수가 반환한 IEnumerator는 별개의 것이기 때문에 코루틴이 정상적으로 중지되지 않는다.
        → 별도의 IEnumerator 형식의 변수를 선언하고 사용해야 한다.

      • 올바른 예시
        IEnumerator enumerator;
        eumerator = TestCoroutine(5);
        StartCoroutine(enumerator);
        StopCorountine(enumerator);
    1. 코루틴 함수의 이름으로 중단
      StopCoroutine("TestCoroutine")
      • 같은 이름의 코루틴 함수가 두 개 이상 호출된 경우, 모든 코루틴이 동시에 중단된다.
      • 코루틴 함수를 직접 호출해 실행한 코루틴은 이름을 통해 중단시킬 수 없다.

  • StopAllCoroutine 이용

    • 해당 컴포넌트가 실행하고 있는 모든 코루틴 중단


코루틴 사용시 주의사항

  • 코루틴은 Start 함수가 호출된 이후에 사용해야 한다.
  • 코루틴 함수가 작성된 스크립트를 부착 게임 오브젝트가 활성화된 상태에서 사용해야 한다.
  • 코루틴 내부에서 무한 루프를 돌릴 때,
    반드시 코루틴 내부의 반복문 안에 yield return을 작성해 적절하게 제어권을 양보해줘야 한다.

    • 잘못된 예시
      IEnumerator InfityLoop() 
      {
          yield return null;
          while (true) 
          {
                 
          }
      }
      : 코루틴의 while문 내부에서 유니티로 제어권을 돌려주는 부분이 없기 때문에 코루틴이 제어권을 독점하게 된다. 따라서 유니티 에디터가 멈추는 문제가 발생한다.

    • 올바른 예
      IEnumerator InfityLoop() 
      {
          while (true) 
          {
              yield return null;
          }
      }
  • 코루틴은 스레드가 아니기 때문에 코루틴은 메인 스레드에서 실행된다.
    멀티 스레드와 코루틴은 서로 다른 개념이다.

    🩵 비동기(Asynchronous)

    • 여러 작업이 동시에 실행되거나, 한 작업이 완료되기 전에 다른 작업이 시작되는 것
    • 비동기 작업 != 멀티스레드
  • 코루틴 사용시 가비지가 생성되기 때문에 필요한 부분에만 사용하고, 반복적으로 사용되는 로직이라면 업데이트 함수에서 처리하는 것이 좋다.

    • 코루틴 실행 (StartCoroutine 호출) 과정에서 유니티 내부적으로 가비지가 생성됨
    • 대기 조건에 사용되는 인스턴스를 new로 생성할 때마다 가비지 생성됨


코루틴 사용 Tip

  • Start 메서드 자체를 코루틴처럼 사용할 수 있다.

    IEnumerator  Start() {
    	yield return null;
    }
  • 물리 법칙 / 입력 / 렌더링과 관련된 메서드들은 코루틴처럼 사용할 수 있다.

    IEnumerator  OnCollisionEnter() {
    	yield return null;
    }
    
    IEnumerator  OnMouseEnter() {
    	yield return null;
    }
  • 인스턴스 생성으로 인한 가비지는 캐싱을 통해 줄일수 있다.
    public static class Util : Monobehaviour
    {
      public static readonly WaitForEndOfFrame WaitForEndOfFrame =  new WaitForEndOfFrame();
      public static readonly WaitForFixedUpdate WaitForFixedUpdate = new WaitForFixedUpdate(); 
      
      private static readonly Dictionary<float, WaitForSeconds> _WaitForSeconds = new Dictionary<float, WaitForSeconds>();
      
      public static WaitForSeconds WaitForSecond(float seconds)
      {
          if(_WaitForSeconds.TryGetValue(seconds, out var waitForSeconds))
          {
              wfs = new WiatForSeconds(seconds);
              _WaitForSeconds.Add(seconds, wfs);
          }
          return wfs;
      }
    }
profile
자라나는 감자

0개의 댓글