기본적으로 코루틴을 실행하는데 yield return으로 코루틴을 기다리는 상황이다.
stopcoroutine으로 해당 코루틴을 중지시켰을 때, yield return을 빠져나오질 못해서 문제가 생긴 상황이였다.
어떤 원리인지 알기전에 기본적으로 IEnumerator의 특성을 보자.
IEnumerator는 C#의 코루틴이나 지연된 실행을 지원하는 인터페이스이다.
yield 키워드를 사용해 코루틴의 실행을 일시 중지하거나 특정 조건이 만족될 때까지 대기할 수 있다.
간단히 말해, IEnumerator는 순차적으로 실행되며 중간에 일시정지된 상태를 유지할 수 있는 함수라고 볼 수 있다.
일시정지는 기본적으로 yield return 키워드를 만나면 적용된다.
yield return 키워드는 대표적으로
yield return null: 다음 프레임에서 다시 이어서 실행.
yield return new WaitForSeconds(time): 주어진 시간이 지난 후 다시 실행.
yield return Coroutine: 다른 코루틴이 완료될 때까지 대기.
등이 있는데, 이번에 내가 사용한건 yield return coroutine; 이다.
yield return 다음에 코루틴을 적어주면 해당 코루틴이 완료될 때까지 기다린다.
또한 IEnumerator는 movenext()라는 함수를 이용해
마지막 반환이 있던 코드의 다음 줄 부터 마지막 반환의 다음 반환까지의 코드를 실행한다고 한다.
movenext()의 리턴값으로는 마지막 yield return기준으로 실행할 코드가 있다면 true,
없다면 false를 반환한다고 한다.
private Coroutine playCoroutine = null;
public override IEnumerator PlayCutscene()
{
playCoroutine = StartCoroutine(PlayScene());
yield return playCoroutine;
}
이런 식으로 함수 내부에 코루틴을 할당했을 때,
private void Update()
{
if (!isActivatedCutscene) return;
//TODO: 컷신중일때만 작동해야 함
if (Input.GetKeyDown(KeyCode.S))
{
for (int i = 0; i < subCoroutines.Length; i++)
{
if (subCoroutines[i] == null) break;
StopCoroutine(subCoroutines[i]);
subCoroutines[i] = null;
}
if (playCoroutine != null)
{
StopCoroutine(playCoroutine);
playCoroutine = null;
}
isSkipped = true;
}
}
update문에서 s키를 누르면 종료되도록 구현했다.
하지만 s키를 누르면 빠져나오지 못하고 PlayCutscene()함수에서 멈춰버린다.
IEnumerator의 movenext()가 false를 반환을 해야 yield return 코루틴 부분을
빠져나가게 된다.
stopcoroutine(playCoroutine);
하지만 위 stopcoroutine함수를 통해 코루틴을 종료시켜버리면
코루틴이 끝이라는 false값을 반환하지 않고 종료되기 때문에
yield return 코루틴 부분에서 계속 대기중이였던 것이다.
따라서 yield return Coroutine 형식으로 사용할 때,
코루틴 실행 도중 stopcoroutine(coroutine);으로 중지시켜도
저 yield return에서 빠져나오지 못한다.
따라서 중간에 코루틴이 끝났는지 확인하기 위해
yield return playCoroutine 보단
playCoroutine = StartCoroutine(PlayScene());
//yield return 으로 기다리면 영원히 멈춰버림
while(playCoroutine != null)
{
yield return null;
}
이렇게 무한루프를 통해 코루틴이 null값인지 간단히 체크해서 빠져나가도록 구현했다.