Unity 생명주기와 최적화 개념 정리

네,가능합니다 ㅣ 2024. 12. 18. 21:18

Unity 생명주기(Unity Life Cycle)에 대해서

  • Unity의 생명주기는 MonoBehaviour 기반 스크립트의 실행 순서를 나타내며, 다음 단계로 이루어짐:
    1. Initialization(초기화 단계): Awake, OnEnable
    2. Runtime Logic(런타임 로직): Start, Update, FixedUpdate, LateUpdate
    3. Destruction(종료 단계): OnDisable, OnDestroy

MonoBehaviour 클래스의 주요 메서드와 기능

  • Awake: 객체가 활성화되기 전에 호출되며, 초기화 코드에 사용됨.
  • Start: 첫 번째 프레임이 실행되기 전에 호출되며, 게임 시작 시 필요한 초기화에 사용됨.
  • OnEnable / OnDisable: 객체가 활성화/비활성화될 때 호출됨.
  • Update: 매 프레임 호출되며, 게임 로직을 업데이트함.
  • FixedUpdate: 물리 연산 업데이트에 사용됨. 고정된 시간 간격으로 호출됨.
  • LateUpdate: 모든 Update가 완료된 후 호출되며, 카메라나 UI 업데이트에 주로 사용됨.
  • OnDestroy: 객체가 파괴될 때 호출됨.

Start와 Awake의 차이:

  • Awake는 스크립트의 의존성 초기화에 사용됨.
  • Start는 필요한 초기화가 완료된 후, 실행 로직을 처리하는 데 사용됨.

사용 예시:

void Awake()
{
    // 다른 컴포넌트 참조 초기화
    rigidbody = GetComponent<Rigidbody>();
}

void Start()
{
    // 초기화 이후 로직 처리
    rigidbody.velocity = Vector3.forward * 10;
}

Update, FixedUpdate, LateUpdate의 차이

  • Update: 프레임마다 호출. 주로 게임 로직, 입력 처리에 사용됨.
  • FixedUpdate: 고정된 시간 간격으로 호출. 물리 연산이나 Rigidbody와 관련된 로직에 사용됨.
  • LateUpdate: Update 이후 호출. 카메라 추적이나 UI 정리에 사용됨.

차이 요약:

  • Update는 프레임 기반이고, FixedUpdate는 시간 기반으로 동작함.
  • LateUpdate는 이전 업데이트들이 모두 완료된 후 실행됨.

Time.deltaTime이란?

  • Time.deltaTime은 두 프레임 사이의 시간 간격을 나타내는 값으로, 프레임 속도가 일정하지 않을 때 애니메이션, 이동 등을 프레임 독립적으로 처리하기 위해 사용됨.

사용 이유:

void Update()
{
    // 프레임 독립적인 이동 처리
    transform.Translate(Vector3.forward * speed * Time.deltaTime);
}

코루틴의 동작원리와 사용 예시

  • **코루틴(Coroutine)**은 특정 조건에서 일시 정지하고 이후 다시 실행할 수 있는 함수임.
  • Unity의 StartCoroutine을 통해 실행되며, 주로 시간 지연이나 단계적인 작업 처리에 사용됨.

사용 예시:

IEnumerator SpawnObjects()
{
    while (true)
    {
        Instantiate(objectPrefab, transform.position, Quaternion.identity);
        yield return new WaitForSeconds(2f); // 2초 대기
    }
}

void Start()
{
    StartCoroutine(SpawnObjects());
}

동작 원리:

  • 코루틴은 Unity의 메인 스레드에서 실행되며, 특정 지점에서 실행을 중단하고 이후 다시 호출됨.

Invoke와 코루틴의 차이

  • Invoke: 일정 시간이 지난 후 특정 메서드를 호출하는 기능.
  • 코루틴: 특정 작업을 단계적으로 실행할 수 있는 기능.

비교:

특징 Invoke Coroutine

동작 방식 단순 시간 지연 단계적인 작업 처리 가능
유연성 낮음 높음
호출 제어 정해진 시간 후 호출 자유로운 흐름 제어

코루틴과 멀티쓰레딩의 차이

  • 코루틴은 메인 스레드에서 실행되며, 실제로 멀티쓰레드를 사용하지 않음.
  • 멀티쓰레딩은 메인 스레드와 별도로 병렬 작업을 수행함.

비교:

특징 코루틴 멀티쓰레딩

동작 방식 메인 스레드에서 실행 병렬 스레드 실행
사용 목적 시간 지연, 순차적 처리 성능 향상, 병렬 처리
데이터 접근 Unity API 접근 가능 Unity API 접근 불가

유니티 최적화 기법

  • 최적화 방법:
    • Batching: 드로우콜 수 줄이기.
    • Object Pooling: 객체를 재사용하여 생성/삭제 비용 감소.
    • LOD(Level of Detail): 카메라 거리 기반으로 간단한 모델 사용.
    • Occlusion Culling: 보이지 않는 객체 렌더링 방지.
    • Profiler 사용: 성능 병목 지점 파악.
  • 텍스처 포맷 최적화: 적절한 텍스처 압축 포맷(예: ASTC, DXT) 사용.

최적화에서 중요한 부분:

  1. 병목 현상 파악 (CPU/GPU)
  2. 필요 없는 연산 최소화
  3. 메모리 사용량 관리
  4. 드로우콜(Draw Call)에 대해 설명
  • 드로우콜: GPU가 렌더링 작업을 수행하기 위해 호출되는 명령.

최적화 방법:

  • Static Batching: 정적 객체를 묶어 렌더링 호출 감소.
  • Dynamic Batching: 작은 객체를 묶어 렌더링 호출 감소.
  • Instancing: 동일한 메시에 대해 렌더링 호출 최적화.

Find 함수 사용을 자제해야 하는 이유

  • Find 함수는 성능이 매우 낮음:
    • 씬 전체를 검색하여 첫 번째로 찾은 객체를 반환하기 때문에 검색 비용이 큼.
  • 대체 방법:
    • 객체 참조를 캐싱하거나, 필요한 경우 Tag를 사용.

Update에서 GetComponent와 캐싱을 지양해야 하는 이유

  • GetComponent는 호출 시 마다 컴포넌트 검색 비용이 발생함.
  • 캐싱을 사용하면 CPU 연산 비용을 크게 줄일 수 있음.

예시:

// 비효율적
void Update()
{
    GetComponent<Transform>().Translate(Vector3.forward);
}

// 효율적
Transform cachedTransform;

void Awake()
{
    cachedTransform = GetComponent<Transform>();
}

void Update()
{
    cachedTransform.Translate(Vector3.forward);
}