2022. 8. 10. 10:56ㆍ유니티 unity
https://github.com/Cysharp/UniTask
UniTask는 기존 C#Task는 무겁고 Unity(단일 스레드)와 일치하지 않기 때문에 유니티에게 최적화해서 사용할 수 있게 만들었습니다.
기존 코루틴의 단점은 리턴 값이 없어서 따로 콜백 처리를 해줘야 하고 try-catch 예외처리를 못하고
StartCoroutine와 YieldInstruction에서 가비지가 주로 생성되는 단점이 있습니다.
코루틴을 대체할 수 있는 게 바로 UniTask입니다.
기본 사용법은
코루틴
void Start()
{
StartCoroutine(HelloCoroutine());
}
IEnumerator HelloCoroutine()
{
Debug.Log("안");
yield return new WaitForSeconds(1);
Debug.Log("녕");
}
/////
UniTask
void Start()
{
HelloTask();
}
async UniTask HelloTask()
{
Debug.Log("안");
//await UniTask.Delay(1000); //1초
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log("녕");
}
////
이렇게도 사용가능
async UniTaskVoid Start()
{
Debug.Log("안");
//await UniTask.Delay(1000); //1초
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log("녕");
}
주의할점은
코루틴은 오브젝트가 삭제되거나 비활성화가 되면 자동으로 중지가 되는데
UniTask는 따로 취소처리를 해줘야 합니다.
UniTask Tracker를 보시면 확인이 가능합니다
비활성화했는데도 비동기 함수가 안 끝남
CancellationTokenSource disableCancellation = new CancellationTokenSource(); //비활성화시 취소처리
CancellationTokenSource destroyCancellation = new CancellationTokenSource(); //삭제시 취소처리
private void OnEnable()
{
if (disableCancellation != null)
{
disableCancellation.Dispose();
}
disableCancellation = new CancellationTokenSource();
}
private void OnDisable()
{
disableCancellation.Cancel();
}
private void OnDestroy()
{
destroyCancellation.Cancel();
destroyCancellation.Dispose();
}
async UniTaskVoid Start()
{
Debug.Log("안");
//await UniTask.Delay(1000,false,PlayerLoopTiming.Update,disableCancellation.Token);
await UniTask.Delay(TimeSpan.FromSeconds(1),false,PlayerLoopTiming.Update,disableCancellation.Token);
Debug.Log("녕");
}
이렇게 처리하면
비활성화 시 비동기 함수가 끝남
또 다른 예시 버튼 클릭 시 취소함
var cts = new CancellationTokenSource();
cancelButton.onClick.AddListener(() => //버튼클릭시 취소처리
{
cts.Cancel();
});
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
코루틴에서 사용되는 것들은 UniTask로 전부 대체 가능합니다.
//yield return null
await UniTask.Yield();
await UniTask.NextFrame();
///////
//yield return WaitUntil
await UniTask.WaitUntil(() => isActive == false);
// 또다른 사용방법
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
////
// yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
또 시간 초과처리도 가능합니다.
var cts = new CancellationTokenSource();
cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5초 시간초과
try
{
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(cts.Token);
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cts.Token)
{
Debug.Log("Timeout");
}
}
다른 취소처리 와 함께 사용을 하려면
var cancelToken = new CancellationTokenSource();
cancelButton.onClick.AddListener(()=>
{
cancelToken.Cancel(); // 버튼 클릭시 취소
});
var timeoutToken = new CancellationTokenSource();
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5초경과시 취소
try
{
// combine token
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token);
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
}
catch (OperationCanceledException ex)
{
if (timeoutToken.IsCancellationRequested)
{
Debug.Log("Timeout.");
}
else if (cancelToken.IsCancellationRequested)
{
Debug.Log("Cancel clicked.");
}
}
UniTask로 3번 클릭 구현
async UniTask TripleClick()
{
// 기본적으로 button.GetCancellationTokenOnDestroy를 사용하여 비동기의 수명을 관리합니다.
await button.OnClickAsync();
await button.OnClickAsync();
await button.OnClickAsync();
Debug.Log("3번 클릭");
}
Unitask로 리턴 값을 받고 싶으면
async UniTask Go()
{
string s = await GetTextAsync(변수넣기);
Debug.Log(s);
}
async UniTask<string> GetTextAsync(UnityWebRequest req)
{
try
{
var op = await req.SendWebRequest();
return op.downloadHandler.text;
}
catch(Exception e)
{
Debug.LogError(e);
return "";
}
}
async UniTask Go()
{
int i = await GetIntAsync();
Debug.Log(i); // 1초뒤에 1이 나옴
}
async UniTask<int> GetIntAsync()
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
return 1;
}
동시처리를 하려면
public async UniTaskVoid LoadManyAsync()
{
//전부 완료되면 다음으로 넘어감
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("foo"),
LoadAsSprite("bar"),
LoadAsSprite("baz"));
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
UniTask는 TemtMeshPro, DOTween, Addressables를 지원합니다.
// 순서대로 처리
await transform.DOMoveX(2, 10);
await transform.DOMoveZ(5, 20);
// 취소처리와 동시처리
var ct = this.GetCancellationTokenOnDestroy();
await UniTask.WhenAll(
transform.DOMoveX(10, 3).WithCancellation(ct),
transform.DOScale(10, 3).WithCancellation(ct));
코루틴을 사용하다 보면 여러 단점들이 있는데
예외처리가 불가능함
리턴 값이 따로 없음
사용 시 가비지 생성 (YieldInstruction는 캐싱해서 사용하면 되는데 StartCoroutine는 유니티 내부 코드라서 따로 최적화 불가능)
코루틴을 사용하게 되면 선형적인 코드 작성 방식에서 벗어나기 때문에 빠른 코드 해석이 어려워짐
오브젝트가 꺼지면 자동으로 코루틴이 꺼지는 부분
이러한 단점을 커버할 수 있는 게 바로 UniTask이라고 생각합니다.
UniTask는 충분히 코루틴을 대체할 수 있다고 생각합니다.
'유니티 unity' 카테고리의 다른 글
Unity / 베지어곡선이용해서 라인렌더로 곡선 그리기 (0) | 2022.08.16 |
---|---|
유니티(Unity) Quaternion 전부 알기 (0) | 2022.08.15 |
unity(유니티) vector 함수 전부 알기 (0) | 2022.08.03 |
Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 붐버맨 만들기)-4 (0) | 2022.07.30 |
유니티 Addressable (사용법,서버에서 받기) (0) | 2022.07.26 |