UniTask 는 async 와 await 를 유니티에서 더 사용하기 편리하도록 만든 통합 패키지다. UniTask 는 Coroutine 에 비해 메모리 사용량, 성능, try-catch 사용 가능 및 return 을 사용할 수 있어서 Coroutine 대신 UniTask 를 이용하도록 권장하고 있는 상황이다.
설치
UniTask 는 기본으로 지원되는 기능이 아니기 때문에 Package Manager 를 이용해 설치해줘야 한다.
Package Manager 를 실행하고 위와 같이 [Add package from git URL...] 을 선택하고 나타나는 주소입력 창에 아래의 주소를 입력하고 [Add] 버튼을 클릭한다.
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
예제
코루틴과 UniTask 를 비교하기 위해 다음과 같은 프로그램을 만든다
● 마우스 왼쪽 버튼을 클릭하면 0.5 초마다 sphere 생성
● 오른쪽 버튼을 클릭하면 종료
● 종료 후 생성된 sphere 수를 생성자 id 와 함께 출력
async void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Input.GetKey(KeyCode.LeftControl))
await SphereGenByUniTask();
else
SphereGenByCoroutine();
}
else if (Input.GetMouseButtonDown(1))
{
if (Input.GetKey(KeyCode.LeftControl))
{
cts.Cancel();
cts.Dispose();
}
else
stopGen = true;
}
}
유니티 스크립트를 생성하면 자동으로 생성되는 Update 함수 앞에 async keyword 가 있다. async 가 있으면 Update 함수가 비동기 함수로 동작해서 await 키워드를 사용할 수 있게 된다.
코루틴 이용
void SphereGenByCoroutine()
{
stopGen = false;
StartCoroutine(SC_SphereGen(++coroutine_spawn_id, (r) => {
print("SpawnerID: " + r.id.ToString() + " / Spawn Count: " + r.count.ToString());
}));
print("SphereGenByCoroutine Done");
}
코루틴을 이용하면 코루틴의 결과는 System.Action 과 Lambda Expression (람다식) 을 이용해 처리해야 한다.
코드의 실행 순서는
- stopGen
- StartCoroutin(...)
- print("SphereGenByCoroutine done")
- 함수 종료
- stopGen이 true가 된 후에 람다식 -> print("SpawnerID: " ...);
IEnumerator SC_SphereGen(int id, System.Action<SpawnInfo> done)
{
int spawned = 0;
float time = 0f;
while(true)
{
Vector3 p = UnityEngine.Random.insideUnitSphere * 5;
Vector3 pos = new Vector3(p.x, p.y, p.z);
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
spawned++;
sphere.transform.position = pos;
time = 0f;
while (time < 0.5f && stopGen == false)
{
time += Time.deltaTime;
yield return null;
}
if (stopGen == true) break;
}
done.Invoke(new SpawnInfo() { id = id, count = spawned });
}
stopGen 이 false 인 동안에는 0.5 초마다 sphere 를 생성하는 코루틴 함수다. 여기서 0.5 초를 기다리는 코드를 yield return new WaitForSeconds(0.5f) 를 사용하지 않은 이유는 이 코드를 사용하면 중간에 취소시킬 방법이 없어 무조건 0.5초를 기다려야하기 때문이다.
코루틴 함수가 종료될 때 인자로 받은 done 을 이용해 결과를 리턴한다.
Unitask 이용
async UniTask SphereGenByUniTask()
{
if (cts.IsCancellationRequested)
cts = new CancellationTokenSource();
var r = await SpawnSphere(++unitask_spawn_id);
print("SpawnerID: " + r.id.ToString() + " / Spawn Count: " + r.count.ToString());
print("SphereGenByUniTask Done");
}
UniTask 를 이용해 코루틴 함수를 대체하는 코드다. 코루틴과 다르게 await SpawnSphere() 함수가 종료될 때까지 코드가 다음으로 진행되지 않는다. 코드는 처음부터 순차적으로 진행된다.
async UniTask<SpawnInfo> SpawnSphere(int id)
{
int spawned = 0;
try
{
while(true)
{
Vector3 p = UnityEngine.Random.insideUnitSphere * 5;
Vector3 pos = new Vector3(p.x, p.y, p.z);
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
spawned++;
sphere.transform.position = pos;
await UniTask.Delay(500, cancellationToken: cts.Token);
if (cts.IsCancellationRequested) break;
}
}
catch (Exception e) { }
return new SpawnInfo() { id = id, count = spawned };
}
코루틴 함수에서는 0.5 초를 기다리다가 중간에 cancel 하는 것을 구현하기 위해 while 문을 이용했지만 UniTask 는 await UniTask.Dely(...) 에 cancellationToken 을 이용하는 것으로 간단하게 구현할 수 있다. 그리고 코루틴과 다르게 return 도 사용할 수 있다.
아래는 코루틴에서 사용하는 yield return... 을 대체하는 UniTask 함수들이다.
이 외에도 UniTask 는 더 많은 기능을 제공하고 있다.
다만 코루틴은 StartCoroutine 호출 후 바로 다음 코드가 진행되기 때문에 로직에 따라 이 방식이 더 유리한 경우도 있을 수 있다. 메모리와 성능상의 문제가 크게 발생하지 않는다면 자신이 만든 로직에 따라 두 방식을 선택할 수 있는 유연성도 필요할 것이다.
'게임엔진 > Unity' 카테고리의 다른 글
[Unity] Resources.LoadAll로 리소스 파일 전체 불러오기 (0) | 2023.09.15 |
---|---|
[Unity] UniTask VS Task (UniTask 개념 포함) (0) | 2023.09.13 |
[Unity] Mathf (0) | 2023.09.11 |
[Unity] Vector2 (0) | 2023.09.11 |
[Unity] Tranform 컴포넌트 (0) | 2023.09.11 |