Unity 생명주기(Unity Life Cycle)에 대해서
- Unity의 생명주기는 MonoBehaviour 기반 스크립트의 실행 순서를 나타내며, 다음 단계로 이루어짐:
- Initialization(초기화 단계): Awake, OnEnable
- Runtime Logic(런타임 로직): Start, Update, FixedUpdate, LateUpdate
- 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) 사용.
최적화에서 중요한 부분:
- 병목 현상 파악 (CPU/GPU)
- 필요 없는 연산 최소화
- 메모리 사용량 관리
- 드로우콜(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);
}