Post

TIL : GAS 총기 반동 구현 — PlayerController 책임 분리

GAS에서 반동 직접 제어가 안 되는 이유와 PlayerController PlayerTick으로 분리한 구조

TIL : GAS 총기 반동 구현 — PlayerController 책임 분리

TIL - GAS 총기 반동 구현


문제

GAS 어빌리티 안에서 총기 반동을 구현하려고 했으나, 카메라 회전을 직접 제어할 방법이 없었다.

AddControllerPitchInput 같은 함수는 PlayerController 또는 Pawn에 종속되어 있어서 GAS 내부에서 자연스럽게 호출하기 어렵고, GAS 레이어에서 카메라를 직접 건드리는 건 구조적으로도 맞지 않는다.


구조 — 책임 분리

GAS가 반동의 목표 위치(Target)만 결정하고, 실제 화면을 올리는 건 PlayerController가 담당하도록 분리했다.

1
2
3
4
5
6
GA (발사 어빌리티)
  └─ 반동 목표 Pitch 계산 → PlayerController에 전달

PlayerController::PlayerTick(float DeltaTime)
  └─ 현재 Pitch → 목표 Pitch 방향으로 틱마다 보간하여 적용
       └─ AddControllerPitchInput(...)

구현

PlayerController

반동 목표값을 보관하고, PlayerTick에서 매 틱 처리한다.

1
2
3
4
5
6
// MyPlayerController.h

UPROPERTY()
float RecoilTargetPitch = 0.f;

float RecoilSpeed = 10.f;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// MyPlayerController.cpp

void AMyPlayerController::PlayerTick(float DeltaTime)
{
    Super::PlayerTick(DeltaTime);

    if (!FMath::IsNearlyZero(RecoilTargetPitch))
    {
        float Step = RecoilSpeed * DeltaTime;
        float Applied = FMath::Sign(RecoilTargetPitch) * FMath::Min(FMath::Abs(RecoilTargetPitch), Step);

        AddPitchInput(Applied);
        RecoilTargetPitch -= Applied;
    }
}

GA (발사 어빌리티)

발사 시 반동 목표값만 PlayerController에 넘긴다.

1
2
3
4
5
6
7
8
void UGA_Fire::ApplyRecoil(float RecoilAmount)
{
    APlayerController* PC = Cast<APlayerController>(GetActorInfo().PlayerController.Get());
    if (AMyPlayerController* MyPC = Cast<AMyPlayerController>(PC))
    {
        MyPC->RecoilTargetPitch += RecoilAmount;
    }
}

핵심 요약

역할담당
반동 크기/목표 결정GA (GAS 어빌리티)
실제 카메라 이동PlayerController PlayerTick
This post is licensed under CC BY 4.0 by the author.