Unity 개발일지

[Unity] 코루틴(Coroutine) 실습하기 (수정필요) 본문

Unity 개발

[Unity] 코루틴(Coroutine) 실습하기 (수정필요)

아머르 2024. 7. 16. 15:01

[확인문제]

 

1. InvokeRepeating을 통해 반복 실행 중인 메서드는 오브젝트를 비활성화하면 멈출까요?

더보기

InvokeRepeating은 MonoBehaviour와 연관되어 있기 때문에 오브젝트가 비활성화 되더라도 계속 실행된다.

(오브젝트 비활성화는 MonoBehaviour 자체의 기능을 막지 못하기 때문!)

 

오브젝트 비활성화를 통해 InvokeRepeating 메서드를 멈추고 싶다면 CancelInvoke()를 직접 호출하거나,

OnDisable() 콜백 함수를 사용하여 오브젝트 비활성화 시점에 InvokeRepeating을 중단하도록 코드를 작성한다.

 

2. Coroutine을 통해 반복 실행 중인 메서드는 오브젝트를 비활성화하면 멈출까요?

더보기

Coroutine은 MonoBehaviour와 연관되어 있으며, MonoBehaviour가 비활성화되면 Update()와 StartCoroutine() 호출도 중단되기 때문에 Coroutine을 사용하면 오브젝트 비활성화와 함께 반복 실행 메서드를 간편하게 멈출 수 있다.

1번과 2번 문제의 결론

MonoBehaviour 비활성화 시

Update, Start, OnTriggerEnter, OnCollisionEnterMonoBehaviour 이벤트 및 콜백 함수 실행 중단

하지만, InvokeRepeating, StartCoroutine, 코루틴 자체는 멈추지 않음

 

 

 

 

3. 다음 Coroutine 코드의 성능 상 문제는 무엇이 있을까요? (Hint : 메모리)

IEnumerator Fade()
{
    Color c = renderer.material.color;
    for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
    {
        c.a = alpha;
        renderer.material.color = c;
        yield return new WaitForSeconds(.1f);
    }
}
더보기

제시된 Coroutine 코드는 매 프레임마다 새로운 Color 객체를 생성하고,

renderer.material.color을 직접 수정하고 있기 때문에,

매 프레임마다 새로운 Material 인스턴스를 생성하여 메모리를 낭비할 수 있다. 

 

renderer.material 대신 renderer.sharedMaterial을 사용하거나,

미리 Material 인스턴스를 캐싱하여 사용하는 것이 좋습니다.

 

4. Unity의 Coroutine은 비동기 동작일까요? 비동기 동작이라면 하나의 스레드에서 동작하지 않는걸까요?

더보기

Unity의 Coroutine은 비동기 동작 방식을 사용한다. 하지만 이는 일반적인 멀티스레딩 비동기와는 다르다.

Unity의 Coroutine은 메인 스레드에서 실행되며, 다른 스레드에서 동작하지 않는다.

즉, Unity의 코루틴은 하나의 스레드에서 시간 분할 방식으로 동작한다.

 

따라서, 여러 Coroutine을 동시에 실행하더라도 CPU 부담을 크게 증가시키지 않고 효율적으로 처리할 수 있다.

 

 

[설명문제]

 

1. 코루틴의 동작원리와 사용해본 예시를 함께 설명해주세요.

더보기

 코루틴은 일정 시간이 지나거나 특정 조건이 충족될 때까지 실행을 중단하고, 이후 다시 재개할 수 있는 메서드이다. 이를 통해 복잡한 비동기 작업을 간단하게 처리할 수 있다. Unity에서는 IEnumerator를 반환하는 메서드를 사용하여 코루틴을 구현한다.

 

마우스를 클릭하면 멈추는 코루틴을 작성했다.

IEnumerator getWhileCor;

private void Start()
{
	getWhileCor = WhileCor();
    StartCoroutine(getWhileCor);
}

private void Update()
{
	if(Input.GetMouseButtonDown(0))
    {
    	StopCoroutine(getWhileCor);
    }
}

public IEnumerator WhileCor()
{
	for(int i = 0; i < 3; i++)
    {
    	Debig.Log(i + " 번 실행");
        
        yield return new WaitForSeconds(1.5f);
    }
    
    yield return null
    
    Debig.Log("While 코루틴 끝!");
}

 

2. Invoke와 코루틴의 차이에 대해 설명해주세요.

더보기

Invoke

특정 메서드를 일정 시간 간격으로 반복 호출하거나, 지연 후 한 번 호출하는 방식

간단한 시간 기반 이벤트 처리에 유용하다.

 

코루틴

좀 더 복잡한 비동기 작업을 관리할 수 있는 방법

특정 조건을 기다리거나, 여러 단계로 나누어진 작업을 순차적으로 처리하는 데 유용하다.

 

3. 코루틴과 멀티쓰레딩은 어떤 차이가 있는지 설명해주세요.

더보기

 

코루틴

단일 스레드에서 동작하며, 시간 분할을 통해 비동기적으로 작업을 처리한다.

주로 게임 루프 내에서 프레임 단위로 실행된다.

코루틴은 Unity의 메인 스레드에서 실행되므로, Unity의 API와 상호작용할 때 안전하게 사용할 수 있다.

 

멀티쓰레딩

여러 스레드를 사용하여 동시에 여러 작업을 수행한다.

멀티쓰레딩은 CPU의 여러 코어를 활용할 수 있어, 병렬 처리가 가능하고, 복잡한 연산이나 I/O 작업에 유리하다.

그러나 스레드 간의 동기화 문제를 관리해야 하며, Unity의 대부분의 API는 메인 스레드에서만 안전하게 호출할 수 있다.

 

 

[실습문제]

 

💡 [스크린샷 기능 만들기]

스크린샷을 찍어 파일로 저장하는 코드를 다음과 같이 작성했습니다.

private void ScreenCapture()
{
    // 스크린샷을 저장하기 위한 텍스쳐 생성
    Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);

    // 스크린의 픽셀을 텍스쳐에 적용
    texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
    texture.Apply();

    // 텍스쳐를 PNG로 저장
    byte[] bytes = texture.EncodeToPNG();
    System.IO.File.WriteAllBytes(Application.dataPath + "/Screenshot.png", bytes);

    Destroy(texture);
}

 

이를 평소와 같이 호출했는데,

private void Start()
{
    ScreenCapture();
}

 

렌더링 처리가 완료되지 않은 시점에서 ReadPixels()를 호출하여 에러가 발생합니다.

따라서 내가 스크린샷을 요청한 타이밍(프레임) 중에서도, 렌더링이 완료된 시점에 ScreenCapture() 함수가 호출되어 에러 없이 스크린샷을 저장할 수 있도록 코드를 작성해주세요. (HINT : 코루틴 사용)

using UnityEngine;
using System.Collections;

public class CaptureScreenshot : MonoBehaviour
{
    private void Start()
    {
        // TODO : 렌더링을 마친 뒤에 ScreenCapture를 호출해주는 코드 작성 
        
    }

    // TODO : 필요하다면 추가적인 메서드 작성
    
    //

    private void ScreenCapture()
    {
        // 스크린샷 저장을 위한 텍스쳐 생성
        Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);

        // 스크린 내용을 텍스쳐로 읽어들임
        texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        texture.Apply();

        // 텍스쳐를 PNG로 저장
        byte[] bytes = texture.EncodeToPNG();
        System.IO.File.WriteAllBytes(Application.dataPath + "/Screenshot.png", bytes);

        Destroy(texture);
    }
}

더보기
private void Start()
    {
        StartCoroutine(CaptureAfterRendering());
    }

    private IEnumerator CaptureAfterRendering()
    {
        // 한 프레임 대기하여 렌더링이 완료되도록 함
        yield return new WaitForEndOfFrame();
        
        ScreenCapture();
    }

 

반응형