C#/c#

c# 코루틴(Coroutine, IEnumerator, yield), 코루틴 중단이 안될 때

스카이부침개 2022. 1. 27. 00:15

nameof 연산자는 Type이나 메서드 등의 '이름'을 리턴해주는 것이다.
기존에는 이 이름들을 어딘가에 사용해야 할 때에는 string으로 직접 하드코딩을 해야 했으며, 이러한 하드코딩은 IDE에서 제대로 된 참조가 되지 않기 때문에 디버깅에 어려운 점이 있었다.

(유니티의 StartCoroutine은 string을 받기 때문에 이 코루틴이 사용되는 코루틴인지 아닌지 확인하는건 매우 까다롭다.

실제로 b스크립트에서 a스크립트의 코루틴을 실행하고 a에서 stopcoroutine을 했지만 동작하지 않는 오류가 발생했다.)

(IDE : 통합 개발 환경Integrated Development Environment ex) 비주얼스튜디오)

 

하지만 nameof 연산자를 이용하면 마법처럼 이름이라는 문자열을 반환해준다.

void Awake()
{
    StartCoroutine(nameof(SomeCoroutine));
}

IEnumerator SomeCoroutine()
{
    yield return null;
}

이제 IDE에서 SomeCoroutine이 정말로 사용되는 코루틴인지 확인할 수 있게 된 것이다.

 

 

바보같이 코루틴만 실행하면 내부가 자동으로 반복문이 된다고 생각한적이 있다.

따로 반복문으로 계속 반환해주면 된다.

void Awake()
{
    StartCoroutine(nameof(SomeCoroutine));
}

IEnumerator SomeCoroutine()
{
    while(true)  아무 조건 없이 true뿐이니까 무한 반복이 된다.
    {
        Debug.Log("1초마다 출력");
        yield return new WaitForSeconds(1);
    }
}

StopCoroutine이 제대로 작동하지 않는 경우

Coroutine 타입 변수 사용
Coroutine runningCoroutine = null;  //코루틴 변수. 1개의 루틴만 돌리기 위해 저장한다.

//만약 이미 돌고 있는 코루틴이 있다면, 정지시킨 후 코루틴을 실행한다.
if(runningCoroutine != null)
{
    StopCoroutine(runningCoroutine);
}
runningCoroutine = StartCoroutine(ScaleUp()); //코루틴을 시작하며, 동시에 저장한다.

--------------------------------------------------------------------------------
IEnumerator 타입 변수 사용
void Enter()
{
	만약 stopcoroutine 할 때 작동이 안된다면,
    StopCoroutine(nameof(SomeCoroutine));
    
    IEnumerator CoTemp = null;
    CoTemp = SomeCoroutine();
    StartCoroutine(CoTemp);
    
    if(CoTemp != null)
      StopCoroutine(CoTemp);
}

출처 : https://killu.tistory.com/19

 

[Unity] 코루틴 중단하는 모든 방법 정리 (StopCoroutine)

유니티에서 코루틴을 중단할 때 쓰이는 StopCoroutine은 제대로 작동하지 않는 경우가 많다. 그 이유는 StopCoroutine 메서드를 잘못 사용했기 때문이다. StopCoroutine의 인자는 string, IEnumerator, Coroutine..

killu.tistory.com

<밑에는 좀 더 자세한 설명들>


IEnumerable - 스스로 상태(객체접근 횟수)를 저장하지 못해서

타입의 변수를 인풋으로 받았을 때, 다시 처음부터 콜렉션에 접근해서 객체에 접근한다.

 

IEnumerator - 스스로 상태(객체접근 횟수)를 저장함으로

스스로의 상태 state를 기억하고 있다가 다음 호출시, 그 시첨으로 부터

해당 객체를 뱉어낸다?

출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kimsung4752&logNo=220959490816

 

public interface IEnumerator
{
    object Current { get; }
    bool MoveNext();
    void Reset();
}

즉, IEnumerator는

  • 지금 몇번쨰 까지 읽었는지(state)를 기억한다.
  • MoveNext()호출 하면 다음 순번으로 이동.
  • Current를 요구할 때 해당 순번의 개체를 리턴.

그러면 이런 구조로 볼 수 있겠다. IEnumerable에서 GetEnumerator()호출하면 -> IEnumerator는 Current(), MoveNext(), Reset()등 현재 객체 접근하는 구조. Enuerable 인터페이스에는 foreach 구문에서 필요한 멤버들을 약속한 IEnumerator 개체를 반환하는 GetEnumerator 메서드를 제공하고 있다. // 아직은 뭔소린지 도통 모르겠다..

 

public static System.Collections.IEnumerable SomeNumbers()
{
  yield return 3;
  yield return 5;
  yield return 8;
}
  • IEnumerable, IEnumerator클래스는 컴파일러가 코드를 보고 자동으로 만들어준다.(사용자 정의 컬렉션)
  • IEnumrator가 가지는 state의 초기 값은 -1이다.
  • 자동 생성된 IEnumerator에서 MoveNext() 실행하면 yield return 3; 이전까지 실행하고 state를 0으로 옮긴다.
  • state가 0일 때 Current를 읽으면 3를 리턴한다.
  • 마지막 부분(8)에서 MoveNext()실행 시 false를 리턴한다.
yield return new WaitForSeconds(1);

Current()에서 WaitForSeconds를 리턴 했다면??

  • Update()에서 1초가 지났는지 매프레임 마다 체크.
  • 만약 1초가 지났다면 IEnumerator의 MoveNext()를 호출 한다.
  • 코드 다음 부분이 실행된다.

IEnumerable -> IEnumerator -> 반복기

 

출처 : https://postpiglet.netlify.app/posts/Coroutine-Considerationn/  

출처 : https://www.slideshare.net/jungsoopark104/ienumerator

 

IEnumerator란 무엇인가?

유니티3D를 공부하면서 IEnumerator가 무엇인지 공부한 내용입니다.

www.slideshare.net

 

 

코루틴(Coroutine)

 

기능코루틴  시작코루틴  정지코루틴  재시작기타  
이름을 이용 가능 가능 가능[처음부터 재시작] 매개변수 사용 불가능
함수를 이용 가능 불가능 불가능[정지가 불가능]  
IEnumerator를 이용 가능 가능 가능[멈춘 지점에서 재시작]  
Coroutine을 이용 - 가능 불가능[시작이 불가능]  

yield를 통해 IEnumerable, IEnumerator 를 상속받는 객체를 간단하게 구현할 수 있는 것이나 마찬가지다. 마치 일시정지와 같은 기능이다.

    • yield break; 코루틴을 끝낸다.
    • yield return new WaitForSecondsRealtime (float time); WaitForSeconds와 하는 역할은 동일하지만 결정적으로 다른것은, Scaled Time의 영향을 받지 않고 현실 시간 기준으로만 동작
    • yield return new WaitForFixedUpdate (); 다음 FixedUpdate() 가 실행될때까지 기다리게 됩니다. 이 FixedUpdate()는 Update()와 달리 일정한 시간 단위로 호출되는 Update() 함수라고 생각하시면 됩니다.
    • yield return new WaitForEndOfFrame (); 하나의 프레임워 완전yield return null;히 종료될 때 호출이 됩니다. Update(), LateUpdate() 이벤트가 모두 실행되고 화면에 렌더링이 끝난 이후에 호출
    • yield return null; 다음 Update() 가 실행될때까지 기다린다는 의미를 갖게 됩니다. 좀 더 정확하게는 Update()가 먼저 실행되고 null을 양보 반환했던 코루틴이 이어서 진행 됩니다. 그 다음에 LateUpdate()가 호출
    • yield return new WaitUntil (System.Func predicate); WaitUntil에 실행하고자 하는 식을 정의해 두면 매번 Update() 와 LateUpdate() 이벤트 사이에 호출해 보고 결과값이 true면 이후로 재진행
    • yield return new WaitWhile(System.Func predicate); WaitWhile은 WaitUntil과 동일한 목적을 가지고 있지만 한가지만 다릅니다. WaitUntil은 람다식 실행 결과값이 true가 될때까지 기다린다면 WaitWhile은 false가 될때까지 기다립니다. 즉 WaitWhile은 결과가 true인 동안 계속 기다린다.
    • yield return StartCoroutine (IEnumerator coroutine); 코루틴 내부에서 또다른 코루틴을 호출할 수 있습니다. 물론 그 코루틴이 완료될 때까지 기다리게 됩니다. 의존성 있는 여러작업을 수행하는데에 유리하게 사용.

yield return StartCoroutine (IEnumerator coroutine); 코루틴 내부에서 또다른 코루틴을 호출할 수 있습니다. 물론 그 코루틴이 완료될 때까지 기다리게 됩니다