1. 코루틴이란?
유니티에서 코루틴(Coroutine) 은 작업을 한 번에 끝내지 않고, 중간에 잠깐 멈췄다가 다시 이어서 실행할 수 있는 기능이다.
보통 C# 메서드는 실행되면 위에서 아래로 한 번에 실행된다.
void Start()
{
Debug.Log("시작");
Debug.Log("끝");
}
이 코드는 시작을 출력한 뒤 바로 끝을 출력한다.
하지만 코루틴을 사용하면 중간에 일정 시간 기다리거나, 다음 프레임까지 멈췄다가 다시 실행할 수 있다.
IEnumerator Start()
{
Debug.Log("시작");
yield return new WaitForSeconds(2f);
Debug.Log("2초 뒤 실행");
}
이 코드는 시작을 출력하고, 2초 후에 2초 뒤 실행을 출력한다.
즉, 코루틴은 시간 차를 두고 실행되는 로직을 만들 때 자주 사용된다.
2. 코루틴을 사용하는 이유
게임에서는 “지금 바로 실행”보다 “조금 있다가 실행”해야 하는 상황이 많다.
예를 들어:
- 공격 후 1초 뒤 다시 공격 가능
- 몬스터가 3초마다 움직임
- 스킬 쿨타임 처리
- 페이드 인 / 페이드 아웃
- 일정 시간 무적 상태 유지
- 대사 한 줄씩 출력
- 총알을 일정 간격으로 발사
이런 기능을 Update() 안에서 전부 처리할 수도 있지만, 코드가 복잡해질 수 있다.
코루틴을 사용하면 시간 흐름이 있는 코드를 더 읽기 쉽게 작성할 수 있다.
3. 코루틴의 기본 형태
코루틴은 보통 IEnumerator 반환형을 사용한다.
IEnumerator MyCoroutine()
{
yield return null;
}
그리고 실행할 때는 StartCoroutine()을 사용한다.
StartCoroutine(MyCoroutine());
전체 예시는 다음과 같다.
using System.Collections;
using UnityEngine;
public class CoroutineExample : MonoBehaviour
{
void Start()
{
StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
Debug.Log("코루틴 시작");
yield return new WaitForSeconds(1f);
Debug.Log("1초 뒤 실행");
}
}
여기서 중요한 부분은 이 코드다.
yield return new WaitForSeconds(1f);
이 코드는 1초 동안 기다렸다가 다음 줄을 실행하라는 뜻이다.
4. yield return이란?
코루틴에서 가장 중요한 키워드는 yield return이다.
yield return은 코루틴을 잠시 멈추는 지점이다.
IEnumerator Test()
{
Debug.Log("A");
yield return null;
Debug.Log("B");
}
이 코드는 A를 출력하고, 다음 프레임에 B를 출력한다.
즉,
yield return null;
은 한 프레임 기다리기라는 뜻이다.
5. 자주 사용하는 yield 종류
1. yield return null
yield return null;
다음 프레임까지 기다린다.
예시:
IEnumerator Move()
{
transform.position += Vector3.right;
yield return null;
transform.position += Vector3.right;
}
첫 번째 이동 후, 다음 프레임에 한 번 더 이동한다.
2. WaitForSeconds
yield return new WaitForSeconds(2f);
지정한 초만큼 기다린다.
예시:
IEnumerator AttackDelay()
{
Debug.Log("공격!");
yield return new WaitForSeconds(1f);
Debug.Log("다시 공격 가능");
}
공격 후 1초 뒤에 다시 공격 가능하다는 처리를 할 수 있다.
3. WaitUntil
yield return new WaitUntil(() => hp <= 0);
조건이 참이 될 때까지 기다린다.
예시:
IEnumerator WaitDeath()
{
yield return new WaitUntil(() => hp <= 0);
Debug.Log("플레이어 사망");
}
hp <= 0이 될 때까지 기다리다가, 조건이 만족되면 다음 코드가 실행된다.
4. WaitWhile
yield return new WaitWhile(() => isMoving);
조건이 참인 동안 계속 기다린다.
예시:
IEnumerator WaitMoveEnd()
{
yield return new WaitWhile(() => isMoving);
Debug.Log("이동 종료");
}
isMoving이 true인 동안 기다리고, false가 되면 다음 코드가 실행된다.
5. WaitForEndOfFrame
yield return new WaitForEndOfFrame();
현재 프레임의 모든 렌더링이 끝난 뒤 실행된다.
스크린샷 저장 같은 기능에서 사용되는 경우가 있다.
6. WaitForFixedUpdate
yield return new WaitForFixedUpdate();
다음 물리 업데이트 타이밍까지 기다린다.
Rigidbody 같은 물리 관련 처리를 할 때 사용할 수 있다.
6. 코루틴 예제 1: 3초 뒤 오브젝트 삭제
using System.Collections;
using UnityEngine;
public class DestroyAfterTime : MonoBehaviour
{
void Start()
{
StartCoroutine(DestroyRoutine());
}
IEnumerator DestroyRoutine()
{
yield return new WaitForSeconds(3f);
Destroy(gameObject);
}
}
이 코드는 게임이 시작되고 3초 뒤에 자기 자신을 삭제한다.
총알, 이펙트, 임시 오브젝트 등을 제거할 때 사용할 수 있다.
7. 코루틴 예제 2: 스킬 쿨타임
using System.Collections;
using UnityEngine;
public class Skill : MonoBehaviour
{
private bool canUseSkill = true;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && canUseSkill)
{
StartCoroutine(SkillRoutine());
}
}
IEnumerator SkillRoutine()
{
canUseSkill = false;
Debug.Log("스킬 사용!");
yield return new WaitForSeconds(5f);
canUseSkill = true;
Debug.Log("스킬 사용 가능!");
}
}
이 코드는 스페이스바를 누르면 스킬을 사용하고, 5초 동안 다시 사용할 수 없게 만든다.
핵심은 이 부분이다.
canUseSkill = false;
yield return new WaitForSeconds(5f);
canUseSkill = true;
스킬을 사용한 직후에는 canUseSkill을 false로 만들고, 5초 뒤 다시 true로 바꾼다.
8. 코루틴 예제 3: 몬스터가 일정 시간마다 공격하기
using System.Collections;
using UnityEngine;
public class MonsterAttack : MonoBehaviour
{
void Start()
{
StartCoroutine(AttackRoutine());
}
IEnumerator AttackRoutine()
{
while (true)
{
Debug.Log("몬스터 공격!");
yield return new WaitForSeconds(2f);
}
}
}
이 코드는 몬스터가 2초마다 계속 공격하게 만든다.
while (true)
는 무한 반복을 의미한다.
하지만 코루틴 안에서는 중간에
yield return new WaitForSeconds(2f);
가 있기 때문에 게임이 멈추지 않고, 2초마다 한 번씩 실행된다.
주의할 점은 yield return 없이 while(true)를 쓰면 게임이 멈출 수 있다.
나쁜 예시:
while (true)
{
Debug.Log("무한 반복");
}
이 코드는 쉬는 지점이 없어서 유니티가 멈출 수 있다.
9. 코루틴 멈추기
코루틴은 StopCoroutine()으로 멈출 수 있다.
Coroutine attackCoroutine;
void Start()
{
attackCoroutine = StartCoroutine(AttackRoutine());
}
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
StopCoroutine(attackCoroutine);
}
}
IEnumerator AttackRoutine()
{
while (true)
{
Debug.Log("공격");
yield return new WaitForSeconds(1f);
}
}
또는 해당 오브젝트에서 실행 중인 모든 코루틴을 멈추려면:
StopAllCoroutines();
을 사용할 수 있다.
StopAllCoroutines();
은 현재 MonoBehaviour에서 실행 중인 모든 코루틴을 멈춘다.
10. 코루틴과 Update의 차이
Update()는 매 프레임 자동으로 실행된다.
void Update()
{
Debug.Log("매 프레임 실행");
}
반면 코루틴은 직접 실행해야 한다.
StartCoroutine(MyCoroutine());
그리고 코루틴은 중간에 기다릴 수 있다.
| 실행 방식 | 매 프레임 자동 실행 | StartCoroutine으로 실행 |
| 시간 대기 | 직접 타이머 구현 필요 | WaitForSeconds 사용 가능 |
| 사용 목적 | 계속 감시, 입력 처리, 이동 | 지연 실행, 반복 패턴, 쿨타임 |
| 코드 흐름 | 매 프레임 반복 | 순서대로 기다리며 실행 |
예를 들어 플레이어 이동은 보통 Update()에서 처리한다.
void Update()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x * speed * Time.deltaTime);
}
하지만 스킬 쿨타임처럼 “몇 초 기다리기”가 필요한 기능은 코루틴이 더 편하다.
11. 코루틴은 멀티스레드가 아니다
코루틴을 처음 배우면 “동시에 실행되는 것처럼 보이니까 멀티스레드인가?”라고 생각할 수 있다.
하지만 유니티 코루틴은 멀티스레드가 아니다.
코루틴은 메인 스레드에서 실행된다.
다만 yield return을 통해 실행을 잠깐 멈췄다가 다음 프레임이나 일정 시간 뒤에 이어서 실행할 뿐이다.
즉, 코루틴은 진짜로 여러 작업을 동시에 처리하는 기능이라기보다는, 시간을 나누어 실행하는 기능에 가깝다.
12. 코루틴 사용할 때 주의할 점
1. StartCoroutine을 여러 번 호출하면 여러 개가 동시에 실행된다
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(AttackRoutine());
}
}
이렇게 작성하면 스페이스바를 여러 번 누를 때마다 코루틴이 새로 실행된다.
원하지 않는 중복 실행이 생길 수 있다.
그래서 보통 bool 값을 사용해 막는다.
private bool isAttacking = false;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !isAttacking)
{
StartCoroutine(AttackRoutine());
}
}
IEnumerator AttackRoutine()
{
isAttacking = true;
Debug.Log("공격 시작");
yield return new WaitForSeconds(1f);
Debug.Log("공격 종료");
isAttacking = false;
}
2. 비활성화된 오브젝트에서는 코루틴이 멈출 수 있다
코루틴을 실행하는 MonoBehaviour나 GameObject가 비활성화되면 코루틴도 영향을 받을 수 있다.
예를 들어 오브젝트가 꺼지거나 삭제되면 그 오브젝트에서 실행 중이던 코루틴도 더 이상 정상적으로 이어지지 않는다.
gameObject.SetActive(false);
또는
Destroy(gameObject);
를 하면 해당 오브젝트의 코루틴도 중단될 수 있다.
3. yield가 없는 반복문은 위험하다
코루틴 안에서 반복문을 사용할 때는 반드시 중간에 yield return을 넣어야 한다.
좋은 예시:
IEnumerator CountRoutine()
{
while (true)
{
Debug.Log("반복 중");
yield return null;
}
}
나쁜 예시:
IEnumerator CountRoutine()
{
while (true)
{
Debug.Log("반복 중");
}
}
두 번째 코드는 한 프레임 안에서 무한 반복을 하기 때문에 유니티가 멈출 수 있다.
13. 코루틴을 어디에 쓰면 좋을까?
코루틴은 다음과 같은 기능에 잘 어울린다.
스킬 쿨타임
yield return new WaitForSeconds(coolTime);
공격 딜레이
yield return new WaitForSeconds(attackDelay);
일정 시간 무적
isInvincible = true;
yield return new WaitForSeconds(2f);
isInvincible = false;
대사 출력
foreach (char letter in text)
{
dialogueText.text += letter;
yield return new WaitForSeconds(0.05f);
}
페이드 효과
while (alpha < 1f)
{
alpha += Time.deltaTime;
yield return null;
}
14. 코루틴 간단 정리
코루틴은 유니티에서 시간 흐름이 있는 코드를 쉽게 작성하게 해주는 기능이다.
일반 메서드는 실행되면 끝까지 한 번에 실행되지만, 코루틴은 yield return을 사용해서 중간에 멈췄다가 나중에 이어서 실행할 수 있다.
특히 스킬 쿨타임, 공격 딜레이, 몬스터 반복 공격, 페이드 효과, 대사 출력 같은 기능에서 자주 사용된다.
단, 코루틴은 멀티스레드가 아니며, 너무 많이 사용하거나 중복 실행을 제대로 관리하지 않으면 코드가 복잡해질 수 있다.
마무리
유니티에서 코루틴은 초보자가 반드시 알아야 하는 중요한 기능이다.
처음에는 IEnumerator, yield return, StartCoroutine() 같은 문법이 낯설 수 있지만, 핵심은 간단하다.
코루틴은 “기다렸다가 이어서 실행하는 함수”이다.
이렇게 기억하면 이해하기 쉽다.
IEnumerator Example()
{
Debug.Log("먼저 실행");
yield return new WaitForSeconds(1f);
Debug.Log("1초 뒤 실행");
}
게임에서는 시간에 따라 동작하는 기능이 많기 때문에, 코루틴을 잘 활용하면 훨씬 깔끔하고 자연스러운 코드를 작성할 수 있다.