언리얼 사격 시스템 정리 (라인트레이스 사격, 연발, 부분 랙돌, 부위 판정)
언리얼 Shooting 학습 노트. 강의 10.1~10.3 개념만. 사격 조준 보정, 연발 타이머, 부위 판정 헷갈릴 때 다시 보기.
사격 애니메이션과 몽타주
몽타주 만들 때 애니메이션 우클릭 > Create AnimMontage로 만들면 스켈레톤이 자동 지정돼 실수 없음. 슬롯은 상체용(SXUpperBody)으로 변경.
가장 흔한 함정: 우클릭해도 사격 애니메이션이 안 나옴 → 블렌드 시간 문제. 사격 애니메이션 재생 길이(0.23초)보다 Blend In/Out의 Blend Time(0.25초)이 길면 블렌드만 하다 끝남. Blend Time을 짧게(0.1) 줄여야 함. 짧은 애니메이션엔 항상 블렌드 시간부터 의심.
무기-몽타주 연결: 무기 액터가 AttackMontage를 들고, 캐릭터는 CurrentWeapon을 통해 GetCurrentWeaponAttackAnimMontage()로 가져옴. 무기를 주울 때(HandleOnPickUp) CurrentWeapon에 자기 자신을 세팅.
견착/에임오프셋 (애님 인스턴스 연동)
견착 분기: 애님 인스턴스에 bIsUnarmed 추가. 현재 무기 몽타주가 nullptr이면 맨손, 아니면 견착. Anim Graph 각 스테이트에서 이 bool로 분기.
에임오프셋(마우스 위아래로 총구 따라가기): AimOffset 애셋 준비 후, 애님 인스턴스에서 컨트롤 로테이션의 Pitch를 NormalizeAxis로 정규화한 NormalizedCurrentPitch를 매 프레임 갱신해 AimOffset에 연결.
이펙트/사운드/카메라 쉐이크
머즐 이펙트·사운드: 몽타주 노티파이로 Play Sound, Play Particle Effect를 원하는 프레임에 배치(노티파이 트랙 추가해서 분리).
카메라 쉐이크: DefaultCameraShakeBase 상속 BP 만들어 값 설정 → 캐릭터에 TSubclassOf로 들고 → 발사 시 PlayerController->ClientStartCameraShake. Client- 접두사는 해당 클라이언트에서만 흔들리게 한다는 의미(멀티 고려).
라인 트레이스 사격의 조준 보정 (이 챕터 핵심 원리)
문제: 총알은 총구(Muzzle)에서 나가야 하는데, 조준은 화면 중앙(카메라)을 기준으로 함. 총구와 카메라 위치가 달라서 그냥 총구에서 카메라 방향으로 쏘면 크로스헤어와 안 맞음.
해결 흐름:
- GetPlayerViewPoint로 카메라 위치/회전을 얻음
- 카메라 전방으로 FocalDistance만큼 간 지점을 초점으로 잡음
- 총구 위치를 카메라 조준선에 투영(내적)해서 보정된 초점(FinalFocalLocation) 계산 — 총구가 어디 있든 조준선상의 같은 목표를 향하도록
- 그 보정된 트랜스폼의 X축을 총알 방향으로, 총구를 시작점으로 LineTraceSingleByChannel
- 맞은 액터가 캐릭터면 TakeDamage
핵심은 "조준은 카메라 기준, 발사는 총구 기준"을 내적 투영으로 일치시키는 것. 디버그는 콘솔 변수(ShowAttackRangedDebug)로 토글해서 DrawDebug로 시각화.
크로스헤어: 화면 중앙 UI. PlayerController BeginPlay에서 CreateWidget + AddToViewport. 사격이 화면 중앙 기준이므로 크로스헤어와 트레이스 초점이 일치해야 함.
단발/연발 토글 (타이머 패턴)
치트시트:
- TimeBetweenFire = 60 / FirePerMinute (분당 발사수를 발사 간격 초로 변환)
- 휠 클릭으로 bIsFullAutoFire 토글
- 연발 시작: 입력 Started에서 SetTimer(핸들, TryFire, TimeBetweenFire, true) — 마지막 true가 반복
- 연발 종료: 입력 Completed에서 ClearTimer
함정: 단발 로직과 연발 로직이 같은 입력(AttackRanged)에 묶임. 단발은 InputAttackRanged에서 bIsFullAutoFire가 false일 때만 TryFire 호출, 연발은 Started/Completed에 별도 바인드. 분기 안 하면 단발+연발이 동시에 발사됨.
줌(아이언사이트) — FOV 보간
TargetFOV/CurrentFOV 두 변수. 입력 Started에서 TargetFOV를 좁게(45), Completed에서 원래대로(70). Tick에서 CurrentFOV를 FInterpTo로 TargetFOV에 부드럽게 수렴시키고 SetFieldOfView. 즉시 바꾸지 않고 보간해야 자연스러움. (시점 전환 챕터의 InterpTo 패턴과 동일)
부분 랙돌 피격 모션 (Physics Blend)
피격 시 별도 피격 애니메이션 없이 물리로 흔들리게:
- spine_01 기준 하위 본만 SetAllBodiesBelowSimulatePhysics(true)
- SetAllBodiesBelowPhysicsBlendWeight로 물리 영향 비중 조절(1이면 완전 물리)
- 타이머로 일정 시간 후 복원
선행 조건 함정: 이 로직은 GetMesh()->SetSimulatePhysics(false) 상태가 먼저여야 함. 안 그러면 전신이 랙돌됨.
자연스러운 복원: 블렌드 웨이트를 1→0으로 즉시 끄지 말고, Tick에서 FInterpTo로 서서히 줄임. 0에 도달하면 SimulatePhysics를 끄고 블렌딩 종료. 즉시 끄면 캐릭터가 툭 끊김. (역시 InterpTo 패턴)
부위 판정과 헤드샷 (중요 함정)
HitResult.BoneName으로 맞은 본 이름을 가져와 부위별 처리. 헤드샷이면 BoneName이 "head"인지 비교(대소문자 무시)해서 데미지 차등.
가장 중요한 함정: BoneName이 None으로 나오면 캡슐에 맞은 것. 캡슐 콜리전은 본 정보가 없음. 해결하려면 공격 전용 트레이스 채널을 만들어 캡슐에는 반응 안 하고 SkeletalMesh에만 반응하도록 설정해야 함. (Collision 챕터의 채널 분리가 여기서 실제로 필요해지는 지점)
관통 패턴
이 챕터도 동일: 부드러운 전환(줌 FOV, 랙돌 복원)은 직접 끊지 말고 Tick + FInterpTo로 보간, 반복 동작(연발)은 직접 카운트하지 말고 타이머로, 타이밍(피격 복원)은 타이머 델리게이트로. 앞 챕터들에서 반복된 "직접 폴링하지 말고 보간/타이머/델리게이트로 잇는다" 구조의 연장.
'학습 > Unreal' 카테고리의 다른 글
| 리플렉션, GC, CDO, 메모리 (0) | 2026.05.28 |
|---|---|
| 언리얼 C++ 헤더 파일 패턴: USTRUCT, UENUM, 비트플래그, 델리게이트 (0) | 2026.05.26 |
| 언리얼 C++ 모듈 추가 정리 (Module, Build.cs, ini Config, 비동기 로딩) (0) | 2026.05.16 |
| 언리얼 UI 정리 (WidgetComponent, HUD, 초기화 타이밍 함정) (0) | 2026.05.16 |
| 언리얼 게임 데이터 관리 정리 (StatusComponent, PlayerState, GameInstance, DataTable) (0) | 2026.05.16 |
