게임 로고 이미지

[코루틴에 대해서 알아 봅시다.]

이번 시간에는 코루틴의 사용법을 살펴 보겠습니다. 또한 이를 이용하여 딜레이를 주거나, 애니메이션의 상태를 제어하는 방법 등의 코드를 작성 하여 이해 하는데 도움을 드리겠습니다. 마지막으로 장, 단점까지 알아보겠습니다.


[개념]

  • 비동기적인 작업을 수행하고 제어할 수 있는 기능입니다.
    • 비동기 작업은 여러 작업이 동시에 실행되거나, 한 작업이 다른 작업의 완료를 기다리지 않고 독립적으로 실행되는 것을 의미합니다.
  • 때문에 여러 작업을 수행하거나 딜레이를 줘야 할 때 유용하게 사용 됩니다.
  • 일반적으로 함수 내부에서 사용되며, 함수의 실행을 일시 중단하고 나중에 다시 시작할 수 있도록 도와줍니다. 이는 함수의 실행을 일시 중단하고 다른 작업을 처리한 후에 다시 해당 함수의 실행을 재개할 수 있도록 해주는 “yield” 문을 사용하여 구현됩니다.
public class CoroutineExample : MonoBehaviour
{
    private IEnumerator Start()
    {
        while (true)
        {
            // "Coroutine example"를 콘솔에 출력합니다.
            Debug.Log("Coroutine example");

            // 1초 대기합니다.
            yield return new WaitForSeconds(1.0f);
        }
    }
}
  • 예시 코드로 1초마다 메세지를 출력하는 간단한 함수 입니다.
  • yield return new WaitForSeconds(1.0f);는 1초의 대기 시간을 가지는 코드입니다. 이를 통해 각 반복마다 1초씩 대기하게 됩니다.

[언제 사용하는지?, 사용 사례]

  • 딜레이를 줘야하는 작업에 많이 사용 됩니다.
  • 애니메이션 상태 제어에도 사용 됩니다.
    • 애니메이션을 재생하고, 특정 상태로 전이하거나 애니메이션 종료 후 작업을 수행하는 등의 복잡한 상태 전환 로직을 구현할 수 있습니다.
using UnityEngine;

public class CharacterController : MonoBehaviour
{
    public Animator animator;
    private CharacterState currentState;

    private void Start()
    {
        // 초기 상태 설정
        currentState = CharacterState.Idle;

        // 상태 전환 코루틴 시작
        StartCoroutine(StateTransitionCoroutine());
    }

    private IEnumerator StateTransitionCoroutine()
    {
        while (true)
        {
            switch (currentState)
            {
                case CharacterState.Idle:
                    // Idle 상태에서의 애니메이션 재생
                    animator.SetBool("IsIdle", true);
                    animator.SetBool("IsWalking", false);

                    // 일정 시간 대기 후 Walk 상태로 전환
                    yield return new WaitForSeconds(3f);
                    currentState = CharacterState.Walk;
                    break;

                case CharacterState.Walk:
                    // Walk 상태에서의 애니메이션 재생
                    animator.SetBool("IsIdle", false);
                    animator.SetBool("IsWalking", true);

                    // 일정 시간 대기 후 Idle 상태로 전환
                    yield return new WaitForSeconds(3f);
                    currentState = CharacterState.Idle;
                    break;
            }

            // 다음 상태로 전환하기 전에 대기
            yield return null;
        }
    }
}

public enum CharacterState
{
    Idle,
    Walk
}
  • 위 코드 예시와 같이 캐릭터의 상태를 제어하거나 공격 애니메이션의 길이 만큼 코루틴을 이용해 대기를 하는 등 여러 방면에 사용됩니다.
  • 게임 상태 및 진행 제어
    • 오브젝트의 상태 전환, 스테이지 로딩, 게임 종료 등 게임의 전체적인 흐름을 제어에 사용 됩니다.
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameController : MonoBehaviour
{
    public GameObject loadingScreen;
    public GameObject gamePlayScreen;
    public GameObject endScreen;
    
    private void Start()
    {
        // 게임 시작 코루틴 시작
        StartCoroutine(GameFlowCoroutine());
    }

    private IEnumerator GameFlowCoroutine()
    {
        // 게임 시작 후 3초 대기
        yield return new WaitForSeconds(3f);

        // 로딩 화면 표시
        loadingScreen.SetActive(true);

        // 레벨 로딩 - 로딩이 될 때까지 다음으로 넘어가지 않는다.
        yield return SceneManager.LoadSceneAsync("Level1");

        // 로딩 완료 후 게임 플레이 화면 표시
        loadingScreen.SetActive(false);
        gamePlayScreen.SetActive(true);

        // 게임 플레이 중...

        // 게임 종료
        EndGame();
    }

    private void EndGame()
    {
        // 게임 종료 화면 표시
        gamePlayScreen.SetActive(false);
        endScreen.SetActive(true);

        // 게임 오브젝트 비활성화
        gameObject.SetActive(false);

        // 종료 메시지 표시
        Debug.Log("Game Over");
    }
}
  • 위 예시 코드와 같이 게임의 시작부터 끝까지의 흐름을 제어 할 때 사용합니다.
    • 코드를 보시면 yield return을 이용하여 해당 작업이 완료 될 때까지 다음 작업을 수행하지 않도록 흐름을 제어 하고 있습니다.
  • 네트워크 통신에도 사용 됩니다.
    • 코루틴을 사용하면 네트워크 요청을 보내고 응답을 기다리는 동안 다른 작업을 수행할 수 있습니다.
    • 네트워크 처리 중 로딩화면을 띄워서 네트워크의 데이터가 로드 되는 동안 로딩 화면에서 여러 팁들을 보여주거나, 미니 게임을 하는 등 유니티의 단일 쓰레드의 단점을 보완하는 역할을 하는 것 입니다.
  • UI의 반응을 제어 할 때도 많이 사용 됩니다.
    • 버튼 클릭 후 일정 시간 동안 딜레이를 주거나, 페이드 인/아웃 효과를 주는 등의 작업을 구현할 수 있습니다.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class UIController : MonoBehaviour
{
    public Button button;
    public Image fadeImage;

    private void Start()
    {
        // 버튼 클릭 이벤트에 코루틴 연결
        button.onClick.AddListener(OnButtonClick);
    }

    private void OnButtonClick()
    {
        // 버튼 클릭 후 딜레이를 주고 페이드 인/아웃 효과 적용하는 코루틴 시작
        StartCoroutine(ButtonClickCoroutine());
    }

    private IEnumerator ButtonClickCoroutine()
    {
        // 버튼 클릭 후 1초 대기
        yield return new WaitForSeconds(1f);

        // 페이드 아웃 효과
        yield return FadeOut(0.5f);

        // 페이드 인 효과
        yield return FadeIn(0.5f);
    }

    private IEnumerator FadeOut(float duration)
    {
        float elapsedTime = 0f;
        Color startColor = fadeImage.color;
        Color targetColor = new Color(startColor.r, startColor.g, startColor.b, 0f);

        while (elapsedTime < duration)
        {
            elapsedTime += Time.deltaTime;
            float t = Mathf.Clamp01(elapsedTime / duration);
            fadeImage.color = Color.Lerp(startColor, targetColor, t);
            yield return null;
        }
    }

    private IEnumerator FadeIn(float duration)
    {
        float elapsedTime = 0f;
        Color startColor = fadeImage.color;
        Color targetColor = new Color(startColor.r, startColor.g, startColor.b, 1f);

        while (elapsedTime < duration)
        {
            elapsedTime += Time.deltaTime;
            float t = Mathf.Clamp01(elapsedTime / duration);
            fadeImage.color = Color.Lerp(startColor, targetColor, t);
            yield return null;
        }
    }
}
  • 버튼 클릭 후 페이드 인, 아웃을 처리하기 위한 예시 코드입니다. 페이드 인, 아웃 외에도 버튼이 커졌다가 작아지는 등 여러가지 효과를 줄 때 사용하기도 합니다.

[코루틴의 장점과 단점]

[장점]

  • 비동기 작업 처리: 비동기적인 작업을 처리할 수 있도록 도와줍니다.
  • 단일 쓰레드의 장점을 보완 : 게임에서 다른 데이터를 불러올 동안 다른 작업을 하여 유저가 기다리는 행위를 줄여 주는 행위와 같이 유니티의 단점을 보완해 주는 역할을 합니다.

[단점]

  • 성능의 문제를 끼칠 수 있습니다. 동시에 여러 개를 사용 할 수 있다 보니, 동시에 많은 코루틴이 돌면 한 프레임에 수행 할 수 있는 동작이 제한 되게 되며 프레임 저하 등의 문제를 겪을 수 있습니다.
  • 디버깅이 어려워 집니다.
    • 한번에 여러 개를 실행 할 수 있다는 특성으로 인한 문제 입니다.
  • 동기화의 문제
    • 코루틴이 여러 개 실행 되고 각 코루틴이 서로의 상태를 공유 하는 등 엮어있는 코드가 많게 되면 각 코루틴의 상태를 확인 하거나 동기화를 해줘야 하는 등의 문제가 발생합니다.
    • 최대한 서로간 상태 공유를 제한 하거나, 코드의 흐름을 미리 설계하고 작성하시는 게 좋습니다.

[코루틴 사용에 대한 개인적인 견해]

단일 쓰레드의 단점을 보완하는 좋은 기능이기 때문에 자주 사용합니다. 하지만 사용 하지 않아도 되는 곳에 사용 하다 보면 디버깅이 어려워지기에 적게 사용하려고 노력하는 편입니다. 게임을 만들다 보면 버그를 마주할 수 밖에 없는 상황이 여러 번 생기는데 코루틴을 사용한 부분에 문제가 발생하게 되면 문제의 원인을 파악하기가 쉽지 않습니다. 한 프레임에 모든 코드가 실행되는 개념이 아니기 때문입니다. 이러한 이유들로 사용할 수 밖에 없는 부분에만 사용하시는 것을 권장 합니다.

[같이 보면 좋은 글]

[Unity] 디자인 패턴 MVC, MVP, MVVM 비교 및 예시 코드 – GameDev.Log (programmingdev.com)

[참고 사이트]

[유니티] 코루틴의 사용법 총정리 – Unity Coroutine (tistory.com)

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다