본문 바로가기
유니티3D[Unity3D]

[Unity3D] Async-Await를 이용한 비동기 프로그래밍

by 은유지니 2023. 2. 14.

예제코드 링크 : https://github.com/Eunji-new/async_TestCode_unity

 

GitHub - Eunji-new/async_TestCode_unity: Unity에서 async-await를 이용한 비동기 프로그래밍 예제

Unity에서 async-await를 이용한 비동기 프로그래밍 예제. Contribute to Eunji-new/async_TestCode_unity development by creating an account on GitHub.

github.com

참조 링크(참조 내용을 바탕으로 코드를 제작하였습니다.) : https://kangworld.tistory.com/25

 

[C#] async await 예제 코드 #2 (+ 동기 비동기의 개념)

async await 두 번째 편이자 마지막 편! 빵! 끗! 인트로 이틀간 밤을 새우며 stackoverflow와 저명한 C# 개발자의 개인 홈페이지에서 글을 읽으며 async await 개념을 정리했다. 아직도 궁금한 부분이 많고

kangworld.tistory.com


4개 오브젝트의 애니메이션이 동기/비동기적으로 작동되게하는 프로그램이다.

다음과 같이 테스트할 수 있도록 구성하였다.

  • 1번키 입력 : 코루틴 이용
  • 2번키 입력 : Async-Await 사용(하지만 사실상 동기 프로그래밍)
  • 3번키 입력 : Async-Await 사용 비동기 프로그래밍
private async void Update()
{
    if (Input.GetKeyDown(KeyCode.Alpha1))
    {
        MovingItems_Coroutine();
        Debug.Log("MovingItems_Couroutine가 시작된 후(MovingItems_Couroutine는 진행중이다)");
    }
    else if (Input.GetKeyDown(KeyCode.Alpha2))
    {
        await MovingItems_Async_v1();
        Debug.Log("MovingItems_Async_v1 끝난 후에 발생하는 로그(사실상 비동기)");
    }
    else if (Input.GetKeyDown(KeyCode.Alpha3))
    {
        await MovingItems_Async_v2();
        Debug.Log("MovingItems_Async_v2 끝난 후에 발생하는 로그");
    }
}

3개의 키를 누를 때 실행되는 함수 내부코드는 다음과 같다.

 /*
코루틴을 이용
각 아이템에 맞는 오브젝트 애니메이션 함수를 실행한다.
함수가 끝나는 것을 기다리지않고, 코루틴의 yield문 마다 다음구문이 진행될 수 있도록 넘겨준다.
*/
public void MovingItems_Coroutine()
{
    foreach (var item in currentItems)
    {
        StartCoroutine(PlayAnim_Coroutine(item));
    }
}

/*
async-await 이용(사실상 동기 프로그래밍)
각 아이템에 맞는 오브젝트 애니메이션 함수를 실행하고
실행한 함수가 끝날때까지 기다린다.
*/
public async Task MovingItems_Async_v1()
{
    foreach (var item in currentItems)
    {
        if (item.name.Contains("Cube"))
            await MoveCube();
        else if (item.name.Contains("Sphere"))
            await MoveSphere();
        else if (item.name.Contains("Cylinder"))
            await MoveCylinder();
        else
            await MoveCapsule();

        Debug.Log($"{item.name} 애니메이션 끝남 ");
    }
}

/*
async-await 이용한 비동기 프로그래밍
각 아이템에 맞는 오브젝트 애니메이션 함수를 실행하고
실행한 함수가 종료될 때, 해당 함수가 끝났다는 로그를 띄운다.
함수가 끝나는 것을 기다리지않고, 다른 함수들도 같이 실행된다.
*/
public async Task MovingItems_Async_v2()
{
    var t0 = MoveCube();
    var t1 = MoveSphere();
    var t2 = MoveCylinder();
    var t3 = MoveCapsule();

    List<Task> taskList = new List<Task> { t0, t1, t2, t3 };

    while (taskList.Count > 0)
    {
    	//먼저 끝난 Task를 받아서 처리한다.
        Task finishedTask = await Task.WhenAny(taskList);
		
        if (finishedTask == t0)
        {
            Debug.Log("cube 애니메이션 끝남");
        }
        else if (finishedTask == t1)
        {
            Debug.Log("Sphere 애니메이션 끝남");
        }
        else if (finishedTask == t2)
        {
            Debug.Log("Cylinder 애니메이션 끝남");
        }
        else if (finishedTask == t3)
        {
            Debug.Log("Capsule 애니메이션 끝남");
        }

        taskList.Remove(finishedTask);
    }
}

애니메이션을 실질적으로 Play하는 함수는 다음과 같다.

각 오브젝트 애니메이션 실행 함수에 시간 초를 두어서 차이를 더 쉽게 알도록 구성해보았다.

public IEnumerator PlayAnim_Coroutine(GameObject obj)
{
    GetItemAnim(obj).Play();

    int seconds = 0;
    if (obj.name.Contains("Cube"))
        seconds = 1;
    else if (obj.name.Contains("Sphere"))
        seconds = 2;
    else if (obj.name.Contains("Cylinder"))
        seconds = 3;
    else
        seconds = 4;

    yield return new WaitForSeconds(seconds);

    if (obj.name.Contains("Cube"))
        Debug.Log("cube 애니메이션 끝남");
    else if (obj.name.Contains("Sphere"))
        Debug.Log("sphere 애니메이션 끝남");
    else if (obj.name.Contains("Cylinder"))
        Debug.Log("cylinder 애니메이션 끝남");
    else
        Debug.Log("capsule 애니메이션 끝남");

}

public Animation GetItemAnim(GameObject obj)
{
    return obj.GetComponent<Animation>();
}

#region Move Animation
public async Task MoveCube()
{
    if (!currentItems[0]) return;

    GetItemAnim(currentItems[0]).Play();
    await Task.Delay(1000);
}

public async Task MoveSphere()
{
    if (!currentItems[1]) return;

    GetItemAnim(currentItems[1]).Play();
    await Task.Delay(2000);
}

public async Task MoveCylinder()
{
    if (!currentItems[2]) return;

    GetItemAnim(currentItems[2]).Play();
    await Task.Delay(3000);
}

public async Task MoveCapsule()
{
    if (!currentItems[3]) return;

    GetItemAnim(currentItems[3]).Play();
    await Task.Delay(4000);
}
#endregion

1번 결과 : 코루틴 이용

2번 결과 : async 동기 프로그래밍

3번 결과 : async이용 비동기 프로그래밍

 


결과를 보다시피 코루틴이든 async이든 프로그래밍 결과는 같다.

(async 비동기 프로그래밍은 async 함수가 끝날때까지 await 또 기다리게 되어서 굳이 따지자면 코루틴이 더 비동기스럽다. - await도 안쓰면 똑같겠지만,,,)

유니티에는 이미 코루틴이라는 유용한기능이 있기때문에 Async를 사용하고 쓰레드를 하나 추가해면서까지 비동기를 구현하는 것은 여러모로 비추천이라고 한다.

하지만 코루틴에서 무한 루틴을 만들거나 서버에서 무수한 데이터를 받아오는 등 시간이 오래걸리는 작업에 사용하기에 유용하다. 여러모로 잘 참고해서 프로그램에 맞게 잘 사용하는 것이 좋겠다.

* 부족하거나 잘못된 내용이 있다면 댓글로 남겨주세요!