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