본문 바로가기
유니티 이야기

[Unity] 유니티 코루틴 사용법 - Coroutine 사용 이유

by novices 2022. 9. 18.

코루틴이란 유니티에서 제공하는 기능으로 코드 내에서 구문 실행 도중에 처리를 대기시키거나 순차처리에 함수를 병렬로 동시에 처리하도록 구현할 수 있습니다. 유니티 api에서는 Coroutine이 스레드가 아닌 점을 명심하라고 표기되어있습니다. 말로 하면 좀 복잡해 보이는 데 사용 예시를 보면 쉽게 이해됩니다. 게임 안에서 캐릭터가 중독되어 체력이 감소하는 경우 또는 http 또는 I/O 처리와 같이 대기시간이 긴 비동기 작업을 처리하는 데 사용되며 개인적인 생각이지만 프레임당 실행되는 Update문으로 처리하기보다는 코루틴을 사용하여 지정한 시간 간격으로 처리하여 성능상 이점을 획득할 수 있습니다. 이 포스트에서는 코루틴의 기본적인 사용법 및 특성에 대해 알아보겠습니다.

 

 

1. 코루틴에 선언하기

코 루틴은 단순히 문법적으로 보면 IEnumerator형태의 데이터를 반환하는 함수입니다. 반환 데이터 형태를 IEnumerator로 선언한 코루틴은 반드시 yield 키워드를 사용해 return을 필수적으로 해야 합니다. 보통 대시 시간이 없는 경우 null을 반환하는데 null을 반환하였을 경우 한 프레임의 대기시간을 가집니다. 이런 점은 서문에서 이야기했듯이 코 루틴은 스레드가 아니고 메인 스레드에서 프레임 단위로 대기하거나 실행된다고 이해하면 됩니다. coroutine 사용하기 위해서는 먼저 System.Collections의 네임스페이스 사용을 선언해야 됩니다. 아래는 실행 시 1초 대기 후 문자열을 프린트하는 코 루틴의 선언부입니다. yield return 구문에는 null을 리턴하거나 float를 입력을 받아 생성되는 WaitForSeconds 인스턴스를 리턴합니다. 

using System.Collections;
using UnityEngine;

public class TestCoroutine : MonoBehaviour
{
    IEnumerator PrintAfterWait()
    {
    	//1초 짜리 WaitForSeconds 인스턴스를 리턴하여 1초대기 합니다.
        yield return new WaitForSeconds(1f);

        Debug.Log("실행 후 1초후 프린트 되었습니다.");
    }
}

 

코루틴 기본 요점

  • 코루틴을 사용하려면 System.Collections네임스페이스를 using 해야 합니다.
  • 코루틴은 IEnumerator 형태를 반환하는 함수입니다.
  • coroutine은 반드시 yield 구문을 통해 null 또는 WaitForSeconds의 인스턴스를 반환해야 합니다.
  • WaitForSeconds 생성자의 입력은 float로 초를 뜻합니다.

 

 

2. 코루틴 실행하기

코루틴을 실행하는 방법은 두 가지가 있습니다.  첫 번째는 StartCoroutine함수의 입력으로 함수의 이름을 전달 방법이고 두 번째는 StartCoroutine함수의 입력으로 코루틴 함수 형태로 실행하는 방법입니다. 차이점으로 첫 번째는 원하는 시점에 코루틴을 중지할 수 있기 때문에 제어권을 얻고 성능에는 최적화되지 못하는 것이고 두 번째의 경우는 성능상의 이득이 있는 것입니다. 이 부분 예시로 이야기하면 게임에서 캐릭터가 중독되어 초단위로 체력이 감소되다가 해독 물약을 사용하면 독 피해가 없어지는 경우로 들 수 있습니다. 코루틴 내에 반복 루프를 걸어 초단위로 체력이 빠지다가 해독 물약 사용 시 해당 코루틴을 중지시켜 피해가 없도록 하는 것입니다. 마지막에 예시 코드를 작성해 볼 것입니다. 아래는 코루틴의 실행코드를 start 함수 내에 작성한 예시입니다.

using System.Collections;
using UnityEngine;

public class TestCoroutine : MonoBehaviour
{
    void Start()
    {
        // 함수의이름(문자열 형태)으로 실행할 경우
        // 해당 코루틴의 중지를 제어 할 수 있다.
        StartCoroutine("PrintAfterWait");

        // or

        // 함수형태로 실행할 경우 성능에서 이득을 볼 수 있다.
        StartCoroutine(PrintAfterWait());
    }
    IEnumerator PrintAfterWait()
    {
        yield return new WaitForSeconds(1f);

        Debug.Log("실행 후 1초후 프린트 되었습니다.");
    }
}

 

코루틴 실행하기 요점

  • 코루틴을 실행하는 방법은 두 가지입니다.
  • 코루틴에 입력에 함수 형태로 전달 실행할 경우 성능을 얻을 수 있지만 제어권이 없습니다.
  • 코루틴에 입력에 문자열을 전달 실행할 경우 코루틴 종료 제어권을 얻을 수 있습니다.

 

 

3. 코루틴 정지하기

코루틴을 정지시키는 방법은 3가지가 있습니다. 첫 번째 2항에서 이야기했듯이 실행 자체를 함수 이름으로 입력받아 수행하는 경우 StopCoroutine 함수에 입력으로 함수 이름을 전달해서 해당되는 코루틴을 정지시키는 방법이 있습니다. 이런 경우는 무조건 실행할 때부터 코루틴을 함수 이름을 전달하여 실행해야 되는 제약조건이 생깁니다. 두 번째 방법은 StopAllCoroutines 함수를 실행하는 방법입니다. 이 함수는 작성된 스크립트 안에 있는 코루틴에만 영향을 미칩니다. 다만 첫 번째와는 다르게 모든 코루틴이 종료되기 때문에 여러 개의 코루틴이 실행되는 스크립트에서는 주의가 필요합니다. 마지막 세 번째는 해당 컴포넌트가 비활성화되면 모든 코루틴이 정지됩니다. 아래는 문자열로 실행한 코루틴을 정지 하는 코드와 모든 코루틴을 정지하는 코드를 Start함수내에 작성한 예시입니다.

using System.Collections;
using UnityEngine;

public class TestCoroutine : MonoBehaviour
{
    void Start()
    {
        // 함수의이름(문자열 형태)으로 실행할 경우
        // 해당 코루틴의 중지를 제어 할 수 있다.
        StartCoroutine("PrintAfterWait");

        // PrintAfterWait이름을 가진 코루틴을 종료
        StopCoroutine("PrintAfterWait");

        // or

        // 이 스크립트에서 실행되는 모든 코루틴 종료
        StopAllCoroutines();
    }
    IEnumerator PrintAfterWait()
    {
        yield return new WaitForSeconds(1f);

        Debug.Log("실행 후 1초후 프린트 되었습니다.");
    }
}

 

코루틴 정지하기 요점

  • 이름을 가지고 실행된 코루틴은 StopCoroutine함수에 문자열을 전달해 해당되는 코루틴만 정지시킬 수 있습니다.
  • StopAllCoroutines 함수를 통해 스크립트에서 실행되는 모든 코루틴을 정지시킬 수 있습니다.
  • 코루틴이 작성된 스크립트(컴포넌트)가 비활성화되는 경우 모든 코루틴은 정지됩니다.

 

 

4. 코루틴 예시

게임 안에서 캐릭터가 중독되어 체력이 감소하는 코루틴을 실행하고 해독 물약을 사용해 코루틴을 중지시키는 예시를 작성해 보았습니다. 대표적으로 많이 사용되는 코루틴 + 반복 루프 로직이고 서문에서 이야기했던 http 또는 I/O 처리 대기에 사용되는 예시는 다루지 않습니다. 관련 코드는 www.SendWebRequest 함수를 확인해 보시길 바랍니다. 또는 제가 작성한  구글 시트에서 데이터 읽어오기 포스팅에서 사용한 예시 코드에 포함되어있습니다.

using System.Collections;
using UnityEngine;

public class Player : MonoBehaviour
{
    int healthPoint = 1000;

    void Start()
    {
        StartCoroutine("DamageByPoison");
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.R))
            StopCoroutine("DamageByPoison");
    }
    IEnumerator DamageByPoison()
    {
        int poisonDamage = 10;
        // 무한 루프
        while(true)
        {
            healthPoint -= poisonDamage;
            yield return new WaitForSeconds(1f);
        }
    }
}

 

예시 확인사항

  • 예시는 코루틴 내부에 while로 1초 단위 실행되는 무한루프를 걸어 프로그램 구동에 영향이 없도록 하였습니다.
  • 예시는 스크립트 안에 코루틴이 한 개만 존재하여 StopCoroutine대신 StopAllCoroutines함수를 사용해도 무관합니다.
  • 예시의 코루틴을 Update문으로 전환 가능하지만 코드가 복잡해질 수 있습니다.
  • 프레임 단위로 실행되는 Update문으로 기능을 구현하는 것보다 yield문의 원하는 시간 간격으로 의도적인 무한반복 루프가 성능 최적화에도 이점이 많습니다.

 

 

댓글