Tick 최적화 고급 기법
Game Thread 부하의 주범인 Tick을 효과적으로 최적화합니다
Tick의 비용
왜 Tick이 문제인가
Event Tick이 최적화되지 않으면 게임플레이의 유동성에 심각한 영향을 미칩니다. 언리얼 엔진은 멀티스레딩을 지원하지만, EventTick과 블루프린트는 여전히 Game Thread에서 실행됩니다.
"Tick하지 않아도 되면, Tick하지 마라." 모든 액터와 컴포넌트가 매 프레임 Tick할 필요는 없습니다.
100개 액터 × 0.1ms = 10ms → Game Thread 버짓 초과!
# 100개의 액터가 매 프레임 Tick
100 Actors × 0.1ms = 10ms / frame
# 60 FPS에서 Game Thread 버짓: ~5-6ms
# Tick만으로 이미 버짓 초과!
Tick 비활성화
가장 효과적인 최적화
완전 제거
런타임 활성화 불가
최고 효율
시작 시 비활성화
SetActorTickEnabled()로 활성화 가능
유연함
C++에서 Tick 비활성화
// 생성자에서 설정
AMyActor::AMyActor()
{
// Tick 완전 비활성화 (가장 효율적)
PrimaryActorTick.bCanEverTick = false;
// 또는 시작 시에만 비활성화 (나중에 활성화 가능)
PrimaryActorTick.bStartWithTickEnabled = false;
}
// 컴포넌트 생성자
UMyComponent::UMyComponent()
{
PrimaryComponentTick.bCanEverTick = false;
// SceneComponent 등은 기본적으로 Tick 활성화됨
// 명시적으로 비활성화해야 함!
}
SceneComponent, SkeletalMeshComponent 등은 기본적으로 Tick이 활성화되어 있습니다. 필요 없다면 명시적으로 비활성화하세요!
Tick Interval 설정
매 프레임 대신 주기적 업데이트
모든 액터가 매 프레임 업데이트될 필요는 없습니다. 멀리 있는 오브젝트나 덜 중요한 시스템은 더 낮은 빈도로 업데이트해도 됩니다.
0.5초 Interval만으로 30배 호출 횟수 감소!
// 생성자에서 설정
AMyActor::AMyActor()
{
// 0.5초마다 Tick (초당 2회)
PrimaryActorTick.TickInterval = 0.5f;
// 1초마다 Tick (초당 1회)
// PrimaryActorTick.TickInterval = 1.0f;
}
// 런타임에 동적 변경
void AMyActor::SetTickFrequency(float Interval)
{
SetActorTickInterval(Interval);
}
이벤트 기반 설계
Tick 대신 델리게이트 활용
이벤트 기반 설계는 "매 프레임 확인" 대신 "변화가 있을 때만 반응"하는 패턴입니다.
60 FPS = 60회/초 확인
변화 없어도 계속 실행
비효율적
변화 발생 시에만 호출
델리게이트로 알림
효율적
// 매 프레임 체력 확인 - 비효율적!
void AMyActor::Tick(float DeltaTime)
{
if (Health <= 0)
{
Die();
}
}
// 델리게이트 선언
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnHealthChanged);
UPROPERTY(BlueprintAssignable)
FOnHealthChanged OnHealthChanged;
// 체력 변경 시에만 호출
void AMyActor::SetHealth(float NewHealth)
{
if (Health != NewHealth)
{
Health = NewHealth;
OnHealthChanged.Broadcast(); // 변경 시에만!
if (Health <= 0)
{
Die();
}
}
}
주기적 작업은 Tick 대신 타이머를 사용하세요. Tick보다 관리가 쉽고 명확합니다.
// 주기적 작업은 타이머 사용
GetWorldTimerManager().SetTimer(
TimerHandle,
this,
&AMyActor::PeriodicUpdate,
1.0f, // 1초 주기
true // 반복
);
핵심 요약
- bCanEverTick = false: Tick이 필요 없으면 완전히 비활성화
- TickInterval: 매 프레임 대신 주기적 업데이트로 호출 횟수 대폭 감소
- 이벤트 기반 설계: 상태 확인 대신 델리게이트로 변경 시에만 반응
- 타이머 활용: 주기적 작업은 Tick 대신 SetTimer 사용
- 핵심 원칙: "Tick하지 않아도 되면, Tick하지 마라"
다음 강의에서는 Significance Manager를 활용하여 플레이어와의 거리/중요도에 따라 동적으로 업데이트 빈도를 조절하는 방법을 다룹니다.
도전 과제
배운 내용을 직접 실습해보세요
PrimaryActorTick.bCanEverTick = false로 설정된 Actor를 만들고, 필요한 경우에만 FTimerManager로 주기적 업데이트를 구현하세요. stat game에서 Tick 비용 변화를 측정합니다.
PrimaryActorTick.TickInterval을 0.1초, 0.5초, 1초로 각각 설정하여 100개 Actor의 Tick 비용을 비교하세요. SetActorTickEnabled()로 화면 밖 Actor의 Tick을 동적으로 비활성화합니다.
USignificanceManager를 활용하여 카메라 거리에 따라 Actor의 Tick 주기를 자동 조절하는 시스템을 구현하세요. 가까운 Actor는 매 프레임, 먼 Actor는 0.5초 간격으로 Tick하도록 설정합니다.