1. Unity 코루틴(Coroutine)이란?
유니티(Unity)에서 코루틴(Coroution)은 프레임 간에 멈추고 재개할 수 있는 특별한 형태의 메서드입니다. 코루틴은 특정 시간 동안 대기하거나 비동기 작업을 수행할 때 유용하게 사용할 수 있습니다. 유니티에서는 코루틴을 사용하여 비동기적인 작업을 처리할 수 있으며, 이는 게임 개발에서 자주 사용됩니다.
주요 특징
1. 메인 스레드에서 실행
- Unity의 코루틴은 메인 스레드에서 실행됩니다. 이는 코루틴이 Unity API와 상호작용할 수 있게 해주며, 비동기 작업을 쉽게 처리할 수 있게 합니다. 따라서, 코루틴 내에서 Unity 오브젝트에 안전하게 접근할 수 있습니다.
2. 비동기 작업
- 코루틴은 비동기적으로 작업을 처리할 수 있게 해주며, 긴 작업을 여러 프레임에 걸쳐 나누어 수행할 수 있습니다. 이는 게임의 프레임률 유지에 도움이 됩니다.
3. 타이밍 제어
- WaitForSeconds, WaitForEndOfFrame, WaitForFixedUpdate, WaitUntil, WaitWhile 등을 사용하여 다양한 방식으로 대기 시간을 제어할 수 있습니다.
4. 코루틴 중첩
- 코루틴 내에서 다른 코루틴을 호출할 수 있습니다. 이를 통해 복잡한 비동기 작업을 체계적으로 관리할 수 있습니다.
5. 간단한 상태 관리
- 코루틴은 복잡한 상태 머신을 간단하게 표현할 수 있게 해줍니다. 여러 상태를 일련의 코루틴 단계로 나눌 수 있습니다.
6. 게임 오브젝트의 활성화 상태에 따른 자동 정지
- 게임 오브젝트가 비활성화되거나 파괴될 때, 해당 오브젝트에서 시작된 코루틴은 자동으로 중지됩니다.
사용 방법
코루틴은 MonoBehaviour를 상속받는 클래스에서만 사용할 수 있으며, 'IEnumerator' 타입의 메서드를 사용하여 정의되며 'StartCoroutine' 메서드를 통해 시작됩니다.
기본 사용 예제:
using UnityEngine;
public class CoroutineExample : MonoBehaviour
{
void Start()
{
// 코루틴 시작
StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
Debug.Log("Start Coroutine");
// 2초 대기
yield return new WaitForSeconds(2);
Debug.Log("After 2 seconds");
// 3프레임 대기
yield return null;
yield return null;
yield return null;
Debug.Log("After 3 frames");
}
}
다양한 기능
1. WaitForSeconds:
지정된 시간동안 대기합니다. 시간은 초 단위로 지정됩니다.
using UnityEngine;
public class WaitForSecondsExample : MonoBehaviour
{
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator ExampleCoroutine()
{
Debug.Log("Coroutine started at " + Time.time);
// 2초 대기
yield return new WaitForSeconds(2);
Debug.Log("2 seconds later: " + Time.time);
}
}
2. WaitForEndOfFrame
현재 프레임의 렌더링이 끝날 때 까지 대기합니다. 이를 통해 현재 프레임의 마지막에 특정 작업을 수행할 수 있습니다.
using UnityEngine;
public class WaitForEndOfFrameExample : MonoBehaviour
{
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator ExampleCoroutine()
{
Debug.Log("Start of frame");
// 프레임 끝까지 대기
yield return new WaitForEndOfFrame();
Debug.Log("End of frame");
}
}
3. WaitForFixedUpdate
다음 물리 업데이트(고정 프레임 업데이트)까지 대기합니다. 주로 물리 연산이 완료된 후 특정 작업을 수행할 때 사용됩니다.
using UnityEngine;
public class WaitForFixedUpdateExample : MonoBehaviour
{
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator ExampleCoroutine()
{
Debug.Log("Before FixedUpdate");
// 다음 FixedUpdate까지 대기
yield return new WaitForFixedUpdate();
Debug.Log("After FixedUpdate");
}
}
4. WaitUntil
특정 조건이 참이 될 때까지 대기합니다. 조건은 람다식으로 지정할 수 있습니다.
using UnityEngine;
public class WaitUntilExample : MonoBehaviour
{
private bool isReady = false;
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator ExampleCoroutine()
{
Debug.Log("Waiting for condition...");
// 조건이 true가 될 때까지 대기
yield return new WaitUntil(() => isReady);
Debug.Log("Condition met!");
}
void Update()
{
// Space 키를 누르면 조건을 true로 설정
if (Input.GetKeyDown(KeyCode.Space))
{
isReady = true;
}
}
}
5. WaitWhile
특정 조건이 거짓이 될 때까지 대기합니다. 조건은 람다식으로 지정할 수 있습니다.
using UnityEngine;
public class WaitWhileExample : MonoBehaviour
{
private bool isRunning = true;
void Start()
{
StartCoroutine(ExampleCoroutine());
}
IEnumerator ExampleCoroutine()
{
Debug.Log("Waiting while condition is true...");
// 조건이 false가 될 때까지 대기
yield return new WaitWhile(() => isRunning);
Debug.Log("Condition is false, continuing execution.");
}
void Update()
{
// Space 키를 누르면 조건을 false로 설정
if (Input.GetKeyDown(KeyCode.Space))
{
isRunning = false;
}
}
}
6. 중첩 코루틴
코루틴 내에서 다른 코루틴을 호출할 수 있습니다.
IEnumerator OuterCoroutine()
{
Debug.Log("Starting Outer Coroutine");
yield return StartCoroutine(InnerCoroutine());
Debug.Log("Outer Coroutine Resumed");
}
IEnumerator InnerCoroutine()
{
Debug.Log("Starting Inner Coroutine");
yield return new WaitForSeconds(2);
Debug.Log("Inner Coroutine Finished");
}
7. 종료
코루틴을 중단하거나 정지시키기 위해 'StopCoroutine', 'StopAllCoroutines'를 사용할 수 있습니다.
StopCoroutine: 특정 코루틴 정지
StopAllCoroutines: 해당 클래스의 모든 코루틴 정지
IEnumerator MyCoroutine()
{
while (true)
{
Debug.Log("Running Coroutine");
yield return new WaitForSeconds(1);
}
}
void Start()
{
StartCoroutine("MyCoroutine");
}
void StopMyCoroutine()
{
StopCoroutine("MyCoroutine");
}
using UnityEngine;
public class StopAllCoroutinesExample : MonoBehaviour
{
void Start()
{
// 여러 코루틴 시작
StartCoroutine(Coroutine1());
StartCoroutine(Coroutine2());
// 5초 후에 모든 코루틴 중지
StartCoroutine(StopAllAfterDelay(5f));
}
IEnumerator Coroutine1()
{
while (true)
{
Debug.Log("Coroutine1 running...");
yield return new WaitForSeconds(1);
}
}
IEnumerator Coroutine2()
{
while (true)
{
Debug.Log("Coroutine2 running...");
yield return new WaitForSeconds(2);
}
}
IEnumerator StopAllAfterDelay(float delay)
{
// delay 만큼 대기
yield return new WaitForSeconds(delay);
Debug.Log("Stopping all coroutines");
// 모든 코루틴 중지
StopAllCoroutines();
}
}
2. IEnumerator, IEnumerable 이란?
Unity의 코루틴은 IEnumerator 형태의 메서드를 통해 실행합니다. 해당 인터페이스들은 C#에서 컬렉션을 반복(iteration)하고, 컬렉션의 요소에 순차적으로 접근하는 표준 방법을 제공합니다. 이 두 인터페이스는 다양한 컬렉션 타입에서 공통적으로 사용되며, foreach 루프 기반을 제공합니다.
IEnumerator 인터페이스
컬렉션의 요소를 하나씩 순차적으로 접근하는 방법을 정의합니다. 이는 주로 컬렉션의 현재 요소를 가져오고, 다음 요소로 이동하며, 열거를 재설정하는 기능을 제공합니다.
목적
1. 컬렉션 반복: 컬렉션의 각 요소에 순차적으로 접근할 수 있게 합니다.
2. 상태 관리: 현재 요소를 추적하고, 다음 요소로 이동하며, 열거 상태를 관리합니다.
3. 표준화: 다양한 컬렉션 타입에서 일관된 반복 패턴을 제공합니다.
주요 멤버
Current: 현재 요소를 가져옵니다.
MoveNext(): 다음 요소로 이동합니다. 요소가 더 이상 없으면 false를 반환합니다.
Reset(): 열거를 초기 상태로 재설정합니다.(일반적으로 이 메서드는 구현되지 않습니다.)
예제
using System;
using System.Collections;
public class SimpleEnumerator : IEnumerator
{
private string[] _items;
private int position = -1;
public SimpleEnumerator(string[] items)
{
_items = items;
}
public bool MoveNext()
{
position++;
return (position < _items.Length);
}
public void Reset()
{
position = -1;
}
public object Current
{
get
{
if (position < 0 || position >= _items.Length)
throw new InvalidOperationException();
return _items[position];
}
}
}
public class Program
{
public static void Main()
{
string[] items = { "Apple", "Banana", "Cherry" };
IEnumerator enumerator = new SimpleEnumerator(items);
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
}
}
IEnumerable 인터페이스
IEnumerator를 반환하는 GetEnumerator() 메서드를 정의하여, 컬렉션의 반복을 시작할 수 있게 합니다. IEnumerable은 컬렉션을 반복할 수 있는 객체를 나타냅니다.
목적
1. 반복 지원: 컬렉션의 반복을 시작하는 표준 방법을 제공합니다.
2. foreach 지원: 컬렉션을 foreach루프에서 사용할 수 있게합니다.
3. 호환성: .NET의 다양한 컬렉션과 호환되도록 합니다.
주요 멤버
- GetEnumerator(): IEnumerator를 반환하여 반복을 시작합니다.
예제
using System;
using System.Collections;
public class SimpleCollection : IEnumerable
{
private string[] _items;
public SimpleCollection(string[] items)
{
_items = items;
}
public IEnumerator GetEnumerator()
{
return new SimpleEnumerator(_items);
}
}
public class Program
{
public static void Main()
{
string[] items = { "Apple", "Banana", "Cherry" };
IEnumerable collection = new SimpleCollection(items);
foreach (string item in collection)
{
Console.WriteLine(item);
}
}
}
Unity에서 코루틴은 왜 IEnumerator 형태의 메서드를 사용할까?
Unity에서 코루틴을 IEnumerator형태의 메서드로 사용하는 이유는 주로 코루틴의 동작 방식과 관련이 있습니다. IEnumerator인터페이스는 반복자(iterator)패턴을 구현하는 데 사용되며, 이를 통해 코루틴이 일시 중지되고 다시 시작되는 메커니즘을 쉽게 구현할 수 있습니다.
이유 1: 반복자 패턴을 통한 일시 중지와 재개
IEnumerator는 반복자 패턴을 구현하는 인터페이스로, 이 패턴은 컬렉션을 순회할 수 있게 해줍니다. Unity에서는 이 패턴을 활용하여 코루틴을 프레임 간에 일시 중지하고 재개할 수 있게 합니다.
- IEnumerator의 MoveNext()메서드는 코루틴의 다음 실행 단계를 결정합니다.
- yield return문을 통해 코루틴을 일시 중지하고, 다음 단계로 제어를 넘길 수 있습니다.
이유2: 유연한 대기 상태 관리
IEnumerator인터페이스를 사용하면 다양한 대기 상태를 유연하게 관리할 수 있습니다. yield return문을 통해 다양한 조건(예: 특정 시간, 특정 조건, 프레임 끝 등)에서 대기할 수 있습니다.
- yield return new WaitForSeconds(1): 1초 동안 대기
- yield return null: 다음 프레임까지 대기
- yield return new WaitUntil(() => condition): 특정 조건이 충족될 때까지 대기
이유3: 성능 및 메모리 관리
IEnumerator는 상태를 유지하면서 코루틴의 실행을 제어할 수 있게 해줍니다. 이를 통해 메모리와 성능 측면에서도 효율적인 비동기 작업 처리가 가능합니다.
3. 코루틴과 가비지 컬렉션
Unity의 코루틴은 일반적으로 가비지 컬렉션(GC)를 유발할 수 있습니다. 이는 주로 IEnumerator객체와 yield return을 사용하면서 발생합니다. 특히 많은 코루틴을 생성하거나 자주 호출할 때 GC오버헤드가 문제가 될 수 있습니다.
가비지 컬렉션의 발생 원인
1. IEnumerator객체 생성:
코루틴은 호출할 때마다 IEnumerator객체가 생성됩니다. 이 객체들은 GC가 필요할 때 수집됩니다.
2. yield return 사용:
yield return문은 코루틴이 일시 중지되었음을 나타내며, 이 때마다 새로운 객체가 생성될 수 있습니다.
3. 상태 저장:
코루틴이 중단될 때마다 상태를 저장하고, 이를 재개할 때 상태를 복원해야 합니다. 이 과정에서 추가적인 객체가 생성될 수 있습니다.
가비지 컬렉션 방지 방법
1. 코루틴 캐싱:
자주 사용하는 코루틴을 미리 생성해 두고 재사용하는 방법입니다. 이를 통해 반복적으로 객체를 생성하는 것을 방지할 수 있습니다.
private IEnumerator cachedCoroutine;
void Start()
{
cachedCoroutine = MyCoroutine();
StartCoroutine(cachedCoroutine);
}
IEnumerator MyCoroutine()
{
while (true)
{
yield return new WaitForSeconds(1);
Debug.Log("This is a cached coroutine.");
}
}
2. 풀링(Pooling):
코루틴에서 사용하는 객체를 풀링하여 재사용할 수 있습니다. 예를 들어, WaitForSeconds객체를 풀링하여 매번 새로 생성하지 않도록 할 수 있습니다.
private Dictionary<float, WaitForSeconds> _timeInterval = new Dictionary<float, WaitForSeconds>();
public WaitForSeconds WaitForSeconds(float seconds)
{
WaitForSeconds wfs;
if (!_timeInterval.TryGetValue(seconds, out wfs))
_timeInterval.Add(seconds, wfs = new WaitForSeconds(seconds));
return wfs;
}
3. LINQ 사용 최소화:
코루틴 내부에서 LINQ를 자주 사용하면 많은 임시 객체가 생성되어 GC 오버헤드를 유발할 수 있습니다. 이를 최소화하거나 사용하지 않도록 합니다.
이러한 최적화 방법을 통해 Unity에서 코루틴 사용 시 발생하는 가비지 컬렉션을 최소화하고, 게임의 성능을 향상시킬 수 있습니다.
지금까지 Unity 코루틴에 대해 알아보았습니다.
현재 글의 내용 중 틀린 부분이나, 지적하실 내용이 있으시다면 언제든지 알려주세요! 읽어주셔서 감사합니다.
'Unity 공부' 카테고리의 다른 글
[Unity] 재사용 스크롤 뷰 - 수정(Recyclable Scroll View) (2) | 2024.10.08 |
---|---|
[Unity] 속성(Attribute) (0) | 2024.08.24 |
[Unity] 재사용 스크롤 뷰(Recyclable Scroll View) (0) | 2024.06.18 |
[Unity] 카메라 이동(드래그) (0) | 2024.02.04 |
[Unity] 컷 아웃 마스크(Cutout Mask) (0) | 2024.01.24 |