페이지

2012년 12월 18일 화요일

StartCoroutine 과 InvokeRepeating


Unity엔진에서는 쉽게 사용 할 수 있는 두 가지 방식의 Coroutine함수를 제공한다.

Coroutine 에 대한 자세한 내용은 wikipedia 참조: http://en.wikipedia.org/wiki/Coroutine#Implementations_for_C.2B.2B

간단히 말하면 Coroutine은 여러개의 서브루틴을 생성할 수 있게 해준다. 하나의 함수에 여러개의 엔트리포인트를 둘 수 있다. 즉 하나의 함수내에서 원하는 리턴 포인트를 찍고 외부로 수행 권한을 넘긴다. 그런 후 조건이 충족되면 다시 넘겼던 포인트로 되돌아오게 된다. 이는 실행 상태를 지연시킬 수 있어 유용하며 멀티스래드와도 많이 비교되는 부분이다.
하지만 스래드의 경우 우선권에 따라 스케줄링이 되거나 Lock을 걸어 우선권을 줘야 하며 구현하는 것도 복잡한 편이고 여러가지 문제를 야기 시킨다. 반면 Coroutine은 특정 포인트에서만 스케줄링이 되며 lock을 걸지 않으며 동기화 하지 않는다.

어쨋든 내가 이 글에서 말하려고 하는 것은 이러한 Coroutine을 Unity엔진에서 간단하게 몇가지 함수로 제공해주고 있어 그것들은 좀 살펴보자는 것이다.

두 가지가 있는데 StartCoroutine과 Invoke이다.
StartCoroutine의 경우 직접 entry point를 지정하여 리턴할 수 있고, Invoke의 경우 함수 이름과 delay시간을 주어 함수내부에서 delay 하여 실행시켜 준다. 만약 좀 더 세심한 조작을 하고 싶다면 Coroutine함수를 작성하여 StartCoroutine을 이용해 작동 시키면 된다.

두 가지 방식 모두 사용이 편리하며 필요에 맞게 쓰면 되는데, 아래 간단한 예제를 보자.


For StartCoroutine(): 

Enemy Respawn 시간을 지연하는 예제

       void Start () 
       {
         StartCoroutine(SpawnAfterSeconds());
    }

       IEnumerator SpawnAfterSeconds()
    {
        while ( true)
        {
            yield return new WaitForSeconds (Random .Range (minInterval, maxInterval));

            ThrowUpEnemy();
        }
    }

위와 같이 Coroutine함수 내부에서 지연시간을 임의로 지정 할 수 있다. 또한 아래와 같은 것도 가능하다.

       void Start () 
       {
         StartCoroutine(DoSeveralTimes());
    }

       private IEnumerator DoSeveralTimes()
    {
        while ( true)
        {
            yield return new WaitForSeconds (Sometime));

            DoSomething();

                    yield return new WaitForSeconds (Sometime));

                    DoAnotherthing();

                    yield return new WaitForSeconds (Sometime));

                    DotheLastThing();
        }
    }



For InvokeRepeating(): 

Enemy Respawn 시간을 지연하는 예제

     InvokeRepeating( "ThrowUpEnemy", 1f, Random.Range(minRSInterval, respawnInterval));
     
첫번째 인자는 함수이름이고 두번째는 함수가 실행 될 지연시간이다. 마지막 인자는 함수가 첫 실행 된 후 일정시간 간격으로 반복호출을 하는 시간을 넘겨주면 된다. 
하지만 위의 함수를 실행하면 제대로 동작하지 않는다. InvokeRepeating의 경우 당연히 처음으로 넘긴 파라미터 값의 시간이 적용되기 때문이다.
이것이 StartCoroutine과 InvokeRepeating함수를 구분해서 사용해야 하는 이유이다.

     InvokeRepeating( "ThrowUpEnemy", 1f, IntervalTime);

또한 StartCoroutine이 StopCoroutine이 있듯이 Invoke함수도 CancelInvoke()함수가 있다. 그리고 IsInvoking()함수도 제공한다.
     
p.s: 추가적으로 안 사실하나 더 StartCoroutine의 경우 GameObject의 Active 가 꺼지면 실행이 중단된다. 그래서 만약 대기상태에 있던 코루틴이 중간에 분기점을 잃어버리면 다음 코드를 수행하지 못하게 된다.

반면 InvokeRepeating 함수는 오브젝트의 Active가 꺼져도 수행을 한다.


댓글 2개:

  1. 꼭 찾고 있었던 개념인데 자세한 설명 감사합니다..
    덕분에 두가지 개념이 어렴풋 알것 같은데.. 잘 이해가 안가는 부분이 있어서요
    InvokeRepeating( "ThrowUpEnemy", 1f, Random.Range(minRSInterval, respawnInterval));

    이 함수가 제대로 작동하지 못한다고하셨는데 읽어봐도 코딩 초보라 잘 모르겠습니다.. 혹시 좀 알려주실수있나요?
    그리고 InvokeRepeating과 invoke는 어떻게 다른가요?

    답글삭제
    답글
    1. Invoke는 아래 예제와 같이 해당 함수를 2초후에 실행한다는 뜻이고요. // Launches a projectile in 2 seconds

      var projectile : Rigidbody;

      Invoke("LaunchProjectile", 2);

      function LaunchProjectile () {
      var instance : Rigidbody = Instantiate(projectile);
      instance.velocity = Random.insideUnitSphere * 5;
      }

      InvokeRepeating은 아래 예제와 같이 해당함수를 2초후 실행하고 0.3초마다 반복하라는 함수입니다.

      // Starting in 2 seconds.
      // a projectile will be launched every 0.3 seconds

      var projectile : Rigidbody;

      InvokeRepeating("LaunchProjectile", 2, 0.3);

      function LaunchProjectile () {
      var instance : Rigidbody = Instantiate(projectile);
      instance.velocity = Random.insideUnitSphere * 5;
      }

      여기서 InvokeRepeating의 3번째 인자가 처음 함수가 호출될때 받았던 파라미터값을 으로 반복 실행되기때문에 Random값으로 호출이 불가능하다는 뜻이에요.

      삭제