// 아이템 투명화 on/off
void SetItemInvisible(bool bNewInvisible);
// 아이템 투명화 상태
UPROPERTY(ReplicatedUsing = OnRep_bIsItemInvisible)
bool bIsItemInvisible = false;
// bIsItemInvisible 복제 시 호출되는 콜백
UFUNCTION()
void OnRep_bIsItemInvisible();
DOREPLIFETIME(AFDHiderCharacter, bIsItemInvisible);
void AFDHiderCharacter::SetItemInvisible(bool bNewInvisible)
{
if (!HasAuthority()) return;
bIsItemInvisible = bNewInvisible;
// 서버니까 직접 호출
OnRep_bIsItemInvisible();
}
void AFDHiderCharacter::OnRep_bIsItemInvisible()
{
// 투명화 상태에 따라 메시 표시/숨김
// SetVisibility(false)는 눈에만 안 보임
// 콜리전은 그대로라 술래가 부딪히면 위치 노출됨
USkeletalMeshComponent* SkeletalMesh = GetMesh();
if (!SkeletalMesh) return;
// 투명화 상태니까 bIsItemInvisible은 true인 상태
// 근데 메시는 안보여야 하니까 ! 붙인 것
SkeletalMesh->SetVisibility(!bIsItemInvisible, true);
}
FDItemInventoryComponent.cpp의 ExecuteEffect 채우기
void UFDItemInventoryComponent::ExecuteEffect(const FFDItemData& Data)
{
AFDHiderCharacter* Hider = Cast<AFDHiderCharacter>(GetOwner());
if (!Hider) return;
switch (Data.Effect)
{
case EFDItemEffect::Invisibility:
{
// 투명화 켜기
Hider->SetItemInvisible(true);
// Duration초 뒤 자동으로 끄기
// 람다 안에서 Hider가 그 사이 파괴됐을 수 있으니 IsValid로 방어
GetWorld()->GetTimerManager().SetTimer(EffectExpireTimerHandle,
[Hider]()
{
if (IsValid(Hider))
{
Hider->SetItemInvisible(false);
}
},
Data.Duration, false);
break;
}
case EFDItemEffect::TaggerStun:
// 투사체 스폰
break;
case EFDItemEffect::ForcedEmote:
// 강제 이모션
break;
case EFDItemEffect::Noise:
// 소리 발생
break;
default:
break;
}
}
서버: SetItemInvisible(true) 호출
HasAuthority() 통과 (서버니까)
bIsItemInvisible = true 값 변경
이 변경을 모든 클라이언트로 복제 시작
OnRep_bIsItemInvisible() 호출 -> 추후에 서버에서 실행될 이유가 없다고 판단되면 삭제 예정
메시 숨김
클라들: bIsItemInvisible = true 수신
OnRep_bIsItemInvisible() 자동 호출 -> 각자 화면 반영
Z키 입력
전체 흐름 (최종 연결)
플레이어가 Z 누름
PlayerController의 Input_UseInvisibility 콜백
InventoryComp -> Server_UseItem(EFDItemEffect::Invisibility) = 서버로 RPC
(서버)
InvisibilityCount > 0 확인 -> -1 -> ExecuteEffect
Hider -> SetItemInvisible(true) + 5초 뒤 자동 false
FDPlayerController.h
// 투명화 아이템 사용 키 (Z) - 하이더 전용
UPROPERTY(EditDefaultsOnly, Category = "Input")
UInputAction* IA_UseInvisibility;
// 투명화 아이템 사용 입력 처리 → 하이더의 인벤토리 컴포넌트에 사용 요청
void Input_UseInvisibility(const FInputActionValue& Value);
SetupInputComponent에 바인딩 추가
// 투명화 아이템 사용 입력 바인딩 (하이더 전용)
if (IA_UseInvisibility)
{
EIC->BindAction(IA_UseInvisibility, ETriggerEvent::Started, this, &AFDPlayerController::Input_UseInvisibility);
}
ETriggerEvent::Started를 쓴 이유
키를 누르는 순간 한 번만 발동하면 되니까
콜백 함수 구현
void AFDPlayerController::Input_UseInvisibility(const FInputActionValue& Value)
{
// 하이더인지 확인
AFDHiderCharacter* Hider = Cast<AFDHiderCharacter>(GetPawn());
if (!Hider) return;
// 하이더에 붙어있는 인벤토리 컴포넌트 찾기
UFDItemInventoryComponent* Inventory =
Hider->FindComponentByClass<UFDItemInventoryComponent>();
if (!Inventory) return;
// 서버에 투명화 사용 요청
Inventory->Server_UseItem(EFDItemEffect::Invisibility);
}
에디터 작업
인풋 액션 애셋 만들기
- Input Action 생성 (IA_UseInvisibility)
- 설정에서 Value Type : 그냥 눌렸다 안 눌렸다만 필요하니 Digital (bool)
매핑 컨텍스트에 Z키 연결
인풋 액션은 이런 동작이 있다는 선언
어떤 키가 이 동작을 발동하는지는 매핑 컨텍스트(IMC)에서
- IMC Mappings 목록에서 + 버튼으로 새 매핑 추가
- 방금 만든 IA_UseInvisibility 선택
- 그 아래 키 입력란을 클릭하고 Z 키를 누르면 Z가 등록됨
컨트롤러에 IA 애셋 할당
C++에서 만든 IA_UseInvisibility 포인터에 에셋 연결
BP_FDPlayerController에서 Input 카테고리의 IA Use Invisibility 슬롯에 IA_UseInvisibility 애셋을 드롭다운으로 선택
- Input Action (IA_UseInvisibility) = 투명화 사용이라는 동작이 존재한다
- Mapping Context (IMC) = 그 동작은 Z키로 발동한다
- PlayerController의 할당 = 이 컨트롤러가 그 IA를 코드에서 쓴다
투명화는 IsNetRelevantFor를 안 쓴 이유
정찰모드에서 술래한테 하이더를 숨길 땐 IsNetRelevantFor를 썼는데, 투명화 아이템에선 SetVisibility를 씀
왜냐면 정찰모드에서는 IsNetRelevantFor 하이더의 존재 자체를 숨김 (액터에 대한 정보 자체를 안 보내는 것)
근데 투명화는 SetVisibility(false) = 그래픽층 렌더링만 끔
즉 액터는 여전히 존재하고 복제도 계속됨
위치 정보도 계속 전달됨 단지 화면에 나타나지만 않는 것
이렇게 되면 하이더가 투명화가 되어도 술래랑 부딪힐 수 있어서 재미를 더할 수 있음
정리
획득 (박스 -> 인벤토리)
하이더가 아이템 박스에 접근
-> AFDItemPickup::OnPickupOverlap (서버)
가중치 뽑기로 Invisibility 결정
-> UFDItemInventoryComponent::ReceiveDrawnItem
bStoredOnPickup == true -> 보관형일 때 true
-> InvisibilityCount++ (최대 1)
-> OnRep_Inventory -> UI 갱신 신호 (복제됨)
입력 (Z키 -> 서버 요청)
플레이어가 Z 누름 (클라이언트)
-> AFDPlayerController::Input_UseInvisibility
GetPawn -> 하이더 캐스팅 -> 인벤토리 컴포넌트 찾기
Inventory -> Server_UseItem(Invisibility)
[클라이언트 -> 서버로 RPC 전송]
판정과 소모 (서버)
UFDItemInventoryComponent::Server_UseItem_Implementation (서버)
InvisibilityCount > 0 확인 (개수 판정 = 서버 권한)
-> InvisibilityCount-- (소모 먼저 - 중복 사용 방지)
-> OnRep_Inventory -> UI 갱신
데이터 테이블에서 Duration 등 조회
-> ExecuteEffect(투명화 데이터)
효과 실행 (인벤토리 -> 캐릭터)
UFDItemInventoryComponent::ExecuteEffect (서버)
switch(Effect) -> Invisibility 케이스
-> Hider->SetItemInvisible(true)
+
5초 뒤 타이머 예약 -> SetItemInvisible(false)
상태 변경과 복제 (캐릭터)
-> bIsItemInvisible = true : 복제 변수 변경
-> OnRep_bIsItemInvisible() 서버 수동 호출
화면 반영 (모든 클라이언트)
메시 숨김 (콜리전은 유지)
클라가 Z 입력(투명화) -> Server_UseItem 발동 -> 서버로 RPC = setiteminvisible true 되고 Replication 값인 bIsItemInvisible도 true 됨 -> 개수 판정 및 소모 후 ExecuteEffect 함수 실행
Replication 된게 OnRep 호출 -> 메시 숨김 -> 5초뒤 타이머가 SetItemInvisible을 false로 바꿈 -> 위 과정이 반대로 진행됨
'프로젝트 > Funny Or Die (멀티플레이 술래잡기 게임)' 카테고리의 다른 글
| 투사체 조준 및 발사 (X키 조준 -> 좌클릭 발사) (0) | 2026.07.13 |
|---|---|
| 투사체 생성 (0) | 2026.07.10 |
| 인벤토리 개편 (0) | 2026.07.10 |
| 아이템 데이터 정의 (구조체) (0) | 2026.07.09 |
| 인트로 화면 구성 (0) | 2026.07.07 |
