만들 건 bIsInvincible 패턴의 복사 (복제 bool, on/off 함수, OnRep 비주얼)이고
타이머는 인벤토리 컴포넌트의 EffectExpireTimerHandle에 둠
// 아이템 투명화 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);
}

 

 

에디터 작업 

인풋 액션 애셋 만들기

  1. Input Action 생성 (IA_UseInvisibility)
  2. 설정에서 Value Type : 그냥 눌렸다 안 눌렸다만 필요하니 Digital (bool)

매핑 컨텍스트에 Z키 연결

인풋 액션은 이런 동작이 있다는 선언

어떤 키가 이 동작을 발동하는지는 매핑 컨텍스트(IMC)에서

  1. IMC Mappings 목록에서 + 버튼으로 새 매핑 추가
  2. 방금 만든 IA_UseInvisibility 선택
  3. 그 아래 키 입력란을 클릭하고 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)

 

상태 변경과 복제 (캐릭터)

AFDHiderCharacter::SetItemInvisible(true) (서버)
 
HasAuthority 확인 (서버만 상태 변경)

-> bIsItemInvisible = true  : 복제 변수 변경
[모든 클라이언트로 복제 시작]

-> OnRep_bIsItemInvisible() 서버 수동 호출
 

화면 반영 (모든 클라이언트)

클라들: bIsItemInvisible 수신 -> OnRep_bIsItemInvisible() 자동 호출
 
GetMesh() -> SetVisibility(!bIsItemInvisible, true)

메시 숨김 (콜리전은 유지)
 
자동 복구
 

클라가 Z 입력(투명화) -> Server_UseItem 발동 -> 서버로 RPC = setiteminvisible true 되고 Replication 값인 bIsItemInvisible도 true 됨 -> 개수 판정 및 소모 후 ExecuteEffect 함수 실행 

 

Replication 된게 OnRep 호출 -> 메시 숨김 -> 5초뒤 타이머가 SetItemInvisible을 false로 바꿈 -> 위 과정이 반대로 진행됨

+ Recent posts