GameDevelopment/[Unity] Library
[Unity] UniTask ( Coroutine 대체 )
주녘
2023. 9. 5. 17:40
0. MonoBehaviour LifeCycle
https://godgjwnsgur7.tistory.com/93
[Unity] MonoBehaviour 생명주기 (Life Cycle)
1. Initialization, Editor - Awake() : 프리팹 인스턴스화 직후에 호출 (스크립트 활성화) - OnEnalbe() : 오브젝트가 활성화된 직후에 호출 - Reset() : Reset 명령 혹은 스크립트 - Start() : 활성화된 후 첫 프레임
godgjwnsgur7.tistory.com
1. Coroutine
UnityEngine에서 지원하는 비선점형 멀티테스킹 시스템 (싱글 쓰레드)
- 객체 생성 시 힙 메모리 영역에 할당됨 (가비지 발생)
- 마치 일시정지를 하듯, 원하는 시점에서 함수의 상태를 저장/복원 가능
-> 원하는 시점에 병렬처리처럼 직렬처리할 수 있음
using System.Collections;
using UnityEngine;
public class TestClass : MonoBehaviour
{
private IEnumerator ITestCoroutine(float delayTime)
{
float currTime = 0.0f;
while (currTime < delayTime)
{
currTime += Time.deltaTime;
// Update 주기 종료 후 호출
yield return null;
}
// 지정한 시간 이후 & Update 주기 이후 호출
yield return new WaitForSeconds(delayTime);
// Time.timeScale의 영향을 받지 않는 절대적인 시간 이후 호출
yield return new WaitForSecondsRealtime(delayTime);
// 지정한 함수가 True를 리턴할 때까지 대기 (Update 주기 종료된 후 조건 체크)
yield return new WaitUntil(() => delayTime < currTime);
// FixedUpdate 주기가 종료된 후 호출
yield return new WaitForFixedUpdate();
// 프레임이 종료된 후 호출 (OnGUI 종료 후)
yield return new WaitForEndOfFrame();
...
// 코루틴 중단
yield break;
}
}
2. UniTask
- Coroutine을 대체할 수 있으며, return 값을 가질 수 있음 ( UniTask<T> )
- WhenAll, WhenAny 등의 내장 메서드 제공
- Zero Allocation 기능으로 성능 향상
-> 구조체 기반이기 때문에 스택에 할당되어 GC의 부담을 줄일 수 있음
-> 내부적으로 "Task Pool"을 미리 생성한 다음 자주 사용되는 비동기 작업 저장하고 재사용
- CancellationTokenSource 클래스를 통한 취소 토큰 활용 가능
- 호출 시점 제어 가능 - 기본 값 : Update 주기 ( 매개변수로 enum PlayerLoopTiming 전달 가능 )
1) Coroutine <-> UniTask
Coroutine | UniTask |
yield return null | await UniTask.Yield |
yield return new WaitForSeconds yield return new WaitForSecondsRealtime |
await UniTask.Delay |
yield return WaitUntil | await UniTask.WaitUntil |
yield new WaitForFixedUpdate |
await UniTask.WaitForFixedUpdate |
yield return WaitForEndOfFrame |
await UniTask.WaitForEndOfFrame |
using Cysharp.Threading.Tasks;
using UnityEngine;
public class TestClass : MonoBehaviour
{
private CancellationTokenSource cts;
private void OnEnable()
{
cts = new CancellationTokenSource(); // 중단조건 토큰
AsyncTestUniTaskVoid().Forget(); // UniTask 메서드 실행
}
protected virtual void OnDisable()
{
cts?.Cancel(); // 토큰을 통해 메서드 종료
cts?.Dispose(); // 사용하는 모든 리소스 해제
}
// UniTaskVoid
private async UniTaskVoid AsyncTestUniTaskVoid(float delayTime)
{
float currTime = 0.0f;
while (currTime < delayTime)
{
currTime += Time.deltaTime;
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken: cts.Token);
}
await UniTask.Delay(TimeSpan.FromSeconds(delayTime),
ignoreTimeScale: false, PlayerLoopTiming.Update, cancellationToken: cts.Token);
await UniTask.WaitUntil(() => delayTime < currTime,
PlayerLoopTiming.Update, cancellationToken: cts.Token);
// Touple 값 받기
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
await UniTask.WaitForFixedUpdate(cancellationToken: cts.Token);
await UniTask.WaitForEndOfFrame(cancellationToken: cts.Token);
...
// UniTask 중단 조건 : 오브젝트 파괴 (취소 토큰)
var _token = this.GetCancellationTokenOnDestroy();
await UniTask.Delay(1000, cancellationToken: _token);
}
// UniTask<T>
private async UniTask<int> AsyncTestUniTask()
{
int value = 0;
try
{
while(true)
{
...
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken: cts.Token);
}
}
catch (Exception e)
{
Debug.LogError(e);
return -1;
}
return value;
}
}
namespace Cysharp.Threading.Tasks
{
// 모노비헤이비어 생명주기 기준 타이밍 열거형
public enum PlayerLoopTiming
{
Initialization,
LastInitialization,
EarlyUpdate,
LastEarlyUpdate,
FixedUpdate,
LastFixedUpdate,
PreUpdate,
LastPreUpdate,
Update,
LastUpdate,
PreLateUpdate,
LastPreLateUpdate,
PostLateUpdate,
LastPostLateUpdate,
TimeUpdate,
LastTimeUpdate
}
}