오늘은 메르시 캐릭터의 크로스헤어 피격 피드백 애니메이션이 표시되지 않는 문제를 해결했음
활을 당겼을 때 크로스헤어 줌인/줌아웃은 정상적으로 작동하는데, 적을 맞췄을 때 피격 피드백 애니메이션만 표시되지 않는 이상한 상황이었음
알고 보니 팀원 분이 작성하신 코드를 제대로 숙지하지 못했고, 데디케이티드 서버 환경을 생각하지 못해서 생긴 오류였음
크로스헤어 기능을 테스트하니까 뭔가 이상했음
정상 작동하는 기능들:
작동하지 않는 기능:
로그를 확인해보니 화살이 적과 충돌은 감지하지만, 크로스헤어 피드백 함수가 호출되지 않고 있었음
첫 번째 단서: BeginOverlap 바인딩 조건
void AGS_SeekerMerciArrow::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority()) // 서버에서만 바인딩!
{
if (CollisionComponent)
{
CollisionComponent->OnComponentBeginOverlap.AddDynamic(this, &AGS_SeekerMerciArrow::OnBeginOverlap);
}
}
}
두 번째 단서: 로컬 컨트롤 체크
if (AGS_Merci* MerciPlayer = Cast<AGS_Merci>(GetOwner()))
{
if (MerciPlayer->IsLocallyControlled()) // 데디케이티드 서버에서는 항상 false!
{
MerciPlayer->ShowCrosshairHitFeedback();
}
}
핵심 문제
HasAuthority() 조건)IsLocallyControlled()가 falseDrawBow/ReleaseArrow 함수 분석
void AGS_Merci::DrawBow(UAnimMontage* DrawMontage)
{
if (IsLocallyControlled() && !GetDrawState()) // 클라이언트에서 먼저 실행
{
if (WidgetCrosshair)
{
WidgetCrosshair->PlayAimAnim(true); // UI 업데이트
}
}
if (!HasAuthority())
{
Server_DrawBow(DrawMontage); // 서버로 RPC 전송
return;
}
// 서버 로직...
}
차이점 발견
기존 방식 (실패)
서버: 피격 감지 → IsLocallyControlled() 체크 → 항상 false → 함수 호출 안됨
개선된 방식 (성공)
서버: 피격 감지 → Client RPC 전송 → 해당 클라이언트: 피드백 실행
구현한 Client RPC
// GS_Merci.h
UFUNCTION(Client, Reliable)
void Client_ShowCrosshairHitFeedback();
// GS_Merci.cpp
void AGS_Merci::Client_ShowCrosshairHitFeedback_Implementation()
{
if (!WidgetCrosshair) return;
WidgetCrosshair->PlayHitFeedback();
}
OnBeginOverlap 수정
// 기존 코드 (실패)
if (MerciPlayer->IsLocallyControlled())
{
MerciPlayer->ShowCrosshairHitFeedback();
}
// 수정된 코드 (성공)
if (AGS_Merci* MerciPlayer = Cast<AGS_Merci>(GetOwner()))
{
MerciPlayer->Client_ShowCrosshairHitFeedback(); // Client RPC 호출
}

크로스헤어 피격 피드백이 정상적으로 작동하게 되었음!
데디케이티드 서버 네트워킹 핵심 교훈
1. 클라이언트 입력 → Server RPC → 서버 로직 처리
2. 서버 이벤트 감지 → Client RPC → 클라이언트 UI 업데이트
3. 서버 상태 변경 → Replication → 모든 클라이언트 동기화
4. Multicast RPC → 모든 클라이언트에서 동시 실행 (VFX, SFX 등)