목표

  • Coroutines에 대해 알아보자.

Unity Thread 구조

  • Unity는 기본적으로 싱글스레드 기반으로 동작했다.
  • 2019년에서야 DOTS(Data-Oriented Technology Stack)등장했고, 멀티스레드 환경을 지원했다.
  • 복잡하지 않은 게임을 만들거나, 스크립트를 막 공부한 사람이라면 싱글스레드 환경에서 개발하게 될 것이다.

코루틴(Coroutine)

  • Update 함수를 통해 프로그램 내용을 갱신할 수 있다.
  • 다만 Update 함수에서 작업이 완료되지 않으면, 다음 frame으로 넘어가지 않고 작업 완료를 대기한다.
  • 함수의 작업시간이 길다면 frame이 넘어가지 않아 렉이 발생한다.
  • 이 문제를 해결하기 위해 Unity는 시분할(time-sharing) 방식의 Coroutine 사용한다.

예제

  • 코루틴은 StartCoroutine을 사용하여 호출 할 수 있다.
  • 함수는 한번 실행 되지만, 리턴되는 결과는 1, 2, 3 이다.
  • yield return 문을 만나기까지 동작하고, 중간 중간 결과를 리턴한다.
  • 마지막 yield return이 실행되면, 종료된다.
void Start()
{
    StartCoroutine(Numbers());

    // 혹은 아래와 같은 방식으로 호출 가능하다.
    // StartCoroutine("Numbers");

}

IEnumerator Numbers() 
{
    yield return 1;

    yield return 2;

    yield return 3;
}
  • Unity에서는 알파 채널값(투명도) 감소를 예제로 제시하고 있다.
  • 코루틴을 사용하지 않고, Fade 함수를 호출하면 알파가 감소되는 과정을 볼 수 없다.
void Start()
{
    // 여기서는 알파가 1인 상태
    Fade();

    // 여기서는 알파가 0인 상태
}

void Fade() 
{
    // 알파값을 1부터 0까지 줄임
    // 알파가 0이면 투명해짐
    for (float f = 1f; f >= 0; f -= 0.1f) 
    {
        // 색깔
        Color c = renderer.material.color;

        // 감소된 알파값 적용
        c.a = f;
        renderer.material.color = c;
    }
}
  • 하지만 코루틴을 사용하면, 알파의 변화 과정을 볼 수 있다.
// StartCoroutine은 MonoBehaviour를 상속 받은 클래스에서만 동작한다.
public class SampleScript : MonoBehaviour
{
  void Start() 
  {
      // 코루틴 함수는 StartCoroutine 함수로 호출한다.
      StartCoroutine("Fade");
  }

  // 코루틴 함수는 IEnumerator 리턴형을 가진다.
  IEnumerator Fade() 
  {
      for (float f = 1f; f >= 0; f -= 0.1f) 
      {
          Color c = renderer.material.color;
          c.a = f;
          renderer.material.color = c;

          // yield return은 여기까지 실행되고 frame을 넘기라는 뜻
          yield return null;
      }
  }
}
  • 여기서 문제가 발생한다. 보통 60fps이면 1frame에 0.01666초라는 소리인데... 이걸 본다고?

  • 이 문제를 해결하기 위해, WaitForSeconds를 사용한다.
// StartCoroutine은 MonoBehaviour를 상속 받은 클래스에서만 동작한다.
public class SampleScript : MonoBehaviour
{
  void Start() 
  {
      // 코루틴 함수는 StartCoroutine 함수로 호출한다.
      StartCoroutine("Fade");
  }

  // 코루틴 함수는 IEnumerator을 리턴형으로 가진다.
  IEnumerator Fade() 
  {
      for (float f = 1f; f >= 0; f -= 0.1f) 
      {
          Color c = renderer.material.color;
          c.a = f;
          renderer.material.color = c;

          // WaitForSeconds를 사용하면, 다음 코루틴의 실행까지 대기 시간을 설정할 수 있다.
          yield return new WaitForSeconds(1f);
      }
  }
}

 

반환형 설명
yield return null 다음 프레임까지 대기
yield return new WiatForSeconds(float) 지정된 시간(초)만큼 대기
yield return new WaitForFixedUpdate() 다음 물리 업데이트 프레임까지 대기
yield return new WaitForEndOfFrame() 현재 프레임이 끝날때까지 대기
yield return StartcoRoutine(string) 새로운 코루틴 함수를 실행하고, 그 함수가 종료될때까지 대기
yield return new WWW(string) 통신 작업이 끝날 때까지  대기
yield return new AsyncOperation 비동기 작업이 완료되기 전까지 대기

코루틴 중지

  • StopCoroutine을 사용하면 코루틴을 중지하는 방법은 3가지가 있다.
public class SampleScript : MonoBehaviour
{
    private IEnumerator enumerator;
    private Coroutine coroutine;

    void Start() 
    {
        enumerator = Fade();
        // 코루틴 함수는 StartCoroutine 함수로 호출한다.
        coroutine = StartCoroutine(enumerator);
    }

    void Update()
    {
      if (Input.GetKeyDown(KeyCode.A))
      {
          // IEnumerator로 실행할 코루틴을 저장하고 있다가 정지에 사용하는 방법
          StopCoroutine(enumerator);
      }

      if (Input.GetKeyDown(KeyCode.B))
      {
          // 함수 이름으로 현재 동작중인 코루틴을 찾아 중지하는 방법
          StopCoroutine("Fade");
      }

      if (Input.GetKeyDown(KeyCode.C))
      {
          //코루틴으로직접 중지하는 방법
          StopCoroutine(coroutine);
      }
    }

    IEnumerator Fade() 
    {
        for (float f = 1f; f >= 0; f -= 0.1f) 
        {
            Color c = renderer.material.color;
            c.a = f;
            renderer.material.color = c;
            yield return new WaitForSeconds(1f);
        }
    }
}
  • 혹은 yield break를 사용하여 코루틴을 즉시 정지시킬 수 있다.
    IEnumerator Fade() 
    {
        for (float f = 1f; f >= 0; f -= 0.1f) 
        {
            Color c = renderer.material.color;
            c.a = f;

            if (f <= 0.4f)
            {
                // 코루틴 즉시 정지
                yield break;
            }

            renderer.material.color = c;
            yield return new WaitForSeconds(1f);
        }
    }

+ Recent posts