Actor 클래스에 로그(Log) 추가
언리얼 엔진에서 `UE_LOG` 매크로를 사용해 Output Log 창에 메시지를 남길 수 있다.
`BeginPlay()` 함수에 로그 추가
`Item.h`
...
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
...
protected:
...
virtual void BeginPlay() override;
};
`Item.cpp`
...
void AItem::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("My Item appears!!"));
}
- `UE_LOG(LogTemp, Warning, TEXT(" "))`
- 로그 카테고리 (Log Category) : `LogTemp`라는 임시 카테고리 사용.
- 로그 수준 (Log Level) : 다양한 로그 수준이 존재.
- `Display` : 일반적인 실행 흐름이나 상태 확인 메시지 (흰색)
- `Warning` : 예상치 못한 동작이나 잠재적인 문제 (노란색)
- `Error` : 즉시 수정이 필요한 심각한 문제 (빨간색)
- 출력할 메시지 : 원하는 문자열 입력.
고유한 카테고리 정의 후 로그 추가
프로젝트 규모가 커져 로그를 세세하게 분류해야 할 경우 `DEFINE_LOG_CATEGORY`를 사용해 고유한 카테고리를 정의할 수 있다.
`.h`
#pargma once
...
DECLARE_LOG_CATEGORY_EXTERN(LogMine, Warning, All);
UCLASS()
class ...
{
...
};
- `DECLARE_LOG_CATEGORY_EXTERN(LogMine, Warning, All);`
- 헤더 파일에서 로그 카테고리를 선언
- `LogMine` : 카테고리 이름 (사용자 지정)
- `Warning` : 이 카테고리 사용 시 `Warning` 이상 로그만 출력
- `All` : 필요하면 모든 로그 활성화 허용
`.cpp`
#include "Item.h"
DEFINE_LOG_CATEGORY(LogMine);
AItem::AItem()
{
...
}
void AItem::BeginPlay()
{
...
UE_LOG(LogMine, Error, TEXT("This is Mine!!"));
}
빌드 후 언리얼 에디터에서 Actor가 배치된 상태로 실행하면 아래와 같이 로그가 출력되는 것을 볼 수 있다.
언리얼 엔진 Actor의 라이프 사이클
액터는 언제든 생성되고 파괴될 수 있다. 이를 라이프 사이클이라 부르며, 이해하면 게임 로직을 좀 더 효율적이고 안정적으로 작성할 수 있다.
액터 라이프 사이클을 알아야 하는 이유
- 초기화 시점 결정
- 생성자, `PostInitializeComponents`, `BeginPlay` 등 언제 호출되는지 알아야 적절한 곳에 코드를 배치할 수 있다.
- 성능 관리
- 매 프레임마다 호출되는 `Tick` 함수는 비용이 크다. 따라서 필요한 액터만 활성화하거나 이벤트 기반으로 전환해 최적화해야 한다.
- 리소스 정리
- 액터가 사라질 때 메모리를 해제하거나 특정 상태를 저장해야 할 수 있다. 적절한 시점에 정리 작업을 하지 않으면 메모리 누수나 예외가 발생할 수 있다.
주요 라이프 사이클 함수
액터는 생성 → 초기화 → 월드 배치 → 실행 → 제거 순으로 동작하며, 이를 지원하기 위해 여러 함수가 자동 호출된다.
- 생성자 (Constructor)
- 호출 시점 : C++ 클래스 객체가 메모리에 생성될 때 한 번만 호출
- 액터가 월드에 완전히 등록되지 않은 상태로, 다른 액터나 월드 관련 기능을 안전하게 호출하기 어렵다.
- 주로 `CreateDefaultSubobject` 등을 사용해 컴포넌트 생성 및 초기 변수 세팅에 활용한다.
- PostInitializecomponents()
- 호출 시점 : 액터의 모든 컴포넌트가 생성 및 초기화된 후 자동 호출
- 각 컴포넌트가 준비된 상태 → 컴포넌트 간 상호작용이 필요한 코드를 배치하기 좋다.
- 보통 생성자에서 단순 "생성 / 할당"만 하고 `PostInitializecomponents()`에서 컴포넌트 사이의 의존 관계를 설정한다.
- BeginPlay()
- 호출 시점 : Play In Editor(PIE)나 런타임에서 게임이 시작될 때, 혹은 이미 실행 중인 게임에 `SpawnActor` 등 새 액터가 생성될 때 한 번만 호출
- 이미 월드와 다른 액터들이 준비된 상태이므로, 자유롭게 상호작용이 가능하다.
- AI, 게임 모드, 플레이어 컨트롤러 등 다른 시스템과 연동도 주로 BeginPlay에서 초기화하며, 타이머, Delegate(Event) 바인딩 등을 시작하기에도 적합하다.
- Tick(float DeltaTime)
- 호출 시점 : 매 프레임마다 호출 (액터의 `PrimaryActorTick.bCanEverTick = true;` 설정 필요
- 실시간 업데이트가 필요한 로직을 처리한다.
- 이벤트 기반으로 전환할 수 있는 부분은 `Tick`을 사용하지 않는 편이 성능에 유리하다.
- Destroyed()
- 호출 시점 : `Destroy()` 함수를 직접 호출하여 액터를 제거할 때 직전에 호출 (단, 레벨 전환이나 게임 종료 시 종종 건너뛰어짐)
- `Destroyed()`가 불린 뒤 최종적으로 `EndPlay()`도 함께 호출된다.
- 수동으로 액터를 제거할 때, 마지막 정리 코드를 넣을 수 있는 곳이다. 하지만 종종 건너뛰는 케이스가 있기 때문에 중요한 정리를 모두 의존하면 안된다.
- 정리할 자원 예시
- 수동으로 할당한 메모리 : `new` 또는 동적 할당한 오브젝트가 있다면 여기서 `delete` 하거나 해제
- 스폰된 자식 액터 : 이 액터가 생성한 다른 액터나 컴포넌트 중 자동으로 해제되지 않는 것이 있다면 제거
- Delegate / Event 바인딩 : 게임 전역적 또는 외부 클래스에 바인딩해 둔 델리게이트가 있다면 해제
- 사운드 / 파티클 등 : 필요 시 이 액터가 재생 중인 사운드나 파티클을 수동으로 정리
- EndPlay(const EEndPlayReason::Type EndPlayReason)
- 호출 시점 : 액터가 더 이상 월드에서 활동하지 않을 때 호출 (파괴, 레벨 전환, 게임 종료 등)
- `EEndPlayReason::Type`으로 어떤 이유로 EndPlay가 호출되었는지 구분한다.
- 게임 종료나 레벨 언로드 같은 상황에서도 `EndPlay()`는 호출 보장이 높지만, `Destroyed()`는 건너뛸 수 있다.
- 따라서 중요한 정리 로직은 `EndPlay()`에 넣는 것이 보다 안전하다.
- 정리할 자원 예시
- 타이머 : `GetWorldTimeManager().ClearTime(...)`와 같이 타이머를 정리
- 동적 할당 리소스 : 해제되지 않은 동적 메모리가 있다면 정리
- 데이터 저장 : 진행 상황을 파일 혹은 DB에 저장하거나, 상위 시스템에 콜백을 보내는 로직도 처리 가능
라이프 사이클 함수에 로그 추가
헤더에 함수 선언
...
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
...
protected:
...
virtual void PostInitializeComponents() override;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
virtual void Destroyed() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};
CPP 파일에 구현 및 출력
...
AItem::AItem()
{
PrimaryActorTick.bCanEverTick = true;
...
UE_LOG(LogMine, Warning, TEXT("%s Constructor"), *GetName());
}
void AItem::PostInitializeComponents()
{
Super::PostInitializeComponents();
UE_LOG(LogMine, Warning, TEXT("%s PostInitializeComponets"), *GetName());
}
void AItem::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("My Item appears!!"));
UE_LOG(LogMine, Error, TEXT("This is Mine!!"));
}
void AItem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AItem::Destroyed()
{
UE_LOG(LogMine, Warning, TEXT("%s Destroyed"), *GetName());
Super::Destroyed();
}
void AItem::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UE_LOG(LogMine, Warning, TEXT("%s EndPlay"), *GetName());
Super::EndPlay(EndPlayReason);
}
빌드를 하고 언리얼 에디터에서 Actor를 추가한 후 실행해보면 아래와 같은 로그를 볼 수 있다.
'본 캠프 > Note' 카테고리의 다른 글
[C++과 Unreal Engine으로 3D 게임 개발] C++ 클래스와 리플렉션 시스템 활용 (0) | 2025.09.17 |
---|---|
[C++과 Unreal Engine으로 3D 게임 개발] Tick 함수로 Transform 조정 (1) | 2025.09.17 |
[C++과 Unreal Engine으로 3D 게임 개발] Actor 클래스에 컴포넌트 추가하기 (0) | 2025.09.15 |
[C++과 Unreal Engine으로 3D 게임 개발] C++ Actor 클래스 생성 및 삭제 (0) | 2025.09.15 |
[게임 개발자를 위한 C++ 문법] 디자인 패턴 (0) | 2025.08.25 |