학습일지/언리얼

차량 AI 카메라 무빙 + 사운드 구현기

Tsukino Ren 2026. 5. 19. 21:19

오늘 한 것

급정거 카메라 피드백, 브레이크 사운드, 엔진 사운드 구현했다.


급정거 카메라 무빙 - 델리게이트 + Timeline

구조

C++에서 델리게이트 선언 → BTT에서 브로드캐스트 → 블루프린트에서 Timeline으로 카메라 이동

C++ 추가

// CityVehiclePawn.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEmergencyBrake, float, BrakeValue);

UPROPERTY(BlueprintAssignable, Category="Camera")
FOnEmergencyBrake OnEmergencyBrake;

UFUNCTION(BlueprintCallable, Category="Camera")
void TriggerEmergencyBrakeCamera(float BrakeValue);

bool bCameraTriggered = false;

// CityVehiclePawn.cpp
void ACityVehiclePawn::TriggerEmergencyBrakeCamera(float BrakeValue)
{
    if (bCameraTriggered) return;
    bCameraTriggered = true;
    OnEmergencyBrake.Broadcast(BrakeValue);
}

// ResumeMovement에 추가
bCameraTriggered = false;

삽질 포인트

  • BlueprintAssignable 델리게이트는 블루프린트에서 직접 브로드캐스트 불가
  • 반드시 BlueprintCallable 함수로 감싸서 호출해야 함
  • BTT는 매 프레임 실행되므로 bCameraTriggered 플래그로 한 번만 실행되게 해야 함

블루프린트 Timeline 설정

Break Moving Camera (Custom Event)
    → Timeline Play from Start
        Update → SET BackSpringArm Target Arm Length = New Track 0

Timeline 커브 키값 (바운스 느낌):

0.0초 : 650 (시작)
0.3초 : 250 (확 쏠림)
1.2초 : 750 (살짝 튕김)
1.5초 : 650 (복귀)

Delay 노드는 함수 안에서 못 씀. 반드시 Custom Event 안에서 써야 함!

BTT 연결

Emergency Stop
    → Trigger Emergency Brake Camera (Target = As City Vehicle Pawn, Brake Value = 1.0)
    → Finish Execute

브레이크 사운드

C++ 구현

// CityVehiclePawn.h
UPROPERTY(EditAnywhere, Category="Sound")
TArray<TObjectPtr<USoundBase>> BrakeSounds;

// TriggerEmergencyBrakeCamera에 추가
if (BrakeSounds.Num() > 0)
{
    int32 Index = FMath::RandRange(0, BrakeSounds.Num() - 1);
    UGameplayStatics::PlaySoundAtLocation(this, BrakeSounds[Index], GetActorLocation());
}
  • 배열로 여러 사운드 등록해서 랜덤 재생
  • 블루프린트에서 Brake_1, Brake_2 배열에 추가

엔진 사운드 - RPM 피치 변화

C++ 구현

// CityVehiclePawn.h
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Sound")
TObjectPtr<UAudioComponent> EngineAudioComponent;

UPROPERTY(EditAnywhere, Category="Sound")
TObjectPtr<USoundBase> EngineSound;

// 생성자
EngineAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("EngineAudio"));
EngineAudioComponent->SetupAttachment(GetRootComponent());
EngineAudioComponent->bAutoActivate = false;

// BeginPlay
if (EngineSound)
{
    EngineAudioComponent->SetSound(EngineSound);
    EngineAudioComponent->Play();
}

// Tick - RPM에 따라 피치 조절
if (EngineAudioComponent && EngineAudioComponent->IsPlaying())
{
    const float RPM = ChaosVehicleMovement->GetEngineRotationSpeed();
    const float MaxRPM = 5500.0f;
    const float PitchMin = 0.5f;
    const float PitchMax = 2.0f;
    const float Pitch = FMath::Lerp(PitchMin, PitchMax, RPM / MaxRPM);
    EngineAudioComponent->SetPitchMultiplier(Pitch);
}

Sound Cue 설정

  • Wave Player에서 Looping 체크
  • Looping Wave Player → Output 구조

삽질 포인트

  • 매 프레임 IsPlaying() 체크해서 꺼져있으면 Play() 다시 호출하면 루프가 끊겨서 뚝뚝 소리남
  • 루프는 언리얼이 알아서 하도록 두고 ResumeMovement에서만 재시작하면 됨
  • 엔진 루프 사운드는 seamless loop 파일이 아니면 루프 시 클릭 소리 발생 (완벽한 해결 어려움)

SplineFollower 조향 튜닝

지그재그 + 차선 이탈 해결

MaxYawDelta : 55 → 30   (조향 반응 강하게)

MaxYawDelta가 낮을수록 같은 각도 오차에서 더 강하게 조향 명령이 나와서 차선 이탈이 줄어든다


마무리

  1. BlueprintAssignable 델리게이트는 블루프린트에서 직접 브로드캐스트 불가 → BlueprintCallable 함수로 감싸야 함
  2. Timeline은 Custom Event 안에서만 사용 가능 (함수 안에서 Delay/Timeline 못 씀)
  3. 엔진 루프 사운드 뚝 끊김은 파일 자체가 seamless loop 아니면 해결 어려움
  4. BTT는 매 프레임 실행되므로 한 번만 실행해야 하는 로직은 플래그로 보호해야 함