시간의 흐름에 따라 함수를 제어할 때 자주 사용하는 함수다.
기능을 분리하여 비동기처럼 실행되지만,
✨사실은 멀티스레드가 아닌 싱글 스레드의 동기 작업이다.
즉, 병렬성이 아닌 동시성 방식이다.
아직은 글로 설명하기 애매하니 코드부터 보자.
Coroutine co;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && co == null)
co = StartCoroutine(MyRoutine());
if (Input.GetKeyDown(KeyCode.Escape) && co != null)
{
StopCoroutine(co);
co = null;
}
}
IEnumerator MyRoutine()
{
// 실행 흐름
yield return new WaitForSeconds(1f);
Debug.Log("1초 후 실행");
// 끝나면 초기화
co = null;
}
우선 코루틴을 사용할 때는 주의할 점이 있는데,
1. 코루틴은 변수에 담아 사용할 것 (메모리 단편화 방지)
2. 사용이 끝나면 중지할 것 (메모리 누수 방지)
그래서 위의 코드도 Coroutine 타입의 필드변수 co를 선언한 후
Update() 함수에서 null 체크를 하고 변수에 담아 사용하고,
Routine() 함수에서 마지막에 co 변수에 null을 할당한다.
코루틴을 실행할 때는 StartCoroutine() 을 사용하고,
코루틴을 종료할 때는 StopCoroutine() 을 사용한다.
실행할 때 인수로 IEnumerator 을 받기 때문에
코루틴에 사용할 함수의 반환 타입은 IEnumerator 타입으로 해야 한다.
그래야 코루틴의 장점인 시간제어를 할 수 있는 yield return 을 사용할 수 있다.
| 명령어 | 설명 |
|---|---|
yield return null | Update 끝날 때 |
yield return new WaitForSeconds(n) | 게임시간 n초간 기다리고, Update 끝날 때 |
yield return new WaitForSecondsRealtime(n) | 현실시간 n초간 기다리고, Update 끝날 때 |
yield return new WaitForFixedUpdate() | FixedUpdate 끝날 때 |
yield return new WaitForEndOfFrame() | 프레임이 끝날 때 (LateUpdate 다음) |
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space)) | (조건 true) 스페이스바 누를떄까지 기다리기 |
yield return new WaitWhile(() => Input.GetKeyDown(KeyCode.Space)); | (조건 false) |
yield return에 여러가지 클래스들을 추가해서 시간이나 타이밍조절을 할 수 있다.
또한 new 키워드로 객체를 생성하기 때문에 중복하여 사용하는 것이라면
필드 변수에 할당하여 사용하는 것이 좋다.
코루틴의 최대 장점은 로직 분리라고 생각한다.
물론 시간제어도 장점 중 하나지만,
이전의 작성했던 탱크 구현 에서는 Update() 에 여러가지 시간이나 기능들을 넣어뒀는데
코루틴을 사용하여 각 기능들을 따로 함수로 관리할 수 있다.
즉, 기능의 책임을 떠넘길 수 있다. 😆

Invoke 함수도 함수를 n초 뒤에 실행하는 함수의 시간제어가 가능하지만,
코루틴과 달리 실행할 함수에 매개변수가 있으면 실행할 수 없다는 큰 단점이 있다.
UnityEvent는 유니티에서 제공하는 이벤트 시스템 클래스로,
델리게이트와 비슷하게 특정 함수들을 실행시킬 수 있다.
주로 UI에 사용한다.
using UnityEngine.Events; 를 선언해야 사용할 수 있다.
UnityEvent는 delegate나 event처럼 다른 함수들을 등록해두고 실행하는 기능이다.
Invoke()로 실행할 수 있다.
// 이벤트 클래스
public UnityEvent staticEventTest1;
public UnityEvent<int> staticEventTest2;
public UnityEvent<int, float, string, bool> staticEventTest3;
void Awake()
{
staticEventTest1.AddListener(StaticTest.TestFunc1);
staticEventTest2.AddListener(StaticTest.TestFunc2);
staticEventTest3.AddListener(StaticTest.TestFunc3);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
staticEventTest1.Invoke();
staticEventTest2.Invoke(5);
// 인수는 4개까지 가능
staticEventTest3.Invoke(1, 5, "Asd", true);
}
}
// 정적 클래스
public static class StaticTest
{
public static void TestFunc1()
{
Debug.Log("테스트함수 실행");
}
public static void TestFunc2(int value)
{
Debug.Log($"테스트함수 실행 {value}");
}
public static void TestFunc3(int v1, float v2, string v3, bool b4)
{
Debug.Log($"{v1} / {v2} / {v3} / {4}");
}
}


유니티 에디터에서 게임 오브젝트를 추가하고,
게임 오브젝트에 포함되어 있는 컴포넌트들에 있는 함수를 선택하여 추가할 수 있다.
다만 정적 클래스는 게임 오브젝트에 포함할 수 없기 때문에 AddListener 함수로 추가해야 한다.
사실 UnityEvent는 C#의 Delegate보다 성능적으로는 안좋다.
다만 델리게이트와 달리 유니티이벤트는 유니티 에디터에서 시각적으로 보거나
이벤트 객체, 이벤트 함수를 설정할 수 있다는 장점이 있다.
하지만 어떤 객체들이 이벤트에 등록되었는지 알 수 없다는 단점이 있다.
(델리게이트는 가능)
여태 코루틴을 메모리 생각없이 그냥 실행하고 그냥 종료하고 했었는데,
그때 흐름이 잘못됐었던 이유를 이제서야 알았다. 🤣
그리고 시각적으로 알 수 있는 유니티 이벤트가 더 편해보이는데
성능적으론 델리게이트가 더 좋다니...
아직은 익숙하지 않으니 쉬워보이는 유니티 이벤트부터 익혀야겠다.