코루틴이란?
코루틴은 함수(function)를 일시 중지하고 나중에 다시 실행할 수 있게 해주는 기능입니다. 시간차로 여러 작업을 수행하거나, 일정 시간 대기 후 실행하는 등의 상황에서 사용합니다.
기본 개념
- IEnumerator: 코루틴 함수(function)의 반환(return) 타입(type)
- StartCoroutine(): 코루틴을 시작하는 함수(function)
- yield: 코루틴을 일시 중지하는 키워드
- WaitForSeconds: 지정한 시간만큼 대기
1. 기본 코루틴 구조
코루틴 함수(function) 만들기
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
void Start()
{
// 코루틴 시작
StartCoroutine(MyCoroutine());
}
// 코루틴 함수 (반환 타입: IEnumerator)
IEnumerator MyCoroutine()
{
Debug.Log("코루틴 시작!");
// 2초 대기
yield return new WaitForSeconds(2f);
Debug.Log("2초 후 실행!");
}
}
코루틴의 특징
- 일시 중지 가능:
yield로 실행을 멈출 수 있음 - 나중에 재개: Unity가 자동으로 다시 실행
- Update()와 독립적: Update()와 별개로 동작
2. WaitForSeconds - 시간 대기
WaitForSeconds는 지정한 시간(초)만큼 대기합니다.
기본 사용법
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
void Start()
{
// 코루틴 시작
StartCoroutine(SpawnFireballs());
}
IEnumerator SpawnFireballs()
{
for (int i = 0; i < 10; i++)
{
// 불덩어리 생성
Instantiate(fireballPrefab);
// 1초 대기
yield return new WaitForSeconds(1f);
}
}
}
랜덤 시간 대기
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
void Start()
{
StartCoroutine(SpawnFireballsWithDelay());
}
IEnumerator SpawnFireballsWithDelay()
{
for (int i = 0; i < 10; i++)
{
// 불덩어리 생성
Instantiate(fireballPrefab);
// 마지막 불덩어리가 아니면 대기
if (i < 9)
{
// 랜덤한 시간 대기 (0.5초 ~ 2초)
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSeconds(delay);
}
}
}
}
3. WaitForSecondsRealtime - 실제 시간 대기
WaitForSecondsRealtime는 Time.timeScale의 영향을 받지 않는 실제 시간으로 대기합니다.
Time.timeScale과의 차이
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
void Start()
{
StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
Debug.Log("시작!");
// Time.timeScale의 영향을 받음 (게임이 일시정지되면 대기)
yield return new WaitForSeconds(2f);
Debug.Log("WaitForSeconds 완료!");
// Time.timeScale의 영향을 받지 않음 (게임이 일시정지되어도 대기)
yield return new WaitForSecondsRealtime(2f);
Debug.Log("WaitForSecondsRealtime 완료!");
}
}
실제 사용 예제
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
IEnumerator SpawnFireballsWithDelay()
{
for (int i = 0; i < 10; i++)
{
Instantiate(fireballPrefab);
if (i < 9)
{
// 게임이 일시정지되어도 실제 시간으로 대기
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSecondsRealtime(delay);
}
}
}
}
4. WaitForEndOfFrame - 프레임 종료 대기
WaitForEndOfFrame는 현재 프레임이 완전히 끝날 때까지 대기합니다.
사용 예제
using UnityEngine;
using System.Collections;
public class Screenshot : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(TakeScreenshot());
}
}
IEnumerator TakeScreenshot()
{
// 현재 프레임이 완전히 렌더링될 때까지 대기
yield return new WaitForEndOfFrame();
// 스크린샷 찍기
ScreenCapture.CaptureScreenshot("screenshot.png");
Debug.Log("스크린샷 저장 완료!");
}
}
5. 코루틴 중지하기
StopCoroutine() - 특정 코루틴 중지
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
Coroutine myCoroutine;
void Start()
{
// 코루틴 시작하고 참조 저장
myCoroutine = StartCoroutine(MyCoroutine());
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// 코루틴 중지
if (myCoroutine != null)
{
StopCoroutine(myCoroutine);
}
}
}
IEnumerator MyCoroutine()
{
while (true)
{
Debug.Log("실행 중...");
yield return new WaitForSeconds(1f);
}
}
}
StopAllCoroutines() - 모든 코루틴 중지
void OnDestroy()
{
// 이 오브젝트의 모든 코루틴 중지
StopAllCoroutines();
}
6. 실제 사용 예제
예제 1: 불덩어리 시간차 생성
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public int fireballCount = 10;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
void Start()
{
StartCoroutine(SpawnFireballsWithDelay());
}
IEnumerator SpawnFireballsWithDelay()
{
// 이미 생성된 불덩어리가 있으면 생성하지 않음
if (spawnedFireballs.Count > 0)
{
yield break; // 코루틴 종료
}
// 지정된 개수만큼 불덩어리 생성
for (int i = 0; i < fireballCount; i++)
{
// 불덩어리 하나 생성
SpawnSingleFireball();
// 마지막 불덩어리가 아니면 랜덤한 시간만큼 대기
if (i < fireballCount - 1)
{
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSecondsRealtime(delay);
}
}
}
void SpawnSingleFireball()
{
Vector3 spawnPosition = GetRandomSpawnPosition();
GameObject fireball = Instantiate(fireballPrefab, spawnPosition, Quaternion.identity);
fireball.SetActive(true);
}
Vector3 GetRandomSpawnPosition()
{
float randomX = Random.Range(-5f, 5f);
return new Vector3(randomX, 8f, 0);
}
}
예제 2: 페이드 인/아웃
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class FadeEffect : MonoBehaviour
{
public Image fadeImage;
public float fadeDuration = 1f;
void Start()
{
StartCoroutine(FadeIn());
}
// 페이드 인 (검은 화면에서 점점 밝아짐)
IEnumerator FadeIn()
{
float elapsed = 0f;
Color color = fadeImage.color;
while (elapsed < fadeDuration)
{
elapsed += Time.deltaTime;
color.a = 1f - (elapsed / fadeDuration);
fadeImage.color = color;
yield return null; // 다음 프레임까지 대기
}
color.a = 0f;
fadeImage.color = color;
}
// 페이드 아웃 (밝은 화면에서 점점 어두워짐)
IEnumerator FadeOut()
{
float elapsed = 0f;
Color color = fadeImage.color;
while (elapsed < fadeDuration)
{
elapsed += Time.deltaTime;
color.a = elapsed / fadeDuration;
fadeImage.color = color;
yield return null;
}
color.a = 1f;
fadeImage.color = color;
}
}
예제 3: 반복 실행
using UnityEngine;
using System.Collections;
public class RepeatingAction : MonoBehaviour
{
void Start()
{
StartCoroutine(RepeatEverySecond());
}
IEnumerator RepeatEverySecond()
{
while (true)
{
Debug.Log("1초마다 실행!");
// 1초 대기
yield return new WaitForSeconds(1f);
}
}
}
7. yield return null
yield return null은 다음 프레임까지 대기합니다.
사용 예제
using UnityEngine;
using System.Collections;
public class SmoothMovement : MonoBehaviour
{
public Vector3 targetPosition;
public float speed = 2f;
void Start()
{
StartCoroutine(MoveToTarget());
}
IEnumerator MoveToTarget()
{
while (Vector3.Distance(transform.position, targetPosition) > 0.1f)
{
// 매 프레임마다 조금씩 이동
transform.position = Vector3.MoveTowards(
transform.position,
targetPosition,
speed * Time.deltaTime
);
// 다음 프레임까지 대기
yield return null;
}
}
}
8. 주의사항
1. MonoBehaviour 필요
// ✅ 코루틴은 MonoBehaviour를 상속받은 클래스에서만 사용 가능
public class Example : MonoBehaviour
{
IEnumerator MyCoroutine() { }
}
// ❌ 일반 클래스에서는 사용 불가
public class Example
{
IEnumerator MyCoroutine() { } // 에러!
}
2. StartCoroutine() 필수
// ✅ 올바른 방법
StartCoroutine(MyCoroutine());
// ❌ 잘못된 방법 (코루틴이 실행되지 않음)
MyCoroutine();
3. 오브젝트 파괴 시 자동 중지
// GameObject가 파괴되면 코루틴도 자동으로 중지됨
void OnDestroy()
{
// 명시적으로 중지할 수도 있음
StopAllCoroutines();
}
4. 중첩 코루틴
IEnumerator OuterCoroutine()
{
Debug.Log("외부 코루틴 시작");
// 내부 코루틴 시작 (완료될 때까지 대기)
yield return StartCoroutine(InnerCoroutine());
Debug.Log("내부 코루틴 완료 후 실행");
}
IEnumerator InnerCoroutine()
{
yield return new WaitForSeconds(2f);
Debug.Log("내부 코루틴 완료");
}
9. 정리
코루틴 vs 일반 함수(function)
| 특징 | 일반 함수(function) | 코루틴 |
|---|---|---|
| 실행 방식 | 즉시 실행 완료 | 일시 중지 후 재개 가능 |
| 시간 제어 | 불가능 | 가능 (yield 사용) |
| 반환(return) 타입(type) | void, int 등 | IEnumerator |
주요 yield 타입(type)
yield return new WaitForSeconds(시간): 지정한 시간 대기yield return new WaitForSecondsRealtime(시간): 실제 시간 대기yield return new WaitForEndOfFrame(): 프레임 종료 대기yield return null: 다음 프레임 대기
베스트 프랙티스
- StartCoroutine() 사용: 코루틴은 반드시 StartCoroutine()으로 시작
- 적절한 yield 사용: 용도에 맞는 yield 타입(type) 선택
- 중지 처리: 필요시 StopCoroutine()으로 중지
- 성능 고려: 너무 많은 코루틴은 성능 저하 가능
연습 문제
-
1초마다 "Hello"를 출력하는 코루틴을 작성하세요.
-
플레이어가 스페이스바를 누르면 3초 후에 "3초 경과!"를 출력하는 코루틴을 작성하세요.
-
오브젝트가 서서히 사라지도록 알파값을 0으로 만드는 코루틴을 작성하세요.