본문 바로가기

유니티/매뉴얼

[유니티 매뉴얼] 코루틴(CoRoutine)

728x90
반응형

1. 코루틴

일반 함수를 호출하면 값을 반환하기 전에 실행 완료됩니다.
이는 함수에서 수행되는 모든 액션이 하나의 프레임 업데이트 내에서 발생해야 한다는 것을 의미합니다. 
시간이 지남에 따라 절차식 애니메이션이나 일련의 이벤트를 포함하는 데 함수 호출을 사용할 수 없습니다.
함수는 하나의 프레임 업데이트 내에서 전체적으로 실행됩니다. 

하지만, 코루틴은 실행을 일시 중지하고 Unity에 제어 권한을 반환한 후 다음 프레임에서 중단했던 위치에서 계속할 수 있는 함수와 같습니다.

 

2. 코루틴 선언

코루틴은 다음과 같이 선언됩니다.

 ① System.Collections를 Using합니다.(유니티 에디터에서 Create > C# Scipt 하면 자동으로 Using됩니다.)

 ② 반환 데이터 타입을 IEnumerator 으로 선언합니다.

 ③ 함수 내에 yield 반환문으로 선언합니다.

 

① using System.Collections; 
using UnityEngine; 

public class 클래스명 : MonoBehaviour 
{ 
    ... 
    ② IEnumerator 코루틴메서드명() 
    { 
        ③ yield 반환문; 
    } 
}

 

using System.Collections; // ①
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;

    void Start()
    {
        m_Coroutine = CoroutineMethod();
        StartCoroutine(m_Coroutine); // 코루틴을 시작하는 함수
    }

    IEnumerator CoroutineMethod() // ②
    {
        Debug.Log("시작");
        yield return new WaitForSeconds(1f); // ③
        Debug.Log("1초 후 ...");
    }
}

 

3. 코루틴 사용

 ① StartCoroutine(routine) : 코루틴 시작. StopCoroutine 사용 후에는 사용하면, 멈춘 위치에서 다시 시작

 ② StopCoroutine(routine) : 코루틴 잠시 멈춤(중지). 다시 StartCoroutine(routine)하면, 코루틴이 계속됩니다.

 ③ StopAllCoroutines() : 이 클래스내 돌고 있는 모든 코루틴 중지.

 ④ SetActive(false)로 인해 연결된 게임 오브젝트가 비활성화될 때에도 코루틴이 중지됩니다.

 ⑤ MonoBehaviour 인스턴스에서 enabled를 false로 설정하여 MonoBehaviour를 비활성화한 경우에는 코루틴이 중지되지 않습니다.

 ⑥ 기본적으로 코루틴은 코루틴 시작 → 코루틴 중지 코루틴 멈춘 위치에서 다시 시작  코루틴 중지 ... 순으로 실행 됩니다. 

  다시 말하면, StartCoroutine StopCoroutine 또는 StopAllCoroutines()  StartCoroutine StopCoroutine 또는 StopAllCoroutines() ... 

  그래야 순차적으로 코루틴이 실행됩니다. 

 

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;


    void Start()
    {
        m_Coroutine = CoroutineMethod();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
            StartCoroutine(m_Coroutine); // ① CoroutineMethod1 시작, ③ StopCoroutine 후 멈춘 위치에서 다시 계속
    
        if (Input.GetKeyDown(KeyCode.B))
            StopCoroutine(m_Coroutine); // ②-1 CoroutineMethod1 일시정지

        if (Input.GetKeyDown(KeyCode.C))
            StopAllCoroutines();  // ②-2 CoroutineMethod1, CoroutineMethod2 일시정지
    }

    IEnumerator CoroutineMethod()
    {
        int count = 0;

        while (true)
        {
            Debug.Log(count);
            yield return new WaitForSeconds(1.0f);
            count++;
        }
    }
}

 

4. 코루틴 재사용 (처음부터 재시작)

StartCoroutine와 StopCoroutine이 순차적으로 반복(StartCoroutineStopCoroutineStartCoroutine ... )하여 코루틴을 제어하게 되면, 한번 시작한 코루틴은 계속 진행되면서 시작과 멈춤, 멈춘 위치에서 다시 시작을 반복합니다.

그리고 코루틴 내에 정해진 실행이 완료되면 코루틴이 사실상 종료 됩니다.

하지만, 경우에 따라서 코루틴을 처음부터 다시 사용해야 하는 경우가 있습니다.

 

4.1 무한반복문(while문)을 종료하여, 코루틴 종료

 

코루틴을 사용 할 때, 실행 도중에 단순 반복 작업을 하기 위해 생각보다 많이 while문을 사용합니다.

경우에 따라서, 메서드내에 무한반복문(while(true))을 사용하여 동일한 실행을 반복하고 있습니다.

이 경우 while문의 조건을 외부 변수로 선언하여, update문 등에서 변경하여 무한반복문을 벗어나게 할 수 있습니다.

이렇게 되면 무한반복문을 벗어나게 되고, 결국 코루틴을 완료하여 종료할 수 있습니다.

 

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;
    private bool m_IsBreak; // 무한반복문 종료를 위한 변수

    void Start()
    {
        m_Coroutine = CoroutineMethod();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
            StartCoroutine(m_Coroutine); 

        if (Input.GetKeyDown(KeyCode.B))
            m_IsBreak = true; // 무한반복문 종료

    }

    IEnumerator CoroutineMethod()
    {
        int count = 0;

        while (!m_IsBreak)
        {
            Debug.Log(count);
            yield return new WaitForSeconds(1.0f);
            count++;
        }
    }
}

 

4.2 코루틴 상태 확인 : 실행 중 ?, 코루틴 종료 상태 확인

 

코루틴을 매개 변수에 out, ref 등을 사용할 수 없어서 코루틴 내에 실행 중이거나, 종료를 확인할 수 있는 방법이 별로 없습니다.

하지만, 코루틴은 비동기 방식이기 때문에 경우에 따라서는 코루틴의 실행 유무나 종료 유무를 확인해야 하는 경우가 있습니다.

이 경우에도 위와 같이 멤버변수에 bool 변수 하나를 선언하여, 코루틴의 실행 유무나 종료 유무를 확인하면 됩니다.

 

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;
    private bool m_IsCoroutineing; // 코루틴 실행 유무 확인

    void Start()
    {
        m_Coroutine = CoroutineMethod();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            StartCoroutine(m_Coroutine);
        }

        if(m_IsCoroutineing)
        {
            Debug.Log("코루틴이 실행 전이거나 종료되었습니다.");
        }

    }

    IEnumerator CoroutineMethod()
    {
        m_IsCoroutineing = true;

        Debug.Log("시작");

        yield return new WaitForSeconds(1.0f);
        Debug.Log("종료");

        m_IsCoroutineing = false;
    }
}

 

4.3 코루틴 처음부터 재사용

 

이미 완료된 코루틴을 재사용 해야 할 경우가 있습니다.

코루틴에도 매개변수를 사용하여, 여러가지 형태로 재사용 할 수 있습니다.

이런 경우, 코루틴을 재할당 하여 시작하면 됩니다.

만약, 코루틴을 제어하기 위해 사용한 멤버변수가 있다면, 경우에 따라서 값을 리셋을 해야 할 때도 있습니다.

 

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;
    private bool m_IsBreak;

    void Start()
    {
        m_Coroutine = CoroutineMethod();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
            StartCoroutine(m_Coroutine); // CoroutineMethod1 시작

        if (Input.GetKeyDown(KeyCode.B))
            m_IsBreak = true; // while문 break를 사용하여 CoroutineMethod while문 완료

        if (Input.GetKeyDown(KeyCode.C))
        {
            m_IsBreak= false; // 맴버 변수 리셋
            m_Coroutine = CoroutineMethod(); // CoroutineMethod 처음부터 다시 시작
            StartCoroutine(m_Coroutine);
        }
    }

    IEnumerator CoroutineMethod()
    {
        int count = 0;

        while (true)
        {
            if (m_IsBreak)
                break;

            Debug.Log(count);
            yield return new WaitForSeconds(1.0f);
            count++;
        }
    }
}

 

5. 코루틴 반환 타입

코루틴은 아래의 반환 타입에 따라서 실행을 일시 중지하고 Unity에 제어 권한을 반환한 후 대기합니다.

 

코루틴 반환 타입

설명

yield return null;

다음 프레임까지 대기

yield return new WaitForSeconds(loat time);

time초만큼 대기

yield return new WaitForSecondsRealtime(float time);

time(Realtime)초만큼 대기

yield return new WaitForFixedUpdate();

다음 FixedUpdate까지 대기

yield return new WaitForEndOfFrame();

모든 렌더링 작업이 끝날 때까지 대기

yield return new WaitUntil(Func<bool> predicate);

조건을 만족할 때까지 대기

yield return new WaitWhile(Func<bool> predicate);

조건을 만족하지 않을 때까지 대기

yield return StartCoroutine(string name);

다른 코루틴이 끝날 때까지 대기

 

6. 코루틴 최적화

코루틴 내에서는 반복문(while문, for문 등)을 사용하여, 동일 실행이나 유사한 실행을 반복하는 경우가 있습니다.

이때  변환 타입을 보면, new 연산자를 사용하여 계속 인스턴스를 생성하기 때문에 결국은 가비지를 생성하게 됩니다.

이 것을 해결 하기 위해서 아래의 방법으로 캐싱을 하여 사용합니다.

 

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private IEnumerator m_Coroutine;
    public static readonly WaitForSeconds m_waitForSecond2s = new WaitForSeconds(2f); // 캐싱

    void Start()
    {
        m_Coroutine = CoroutineMethod();
        StartCoroutine(m_Coroutine);
    }

    IEnumerator CoroutineMethod()
    {
        int count = 0;

        while (true)
        {
            Debug.Log(count);
            yield return m_waitForSecond2s; // 사용
            count++;
        }
    }
}

 

7. 코루틴를 사용하면서 하는 소소한 실수들 ...(Coroutines not working!!)

7.1 StartCoroutine를 사용하지 않고 직접 코루틴 메서드 호출

 

처음 코루틴을 사용할 때, 코루틴을 만들어 놓고 코루틴을 호출 할 때 StartCoroutine를 사용하지 않고 직접 코루틴 메서드를 호출 하는 실수를 종종 할 수 있습니다.

코루틴메서드명(); StartCoroutine(코루틴메서드명());

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{    
    void Start()
    {
        CoroutineMethod(); // 실수 ㅠㅠ
    }

    IEnumerator CoroutineMethod() 
    {
        Debug.Log("시작");
        yield return new WaitForSeconds(1f);
        Debug.Log("1초 후 ...");
    }
}

 

7.2  IEnumerable vs IEnumertor

 

비주얼 스튜디오에서 제공하는 자동 완성 기능 때문에, ie만 입력하더라도 IE... 관련 키워드가 바로 나옵니다.

그러면 별 생각없이 IEnumerable를 선택 할 때가 있습니다.

주의하여 사용하셔야 합니다.

 

 

7.3 무한루프문(while(true)문) 내 yield 반환문

 

무한루프문을 사용할 때, 무한루프 내에 yield 반환문을 꼭 넣고 플레이어 해야 합니다.

아니면, 프로그램 자체가 무한루프에 빠져서 정지가 되지 않는 경우가 발생하고, 그럼 지금까지 작업 한 씬을 저장 못하고 나올 경우가 발생합니다.

728x90
반응형