Unitask

강동현·2024년 8월 11일
0

Unity/C#

목록 보기
25/26

UniTask란?

  • 비동기 프로그래밍 툴
  • 코루틴과 유사

코루틴과 비교

Unitask(=ASync/Await)

  • 코루틴의 기능을 대체
  • 메모리 누수를 방지하기 위한 Task Tracker 윈도우 지원
    장점1: 제로할당: 힙 할당이 발생하지 않음
    장점2: 서버로 받은 값을 리턴 가능
    단점: 라이브러리 임포트 필요(프로젝트의 빵이 커짐)

Coroutine

  • 유니태스크의 장단점과 반대

설치 방법

  • git UPM주소 복사
  • Unity Package 파일을 직접 적용

임포트 구문

using 

UnitTask VS Coroutine 비교 설명

N초 지연

//Coroutine
private IEnumerator Wait1Second(){
	yield return new WaitForSeconds(1f);
    Debug.Log("1 Seconds Passed");
}

//UniTask
//UniTaskVoid: 서버에서 텍스처를 받은 경우 리턴되는 값을 지정하지만, 이 경우 단순 테스트용이기 때문에 Void로 선언
//TimeSpan.~: (일/시간/초/밀리초/틱...) 기준 설정
private async UniTaskVoid Wait1SecondAsync(){
	//await: ~를 기다리겠다.
    //ms 단위
	await UnitTask.Delay(1000);
    await UnitTask.Delay(TimeSpan.FromSeconds(1f));
    Debug.Log("1 Seconds Passed");
}

실행 방법

private void Start(){
	//Coroutine 
	StartCoroutine(Wait1Second());

	//UniTask
    //Forget(): void를 사용할 경우에만 붙여서 선언
    //void를 사용하지 않을 경우, 단순히 함수를 실행하듯 사용 가능
	Wait1SecondAsync().Forget();
    //Wait1SecondAsync();
}

TimeScale에 영향을 받지 않는 방법

  • TimeScale에 영향 받지 않도록 설정
//Coroutine
private IEnumerator WaitForSecond(){
	yield return new WaitForSecondsRealtime(1f);
}

//UniTask
private async UniTaskVoid Wait1SecondAsync(){
	await UniTask.Delay(TimeSpan.FromSeconds(1f), DelayType.UnscaledDeltaTime);
}

private void Start(){
	Time.timeScale = 0;
    StartCoroutine(Wait1Second());
    Wait1SecondAsync().Forget();
}

특정 조건 설정

[SerializedField]
private int _count;
//Coroutine
private IEnumerator Wait3Count(){
	yield return new WaitUntil(() => _count == 3);
    Debug.Log("Count is 3");
}

//UniTask
private async UniTaskVoid Wait3CountAsync(){
	await UniTask.WaitUntil(() => _count == 3);
	Debug.Log("Count is 3");
}

private void Start(){
	//StartCoroutine(Wait3Count());
	Wait3CountAsync().Forget();
}

이미지 가져오기

//Coroutine
private const string AppleImagePath = "https://...";
public RawImage profileImage;

private IEnumerator WaitGetWebTexture(UnityAction<Texture> action)
{
	//리턴 값이 이미 존재하는데, 다른 값을 반환하는 방법
	//yield return new Texture2D(100, 100);
    UnityWebRequest request = UunityWebRequestTexture.GetTexture(AppleImagePath);
    yield return request.SendWebRequest();
    
    if(request.result is UnityWebRequest.Result.ConnectionError or UnityWebRequest.Result.ProtocolError)
    {
    	//에러 처리
        Debug.LogError(request.error);
    }
    else
    {
    	//성공 처리
        Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
        action.Invoke(texture);
    }
}

private void Start()
{
	StartCoroutine(WaitGetWebTexture(texture => {
    	protileImage.texture = texture;
    }));
}
//UniTask
private async UniTask<Texture2D> WaitGetWebTextureAsync()
{
	UnityWebRequest request = UnityWebRequestTexture.GetTexture(AppleImagePath);
    await requst.SendWebRequest();
    
    if(request.result is UnityWebRequest.Result.ConnectionError or UnityWebRequest.Result.ProtocolError)
    {
    	//에러 처리
        Debug.LogError(request.error);
    }
    else
    {
    	//성공 처리
        Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
        //[차이점!]
        return texture;
    }
    return null;
}

private async UniTaskVoid GetImageAsync()
{
	//[★]await을 통해 얻어옴
	Texture2D texture = await WaitGetWebTextureAsync();
    profileImage.texture = texture;
}

private void Start()
{
	//다시 void 타입 UniTask를 실행
	GetImageAsync().Forget();
}

종료 및 일시정지

코루틴

  • 기능이 너무 많아 카테고리별로 분류함
//캐싱 필요
private Coroutine _coroutine;

private void Start()
{
	_coroutine = StartCoroutine(Wait3Second());
}

private void Update()
{
	if(Input.GetKeyDown(KeyCode.Space))
    {
    	//Coroutine
    	StopCoroutine(_coroutine);
    	//또는 StopAllCoroutine();
    }
}

private IEnumerator Wait3Second()
{
	yield return new WaitForSeconds(3f);
    Debug.Log("3 seconds passed");
}

UniTask

  • CancellationTokenSource(): Cancel 타이밍을 원할 때 처리 가능, 직접적인 Dispose 호출 필요
  • source의 메모리자동으로 해제되지 않음
    - Dispose를 통해 수동으로 메모리 해제 필요
//UniTask

private CancellationTokenSource _source = new();

private void Start()
{
	Wait3Second().Forget();
}

private void Update()
{
	if(Input.GetKeyDown(KeyCode.Space))
	{
		_source.Cancel();
	}

}

private UniTaskVoid Wait3SecondAsync()
{
	await UniTask.Delay(TimeSpan.FromSeconds(3f), cancellationToken: _source.Token);
    Debug.Log("3 seconds passed");
}

private void OnDestroy()
{
	_source.Cancel();
    _source.Dispose();
}

private void OnEnable()
{
	if(+source != null)
    {
    	_source.Dispose();
    }
    _source = new();
}

private void OnDisable()
{
	_source.Cancel();
}

- TokenSource 메모리 수동 관리는 귀찮...

  • 이를 자동으로 만드는 방식도 개발해두었다.
  • this.GetCancellationTokenOnDestroy(): Destroy될 때 Cancel 및 Dispose 수행
  • [★]단 오브젝트가 비활성 된 경우에는, 계속 메모리에 할당됨
private async UniTaskVoid Wait3Second()
{
	await UniTask.Delay(TimeSpan.FromSeconds(3f), cancellationToken: this.GetCancellationTokenOnDestroy());
    Debug.Log("3 seconds passed");
}

Task Tracker[★]

  • 코루틴에 없던 기능이라 매우 중요
  • 매우 편함
  • Unity -> Window -> UniTask Tracker -> 진행중인 모든 Task 확인 가능
    - 매우 강력 및 편함
private void Start()
{
	Wait3Second().Forget();
}

private async UniTaskVoid Wait3Second()
{
	await UniTask.Delay(TimeSpan.FromSeconds(3f), cancellationToken : this.GetCancellationTokenOnDestroy());
    Debug.Log("3 seconds passed");
}

DOTween 호완

  • Edit -> ProjectSettings -> Player -> Other Settings -> Script Define Synbols -> + -> 공식 문서 내용(UNITASK_DOTWEEN_SUPPORT)를 넣기 -> Apply 버튼

간단한 UniTask + DOTween 혼용

private void Start()
{
	WaitMove().Forget();
}
private async UniTaskVoid WaitMove()
{
	await.transform.DOMove(new Vector3(0, 3, 3), 3f);
    Debug.Log("XYZ(0,3,0) 위치로 이동 완료");
}
profile
GAME DESIGN & CLIENT PROGRAMMING

0개의 댓글