PART 10 - 강의 4/6

Visual Logger

AI/게임플레이 시각화, 히스토리 분석, 타임라인 디버깅

01

Visual Logger란?

시간에 따른 게임 상태를 시각적으로 기록하고 분석

핵심 기능

타임라인 기록

시간순으로 이벤트와 상태 변화 기록

3D 시각화

위치, 경로, 영역을 3D 공간에 표시

히스토리 재생

과거 시점으로 이동하여 상태 확인

필터링

카테고리와 객체별 필터링

에디터에서 열기

Visual Logger 접근

Window - Developer Tools - Visual Logger

또는 콘솔: VisLog

활성화

Enabling Visual Logger // 프로젝트 설정에서 활성화 (DefaultEngine.ini) [/Script/Engine.Engine] bEnableVisualLoggerRecording=true // 또는 런타임에 활성화 FVisualLogger::Get().SetIsRecording(true); // 콘솔 명령 VisLog record VisLog stop
02

Visual Logger 기본 사용법

UE_VLOG 매크로를 사용한 시각적 로깅

기본 로깅

Basic Visual Logging #include "VisualLogger/VisualLogger.h" // 텍스트 로그 (타임라인에 표시) UE_VLOG(Owner, LogAI, Log, TEXT("AI state changed to: %s"), *StateName); // 조건부 로그 UE_CVLOG(bCondition, Owner, LogAI, Warning, TEXT("Condition met!")); // Owner는 로그를 소유할 UObject (주로 this) void AAIController::UpdateBehavior() { UE_VLOG(this, LogAI, Log, TEXT("Updating behavior for %s"), *GetPawn()->GetName()); }

위치 로깅

Location Logging // 점 표시 UE_VLOG_LOCATION(Owner, LogAI, Log, GetActorLocation(), // 위치 50.0f, // 반경 FColor::Green, // 색상 TEXT("Current Position")); // 화살표 (방향 표시) UE_VLOG_ARROW(Owner, LogAI, Log, StartLocation, EndLocation, FColor::Yellow, TEXT("Movement Direction")); // 세그먼트 (선) UE_VLOG_SEGMENT(Owner, LogAI, Log, StartPoint, EndPoint, FColor::Blue, TEXT("Path Segment")); // 원뿔 (시야각 등) UE_VLOG_CONE(Owner, LogAI, Log, EyeLocation, // 원점 ForwardVector, // 방향 500.0f, // 길이 FMath::DegreesToRadians(45.0f), // 각도 FColor::Cyan, TEXT("Vision Cone"));

도형 로깅

Shape Logging // 박스 UE_VLOG_BOX(Owner, LogAI, Log, BoxCenter, BoxExtent, // FVector(50, 50, 100) FColor::Orange, TEXT("Detection Area")); // 회전된 박스 FQuat Rotation = GetActorQuat(); UE_VLOG_OBOX(Owner, LogAI, Log, FBox(BoxMin, BoxMax), FMatrix(Rotation), FColor::Purple, TEXT("Oriented Box")); // 캡슐 UE_VLOG_CAPSULE(Owner, LogAI, Log, CapsuleCenter, 50.0f, // 반경 100.0f, // 반높이 FQuat::Identity, FColor::Magenta, TEXT("Collision Capsule")); // 원통 UE_VLOG_CYLINDER(Owner, LogAI, Log, CylinderStart, CylinderEnd, 100.0f, // 반경 FColor::White, TEXT("Cylinder Area")); // 메시 (복잡한 형태) TArray<FVector> Vertices; TArray<int32> Indices; UE_VLOG_MESH(Owner, LogAI, Log, Vertices, Indices, FColor::Red, TEXT("Custom Mesh"));
03

AI 디버깅 실전 예제

Visual Logger를 활용한 AI 시스템 디버깅

AI Controller 로깅

AIController Visual Logging void AMyAIController::Tick(float DeltaTime) { Super::Tick(DeltaTime); APawn* ControlledPawn = GetPawn(); if (!ControlledPawn) return; // 현재 위치 로깅 UE_VLOG_LOCATION(this, LogAI, Verbose, ControlledPawn->GetActorLocation(), 30.0f, FColor::Green, TEXT("AI Position")); // 현재 상태 로깅 UE_VLOG(this, LogAI, Log, TEXT("State: %s | Target: %s | Health: %.0f%%"), *UEnum::GetValueAsString(CurrentState), *GetNameSafe(CurrentTarget), HealthPercent); // 타겟이 있으면 시선 표시 if (CurrentTarget) { UE_VLOG_ARROW(this, LogAI, Log, ControlledPawn->GetActorLocation(), CurrentTarget->GetActorLocation(), FColor::Red, TEXT("Target: %s"), *CurrentTarget->GetName()); } // 감지 범위 표시 UE_VLOG_CYLINDER(this, LogAI, Verbose, ControlledPawn->GetActorLocation(), ControlledPawn->GetActorLocation() + FVector(0, 0, 200), DetectionRadius, FColor(100, 100, 255, 50), TEXT("Detection Range: %.0f"), DetectionRadius); } void AMyAIController::OnPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) { if (Stimulus.WasSuccessfullySensed()) { UE_VLOG(this, LogAI, Log, TEXT("Detected: %s at distance %.1f"), *Actor->GetName(), FVector::Distance(GetPawn()->GetActorLocation(), Actor->GetActorLocation())); UE_VLOG_SEGMENT(this, LogAI, Log, GetPawn()->GetActorLocation(), Actor->GetActorLocation(), FColor::Yellow, TEXT("Detection")); } else { UE_VLOG(this, LogAI, Log, TEXT("Lost sight of: %s"), *Actor->GetName()); } }

경로 탐색 로깅

Navigation Path Logging void AMyAIController::MoveToTarget(AActor* Target) { if (!Target) return; // 경로 요청 FAIMoveRequest MoveRequest; MoveRequest.SetGoalActor(Target); FNavPathSharedPtr Path; UPathFollowingComponent* PathComp = GetPathFollowingComponent(); if (FindPathForMoveRequest(MoveRequest, Path) && Path.IsValid()) { // 전체 경로 로깅 const TArray<FNavPathPoint>& PathPoints = Path->GetPathPoints(); UE_VLOG(this, LogAI, Log, TEXT("Path found with %d points to %s"), PathPoints.Num(), *Target->GetName()); // 경로 시각화 for (int32 i = 0; i < PathPoints.Num() - 1; ++i) { UE_VLOG_SEGMENT(this, LogAI, Log, PathPoints[i].Location, PathPoints[i + 1].Location, FColor::Cyan, TEXT("Path %d"), i); UE_VLOG_LOCATION(this, LogAI, Verbose, PathPoints[i].Location, 20.0f, FColor::Blue, TEXT("WP %d"), i); } // 목적지 표시 UE_VLOG_LOCATION(this, LogAI, Log, Target->GetActorLocation(), 50.0f, FColor::Green, TEXT("Destination")); MoveTo(MoveRequest); } else { UE_VLOG(this, LogAI, Warning, TEXT("Path not found to %s"), *Target->GetName()); } }
04

히스토그램과 그래프 로깅

수치 데이터의 시간별 변화 시각화

히스토그램 로깅

Histogram Logging // 히스토그램 데이터 로깅 void AMyCharacter::Tick(float DeltaTime) { Super::Tick(DeltaTime); // 체력을 히스토그램으로 표시 UE_VLOG_HISTOGRAM(this, LogCombat, Log, "Health", // 그래프 이름 FName(TEXT("CurrentHealth")), // 데이터 이름 CurrentHealth); // 값 // 여러 값을 같은 그래프에 UE_VLOG_HISTOGRAM(this, LogCombat, Log, "Resources", FName(TEXT("Stamina")), CurrentStamina); UE_VLOG_HISTOGRAM(this, LogCombat, Log, "Resources", FName(TEXT("Mana")), CurrentMana); // 속도 추적 float Speed = GetVelocity().Size(); UE_VLOG_HISTOGRAM(this, LogCombat, Verbose, "Movement", FName(TEXT("Speed")), Speed); } // 전투 데미지 히스토그램 void AMyCharacter::TakeDamage(float Damage) { UE_VLOG_HISTOGRAM(this, LogCombat, Log, "Damage", FName(TEXT("DamageTaken")), Damage); UE_VLOG(this, LogCombat, Log, TEXT("Took %.1f damage, health: %.1f -> %.1f"), Damage, CurrentHealth, CurrentHealth - Damage); }

커스텀 데이터 스냅샷

Custom Snapshot // 복잡한 상태를 구조화된 형태로 로깅 void AMyAIController::LogCurrentState() { #if ENABLE_VISUAL_LOG if (FVisualLogger::IsRecording()) { FVisualLogEntry* Entry = FVisualLogger::Get().GetEntryToWrite( this, LogAI, ELogVerbosity::Log); if (Entry) { // 카테고리 추가 Entry->AddText(TEXT("=== AI State Snapshot ===")); Entry->AddText(FString::Printf(TEXT("State: %s"), *UEnum::GetValueAsString(CurrentState))); Entry->AddText(FString::Printf(TEXT("Target: %s"), *GetNameSafe(CurrentTarget))); Entry->AddText(FString::Printf(TEXT("Threat Level: %.2f"), ThreatLevel)); // 태그로 상태 표시 Entry->AddTag(TEXT("AIState"), UEnum::GetValueAsString(CurrentState)); // 위치 정보 추가 if (GetPawn()) { Entry->AddElement( FVisualLogShapeElement::MakePoint( GetPawn()->GetActorLocation(), FColor::Green, TEXT("AI Position"), 30.0f)); } } } #endif }
05

Visual Log 저장 및 분석

로그 데이터의 저장, 공유, 오프라인 분석

로그 파일 관리

File Operations // 로그 저장 FVisualLogger::Get().SaveToFile(TEXT("MyVisualLog.vlog")); // 특정 경로에 저장 FString SavePath = FPaths::ProjectSavedDir() / TEXT("VisualLogs"); FVisualLogger::Get().SaveToFile(SavePath / TEXT("Debug_Session_1.vlog")); // 콘솔 명령 // VisLog save MyLog - 로그 저장 // VisLog load MyLog - 로그 로드 // VisLog clear - 로그 클리어 // 자동 저장 설정 (DefaultEngine.ini) [/Script/Engine.VisualLoggerSettings] bAutoSaveOnStopRecording=true AutoSaveFolder=Saved/VisualLogs

디버깅 워크플로우

1. 기록

  • 에디터에서 Visual Logger 열기
  • Record 버튼 클릭
  • 게임 플레이

2. 분석

  • 타임라인 스크러빙
  • 객체별 필터링
  • 카테고리별 필터링

3. 저장

  • .vlog 파일로 저장
  • 팀원과 공유
  • 버그 리포트 첨부

4. 재분석

  • 저장된 로그 로드
  • 오프라인 분석
  • 히스토리 비교

Visual Logger 설정

Configuration // 프로젝트 설정 (DefaultEngine.ini) [/Script/Engine.Engine] bEnableVisualLoggerRecording=true // Shipping 빌드에서 비활성화 #if !UE_BUILD_SHIPPING bEnableVisualLoggerRecording=true #else bEnableVisualLoggerRecording=false #endif // 코드에서 조건부 사용 #if ENABLE_VISUAL_LOG UE_VLOG(this, LogAI, Log, TEXT("Debug info")); #endif
Visual Logger 활용 팁
  • AI 행동 문제 디버깅에 특히 효과적
  • 네트워크 동기화 문제 분석에 유용
  • QA팀이 버그를 재현할 때 .vlog 파일 요청
  • Verbose 로그는 필요할 때만 활성화
SUMMARY

핵심 요약

  • Visual Logger는 시간에 따른 게임 상태를 시각적으로 기록하고 분석
  • UE_VLOG: 텍스트, UE_VLOG_LOCATION: 위치, UE_VLOG_SEGMENT: 선
  • UE_VLOG_BOX, UE_VLOG_CAPSULE 등으로 3D 도형 표시
  • UE_VLOG_HISTOGRAM으로 수치 데이터의 시간별 변화 그래프화
  • .vlog 파일로 저장하여 팀원과 공유 및 오프라인 분석 가능
다음 강의 예고

다음 강의에서는 DrawDebug 함수들을 사용하여 런타임에 직접 시각적 디버깅 정보를 그리는 방법을 학습합니다.

PRACTICE

도전 과제

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

실습 1: Visual Logger 기본 사용

UE_VLOG, UE_VLOG_LOCATION, UE_VLOG_SEGMENT를 사용하여 RPG AI의 이동 경로, 타겟 위치, 감지 범위를 Visual Logger에 기록하세요. Window > Visual Logger에서 재생하세요.

실습 2: Visual Logger 카테고리별 기록

AI 행동(이동, 공격, 회피), 전투(데미지, 힐), 퀘스트(트리거, 진행) 카테고리별로 Visual Logger 데이터를 분리하세요. 시간축에서 특정 이벤트를 찾아 디버깅하세요.

심화 과제: 커스텀 Visual Logger 확장

FVisualLogEntry에 커스텀 데이터를 추가하여, RPG 특화 디버그 정보(현재 어빌리티 스택, 버프/디버프 목록, 어그로 테이블)를 Visual Logger에 표시하세요.