인벤토리 UI가 갱신되는 경로는 이렇게 설계돼 있음

  1. 아이템 획득 -> 서버가 개수(ThrowItemCount 등)를 증가 -> OnRep_Inventory()가 클라에서도 불림 -> OnInventoryChanged.Broadcast()
  2. WBP가 이 OnInventoryChanged 델리게이트를 구독하고 있어 화면을 다시 그림

그런데 클라에서 아이템을 먹어도 숫자가 그대로

로그를 보니 개수 자체는 클라까지 복제돼 오고 있었음(OnRep_Inventory도 정상 호출)

-> 방송은 잘 나가는데 WBP 바인딩이 빠진 걸 의심

 

그리고 서버는 되고 클라만 안 됨

서버와 클라가 다르게 구는 것 = 네트워크 복제 순서와 관련이 있다는 뜻

 

WBP Event Construct(위젯이 화면에 붙는 순간)

Get Owning Player Pawn → Cast to BP_FDHiderCharacter → Get Component by Class
→ SET Inventory Comp → Bind Event to OnInventoryChanged

 

위젯이 주인 폰을 추적해서 하이더를 찾고 거기서 인벤토리 컴포넌트를 꺼내 바인딩하는 구조

클라에서는 폰과 컴포넌트가 네트워크로 따로, 시차를 두고 도착

possess 훅으로 UI 생성을 앞당긴 탓에 위젯은 이제 이른 시점에 만들어짐

그 찰나엔 Get Owning Player Pawn이 아직 이 하이더를 안 가리키거나, 폰은 왔어도 인벤토리 컴포넌트가 아직 복제 도착 전이라 Get Component by Class가 null을 뱉는다고 함

그러면 Bind Event가 실행되지 못하고 바인딩이 누락

 

서버(리슨 호스트)는 폰도 컴포넌트도 처음부터 자기 손에 다 있으니 역추적이 늘 성공 

 

위젯이 폰을 역추적 하는 구조는 복제 순서가 조금만 흔들려도 깨지는 구조임을 알게됨

 

그래서 역추적 자체를 없애고 컴포넌트가 위젯에게 자기를 직접 건네주게 함

 

컴포넌트가 위젯에 무언가를 건네주려면, 위젯에 그걸 받는 창구(함수)가 있어야 함

그런데 기존 위젯의 부모는 엔진 기본 클래스 UUserWidget이라 내 함수를 붙일 수 없음

게다가 C++(컴포넌트)이 위젯의 함수를 이름으로 부르려면 그 함수가 C++ 타입에 선언돼 있어야 함

WBP 안에만 있는 커스텀 이벤트는 C++이 부를 수 없음

 

-> 그래서 UUserWidget을 상속한 UFDItemInventoryWidget을 새로 만듦

// FDItemInventoryWidget.h
UCLASS()
class FUNNYORDIE_API UFDItemInventoryWidget : public UUserWidget
{
    GENERATED_BODY()
    
public:
    UFUNCTION(BlueprintImplementableEvent, Category = "Item")
    void InitializeWithInventory(UFDItemInventoryComponent* InInventory);
};

 

BlueprintImplementableEvent -> 선언 C++에 구현은 WBP 그래프

 

C++에서 이 함수를 호출하면 WBP 쪽 그래프가 실행됨

C++은 이 타입엔 이런 함수가 있다는 것만 알고 그 안에서 어떤 델리게이트에 바인딩할지는 WBP가 채움

 

컴포넌트가 만든 직후 자기 자신을 넘겨줌

// FDItemInventoryComponent.cpp - SetupLocalUI 안
InventoryWidget = CreateWidget<UFDItemInventoryWidget>(PC, InventoryWidgetClass);
if (InventoryWidget)
{
    InventoryWidget->AddToViewport();
    // 위젯이 폰을 역추적하지 않게 컴포넌트가 자기 자신을 직접 건네줌
    // 이 순간 컴포넌트는 당연히 존재하므로 클라 복제 타이밍과 무관하게 성공
    InventoryWidget->InitializeWithInventory(this);
}

 

NotifyWidget도 완전히 대칭으로 만든 직후 InitializeWithInventory(this)를 불러줌

두 위젯 타입을 UUserWidget에서 UFDItemInventoryWidget으로 좁혀 캐스팅 없이 이 함수를 바로 부를 수 있게 함

 

에디터 작업

인벤토리 위젯(WBP_ItemInventory)

  1. Reparent: File -> Reparent Blueprint -> UFDItemInventoryWidget
  2. Event Construct의 Get Owning Player Pawn -> Cast -> Get Component 역추적 사슬 삭제
  3. InitializeWithInventory 이벤트 노드 추가
  4. In Inventory -> SET Inventory Comp -> Bind Event to OnInventoryChanged -> RefreshInventory 순으로 연결

 

 

알림 위젯(WBP_ItemNotify) : 인벤토리와 판박이인데 두 가지가 달랐다

  1. 같은 방식으로 Reparent, 역추적 삭제, InitializeWithInventory로 교체
  2.  바인딩을 두 번 한다: OnItemAcquired(획득 알림)와 OnInventoryFull(한도초과 경고)

+ Recent posts