1. 로그 출력하기
UE_LOG(LogTemp, Warning, TEXT("My Log!"));
- LogTemp : 로그 카테고리 인자를 입력.
- Warning : 로그 수준
- Display: 일반적인 실행 흐름이나 상태 확인 메시지 (흰색)
- Warning: 예상치 못한 동작이나 잠재적인 문제 (노란색)
- Error: 즉시 수정이 필요한 심각한 문제 (빨간색)
2. 로그 카테고리 만들기
2-1. 헤더파일에 선언
DECLARE_LOG_CATEGORY_EXTERN(LogSparta, Warning, All);
- 헤더파일에 이와 같이 선언.
- LogSparta : 카테고리 이름 (사용자가 지정).
- Warning : 이 카테고리를 사용할 때 기본적으로 Warning 이상의 로그만 출력하도록 설정
- All : 필요하면 나중에 모든 로그를 활성화할 수 있도록 허용
2-2. cpp 파일에 구현
DEFINE_LOG_CATEGORY(LogSparta);
이렇게 정의해놓은 로그를 다른 클래스에도 사용하려고 할 때, 그 정의가 포함된 클래스의 헤더를 추가해야되기 때문에,
이렇게 로그만 정의해놓는 클래스를 따로 생성하는게 일반적이다.
3. Actor의 라이프 사이클
3-1. 액터 라이프 사이클을 알아야 하는 이유
- 초기화 시점 결정
- 생성자 (Constructor), PostInitializeComponents, BeginPlay 등이 각각 언제 호출되는지 알아야 적절한 곳에 코드를 배치할 수 있다.
- 예) 컴포넌트 생성(CreateDefaultSubobject)은 생성자에서, 다른 액터 참조나 월드 접근은 BeginPlay에서 처리.
- 성능 관리
- 매 프레임마다 호출되는 Tick 함수는 비용이 클 수 있다.
- 따라서 필요한 액터만 Tick을 활성화하거나 이벤트 기반으로 전환해 최적화해야 한다.
- 리소스 정리
- 액터가 사라질 때 (EndPlay, Destroyed 등) 메모리를 해제하거나 특정 상태를 저장해야 할 수 있다.
- 적절한 시점에 필요한 정리 작업을 하지 않으면 메모리 누수나 예외 상황이 발생할 수 있다.
3-2. 주요 라이프 사이클 함수
언리얼 엔진의 Actor는 생성 → 초기화 → 월드 배치 → Tick(실행) → 제거 순으로 동작하며, 이를 지원하기 위해 여러 함수가 자동 호출된다.
1) 생성자 (Constructor)
- 호출 시점 : C++ 클래스 객체가 메모리에 생성될 때 딱 한 번 호출된다.
- 아직 액터가 월드 (World)에 완전히 등록되지 않은 상태이므로, 다른 액터나 월드 관련 기능을 안전하게 호출하기 어렵다.
- 주로 CreateDefaultSubobject 등을 사용해 컴포넌트 생성 및 초기 변수 세팅에 활용한다.
2) PostInitializeComponents( )
- 호출 시점 : 액터의 모든 컴포넌트가 생성·초기화된 뒤 자동 호출된다.
- 각 컴포넌트가 이미 준비된 상태이므로, 컴포넌트 간 상호작용(예: 서로 다른 컴포넌트 참조 설정)이 필요한 코드를 배치하기 좋다.
- 보통 생성자에서는 단순한 ‘생성/할당’만 하고, PostInitializeComponents()에서 컴포넌트들 사이의 의존 관계를 설정한다.
3) BeginPlay( )
- 호출 시점 : Play In Editor (PIE)나 런타임에서 게임이 시작될 때, 혹은 이미 실행 중인 게임에 SpawnActor 등으로 새 액터가 생성될 때 한 번 호출된다.
- 이 시점에는 이미 월드와 다른 액터들이 준비된 상태이므로, 자유롭게 상호작용이 가능하다.
- AI, 게임 모드, 플레이어 컨트롤러 등 다른 시스템과 연동도 주로 BeginPlay에서 초기화 한다.
- 타이머, Delegate(Event) 바인딩 등을 시작하기에도 적합하다.
4) Tick (float DeltaTime)
- 호출시점 : 매 프레임마다 호출된다. (액터의 PrimaryActorTick.bCanEverTick = true; 설정 필요)
- 실시간 업데이트가 필요한 로직 (캐릭터 이동, 물리 연산, 카메라 추적 등)을 처리한다.
- 이벤트 (Event) 기반으로 전환할 수 있는 부분은 Tick을 사용하지 않는 편이 성능에 유리.
5) Destroyed( )
- 호출 시점 : Destroy() 함수를 직접 호출하여 액터를 제거할 때 직전에 호출된다. 다만 레벨 전환이나 게임 종료 시에는 종종 건너뛰어지기도 하므로 (상황마다 엔진 동작이 달라짐), 절대적으로 보장되는 것은 아니다.
- Destroyed()가 불린 뒤에는 최종적으로 EndPlay()도 함께 호출된다.
- 수동으로 액터를 제거할 때, 마지막 정리 코드를 넣을 수 있는 곳이다.
- But, 게임 종료나 레벨 언로드 시에는 호출되지 않을 수 있으므로, 모든 중요한 정리를 Destroyed()에만 의존하면 놓치는 케이스가 생길 수 있다.
- 정리할 자원 예시
- 수동 할당한 메모리 : new 또는 동적 할당한 오브젝트가 있다면 여기서 delete하거나 해제.
- 스폰된 자식 액터 : 이 액터가 생성한 다른 액터나 컴포넌트 중, 자동으로 해제되지 않는 것이 있다면 제거 처리.
- Delegate / Event 바인딩 : 게임 전역적 또는 외부 클래스에 바인딩해둔 델리게이트가 있다면 해제.
- 사운드/파티클 등 : 필요 시 이 액터가 재생 중인 사운드나 파티클을 수동으로 정리.
6) EndPlay(const EEndPlayReason::Type EndPlayReason)
- 호출 시점: 액터가 더 이상 월드에서 활동하지 않게 될 때 호출. (파괴, 레벨 전환, 게임 종료 등)
- EEndPlayReason::Type으로 어떤 이유로 EndPlay가 호출되었는지(파괴, 레벨 언로드, 게임 종료 등)를 구분.
- 게임 종료나 레벨 언로드 같은 상황에서도 EndPlay()는 상대적으로 호출 보장이 높지만, Destroyed()는 건너뛸 수 있다.
- 따라서 중요한 정리 로직 (자원 해제, Timer 해제, 상태 저장 등)은 EndPlay()에 넣는 것이 보다 안전.
- 정리할 자원 예시
- 타이머: GetWorldTimerManager().ClearTimer(…) 와 같이 타이머를 정리.
- 동적 할당 리소스: 여전히 해제되지 않은 동적 메모리가 남아 있다면 여기서 정리.
- 데이터 저장: 게임 진행 상황 (점수, 인벤토리 등)을 파일/DB에 저장하거나, 상위 시스템에 콜백을 보내는 로직도 EndPlay에서 처리 가능.
'언리얼엔진5 공부' 카테고리의 다른 글
| 언리얼엔진 C++ 챕터2 - 1. Character_클래스를_활용한_캐릭터_구현하기 (2) | 2025.09.19 |
|---|---|
| 언리얼엔진 C++ 챕터1 - 5. C++ 클래스와 리플렉션 시스템 활용하기 (0) | 2025.09.18 |
| 언리얼엔진 C++ 챕터1 - 4. Tick 함수로 Actor의 Transform 조정하기 (1) | 2025.09.17 |
| 언리얼엔진 C++ 챕터1 - 2. Actor 클래에 컴포넌트 추가하기 (0) | 2025.09.15 |
| 언리얼엔진 C++ 챕터1 - 1. 액터 소스파일의 기본 구성 (0) | 2025.09.03 |