본문 바로가기
게임/Unity

[Unity 이론] Coroutine

by 신인용 2019. 11. 26.
반응형

Coroutine

 

 오늘은 게임을 좀 더 효율적으로 동작할 수 있게 하는 "코루틴 (Coroutine)"에 대해 공부하겠습니다.

 

 

 

1. Coroutine이란?

 Co (with) + routine (루틴) 이 합쳐진 말로, 협동루틴이라고 부를 수 있습니다.

 

 코루틴을 사용하면 다음과 같은 특성을 가질 수 있습니다.

  - 다른 연산이 완료될 때까지 대기 가능

  - 시간의 흐름에 따른 처리 가능

  - 루틴을 병행하여 실행 가능

 

 

 

2. Coroutine의 사용

 위의 특성을 근거로, Unity에서 다음과 같은 상황에 사용하면 좋습니다.

 - 특정 프레임이 아닌, 특정 시간을 대기해야 할 때 (Invoke도 있으나, 나중에 다뤄봅시다)

 

만약 코루틴을 사용하지 않는다면 어떤일이 있을까요??

아래 코드를 살펴봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CharacterBehaviour : MonoBehaviour
{
    public GameObject bullet;
    public Transform firePos;
 
    private float lastFireTime;
    private float coolTime = 1f;
 
    void Start(){
        lastFireTime = Time.time;
    }
    void Update(){
        if(Time.time - lastFireTime >= coolTime){
            Instantiate(bullet, firePos.position, Quaternion.identity);
            lastFireTime = Time.time;
        }
    }
}
 
public class BulletBehaviour : MonoBehaviour
{
    public float speed = 5f;
 
    void Update(){
        transform.Translate(Vector2.right * speed * Time.deltaTime);
    }
}
cs
1초마다 총알 발사

 이 코드는 위와 같이 coolTIme(1초)마다 총알을 발사하는 코드입니다.

coolTime과 현재 시간 - lastFireTime을 비교해서 coolTime이 지났으면 발사하는 식으로 구현되어 있습니다.

 

 그래서 이 코드가 무엇이 문제일까요???

바로 총알이 발사되지 않을 때 낭비되는 연산입니다.

만약 아직 coolTime이 지나지 않아 총알이 발사되지 않고 있다면 유니티에서는 무엇을 하고 있을까요?

 

void Update(){

    if(Time.time - lastFireTime >= coolTime)

 

Update함수 속에 이 연산을 계속하고 있겠지요. 한 마디로 쓸데없는 연산을 하고 있습니다.

게임에서는 성능이 매우매우 중요하기 때문에 이런 문제점을 해결해야 합니다.

다행히 우리는 Coroutine이라는 것을 활용해 이를 해결할 수 있습니다!

 

 

 

 

 이제 코루틴을 사용해서 1초에 한 번씩 총알이 나가는 것을 구현해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CharacterBehaviour : MonoBehaviour
{
    public GameObject bullet;
    public Transform firePos;
 
    void Start(){
        StartCoroutine(Fire(1f));
    }
 
    private IEnumerator Fire(float coolTime){
        while (true){
        Instantiate(bullet, firePos.position, Quaternion.identity);
            yield return new WaitForSeconds(coolTime);
        }
    }
}
 
public class BulletBehaviour : MonoBehaviour
{
    public float speed = 5f;
 
    void Update(){
        transform.Translate(Vector2.right * speed * Time.deltaTime);
    }
}
cs
코루틴을 이용한 1초마다 총알 발사

 

 코루틴을 사용하여 코드를 작성하였습니다.

 코루틴을 사용하면 다음과 같이 동작하게 됩니다.

 

 코루틴은 하나의 쓰레드에서 마치 여러 개의 쓰레드를 사용하는 것같이 작동합니다. 엄밀히 말하면 코루틴은 쓰레드가 아닙니다. 쓰레드처럼 보여지는 것일 뿐입니다.

 그래서 코루틴은 프로그램 상에서 보면 멀티 쓰레드와 비슷하다고 이해하면 됩니다. (근데 쓰레드는 아님)

 

 코루틴은 호출 시 멈췄던 부분과 관련된 정보를 가지고 있습니다. 그래서 1초 후에 다시 호출 되었을 때 이전에 실행했던 다음부터 실행할 수 있었던 것입니다.

 또, 재개(Resume)와 양보(yield)가 가능해, 양보(중지)(yield)하면 루틴은 종료되지 않고 대기했다가 다시 호출되는 곳에서 재개(Resume)할 수 있습니다.

 

 

 

 

 마지막으로, StartCoroutine이 있듯이, StopCoroutine도 있습니다.

StopCoroutine은 아무때나 사용하면 안되고, 무한루프에서 특정조건으로 빠져나올 때 사용해야 합니다.

유니티 메뉴얼 참고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using System.Collections;
 
public class Example : MonoBehaviour{
    // keep a copy of the executing script
    private IEnumerator coroutine;
 
    // Use this for initialization
    void Start(){
        print("Starting " + Time.time);
        coroutine = WaitAndPrint(3.0f);
        StartCoroutine(coroutine);
        print("Done " + Time.time);
    }
 
    // print to the console every 3 seconds.
    // yield is causing WaitAndPrint to pause every 3 seconds
    public IEnumerator WaitAndPrint(float waitTime){
        while (true){
            yield return new WaitForSeconds(waitTime);
            print("WaitAndPrint " + Time.time);
        }
    }
 
    void Update(){
        if (Input.GetKeyDown("space")){
            StopCoroutine(coroutine);
            print("Stopped " + Time.time);
        }
    }
}
cs

 

 

 

 

 

3. 결론

 코루틴은 다른 작업을 먼저하도록 양보(yield)하여, 코루틴 실행도중 다른 작업을 할 수 있고, 다시 재개(resume)하여 멈췄던 곳에서부터 기존 데이터로 실행할 수 있습니다.

 

 어디에 사용할까??

대기시간이 필요할 때 사용합니다. (Fade in, Fade out에도 사용합니다. Color의 알파(a)값 조절할 때 사용)

 

 Update() vs Coroutine 무엇을 사용할까??

코루틴은 프레임에 독립적인 시간이 필요할 때 사용하고, 프레임 당 처리가 필요할 땐 Update()를 사용합니다.

 

 

 

 

 

 

 

(참고)

엔진이 제공하는 데이터들과 이를 수행하는 명령들.

A.     Yield return null : 다음 프레임까지 대기

B.     Yield return new WaitForSeconds(flat) : 지정된 초(float) 만큼 대기

C.     Yield return new WaitForFixedUpdate() : 다음 물리 프레임까지 대기

D.     Yield return new WaitForEndOfFrame() : 모든 렌더링 작업이 끝날 때 까지 대기

E.     Yield return StartCoroutine(string) : 다른 코루틴이 끝날 때 까지 대기

F.      Yield return new WWW(string) : 웹 통신 작업이 끝날 때까지 대기

G.     Yield return new AsyncOperation : 비동기 작업이 끝날 때 까지 대기 (씬로딩)



참고: https://kimseunghyun76.tistory.com/304 [하루에 하나씩.....]

 

반응형

'게임 > Unity' 카테고리의 다른 글

[Unity 이론] Firebase Database  (0) 2019.11.28
[Unity 이론] Object Pooling  (0) 2019.11.26
[Unity 3D] 충돌 체크  (0) 2019.11.25
[Unity 이론] Time.deltaTime  (0) 2019.11.24
[Unity 3D] 캐릭터 움직임 구현  (1) 2019.11.23

댓글