PART 2 · 강의 1/4

Tick 최적화 고급 기법

Game Thread 부하의 주범인 Tick을 효과적으로 최적화합니다

01

Tick의 비용

왜 Tick이 문제인가

Event Tick이 최적화되지 않으면 게임플레이의 유동성에 심각한 영향을 미칩니다. 언리얼 엔진은 멀티스레딩을 지원하지만, EventTick과 블루프린트는 여전히 Game Thread에서 실행됩니다.

핵심 원칙

"Tick하지 않아도 되면, Tick하지 마라." 모든 액터와 컴포넌트가 매 프레임 Tick할 필요는 없습니다.

📊 Tick 비용이 프레임 버짓에 미치는 영향
60 FPS 버짓:
16.66ms
Game Thread:
~5-6ms
100 Actors Tick:
10ms ⚠️

100개 액터 × 0.1ms = 10ms → Game Thread 버짓 초과!

Tick 비용 계산 예시
코드 보기
# 100개의 액터가 매 프레임 Tick 100 Actors × 0.1ms = 10ms / frame # 60 FPS에서 Game Thread 버짓: ~5-6ms # Tick만으로 이미 버짓 초과!
02

Tick 비활성화

가장 효과적인 최적화

⚙️ Tick 비활성화 옵션 비교
bCanEverTick = false
🚫

완전 제거
런타임 활성화 불가
최고 효율

bStartWithTickEnabled = false
⏸️

시작 시 비활성화
SetActorTickEnabled()로 활성화 가능
유연함

C++에서 Tick 비활성화

⭐ Actor Tick 비활성화
코드 숨기기
// 생성자에서 설정 AMyActor::AMyActor() { // Tick 완전 비활성화 (가장 효율적) PrimaryActorTick.bCanEverTick = false; // 또는 시작 시에만 비활성화 (나중에 활성화 가능) PrimaryActorTick.bStartWithTickEnabled = false; }
Component Tick 비활성화
코드 보기
// 컴포넌트 생성자 UMyComponent::UMyComponent() { PrimaryComponentTick.bCanEverTick = false; // SceneComponent 등은 기본적으로 Tick 활성화됨 // 명시적으로 비활성화해야 함! }
주의: SceneComponent

SceneComponent, SkeletalMeshComponent 등은 기본적으로 Tick이 활성화되어 있습니다. 필요 없다면 명시적으로 비활성화하세요!

03

Tick Interval 설정

매 프레임 대신 주기적 업데이트

모든 액터가 매 프레임 업데이트될 필요는 없습니다. 멀리 있는 오브젝트나 덜 중요한 시스템은 더 낮은 빈도로 업데이트해도 됩니다.

⚡ Tick Interval에 따른 호출 횟수 (100 Actors, 60 FPS)
매 프레임 Tick:
6000 calls/sec
0.5초 Interval:
200 calls/sec
1초 Interval:
100 calls/sec

0.5초 Interval만으로 30배 호출 횟수 감소!

⭐ Tick Interval 설정
코드 숨기기
// 생성자에서 설정 AMyActor::AMyActor() { // 0.5초마다 Tick (초당 2회) PrimaryActorTick.TickInterval = 0.5f; // 1초마다 Tick (초당 1회) // PrimaryActorTick.TickInterval = 1.0f; } // 런타임에 동적 변경 void AMyActor::SetTickFrequency(float Interval) { SetActorTickInterval(Interval); }
04

이벤트 기반 설계

Tick 대신 델리게이트 활용

이벤트 기반 설계는 "매 프레임 확인" 대신 "변화가 있을 때만 반응"하는 패턴입니다.

🔄 Tick 폴링 vs 이벤트 기반
❌ Tick 폴링
🔁 매 프레임 확인

60 FPS = 60회/초 확인
변화 없어도 계속 실행
비효율적

✅ 이벤트 기반
📢 변경 시에만 반응

변화 발생 시에만 호출
델리게이트로 알림
효율적

❌ 나쁜 예: Tick에서 상태 확인
코드 보기
// 매 프레임 체력 확인 - 비효율적! 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 // 반복 );
SUMMARY

핵심 요약

  • bCanEverTick = false: Tick이 필요 없으면 완전히 비활성화
  • TickInterval: 매 프레임 대신 주기적 업데이트로 호출 횟수 대폭 감소
  • 이벤트 기반 설계: 상태 확인 대신 델리게이트로 변경 시에만 반응
  • 타이머 활용: 주기적 작업은 Tick 대신 SetTimer 사용
  • 핵심 원칙: "Tick하지 않아도 되면, Tick하지 마라"
다음 단계

다음 강의에서는 Significance Manager를 활용하여 플레이어와의 거리/중요도에 따라 동적으로 업데이트 빈도를 조절하는 방법을 다룹니다.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Tick 비활성화 실습

PrimaryActorTick.bCanEverTick = false로 설정된 Actor를 만들고, 필요한 경우에만 FTimerManager로 주기적 업데이트를 구현하세요. stat game에서 Tick 비용 변화를 측정합니다.

실습 2: Tick 주기 조절

PrimaryActorTick.TickInterval을 0.1초, 0.5초, 1초로 각각 설정하여 100개 Actor의 Tick 비용을 비교하세요. SetActorTickEnabled()로 화면 밖 Actor의 Tick을 동적으로 비활성화합니다.

심화 과제

USignificanceManager를 활용하여 카메라 거리에 따라 Actor의 Tick 주기를 자동 조절하는 시스템을 구현하세요. 가까운 Actor는 매 프레임, 먼 Actor는 0.5초 간격으로 Tick하도록 설정합니다.