PART 6 · 강의 1/3

인벤토리 UI

드래그 앤 드롭, 그리드 인벤토리, 아이템 툴팁, 슬롯 시스템을 C++로 구현합니다.

01

인벤토리 슬롯 시스템

슬롯 기반 인벤토리 UI의 기본 구조

C++ - 인벤토리 슬롯 위젯
UCLASS() class UInventorySlotWidget : public UUserWidget { GENERATED_BODY() public: UPROPERTY(meta = (BindWidget)) UImage* ItemIcon; UPROPERTY(meta = (BindWidget)) UTextBlock* QuantityText; UPROPERTY(meta = (BindWidget)) UImage* RarityBorder; UPROPERTY(meta = (BindWidget)) UButton* SlotButton; void SetItem(const FInventoryItem& Item) { if (Item.Icon) { ItemIcon->SetBrushFromTexture(Item.Icon); ItemIcon->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else { ItemIcon->SetVisibility(ESlateVisibility::Hidden); } if (Item.Quantity > 1) { QuantityText->SetText(FText::AsNumber(Item.Quantity)); QuantityText->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else { QuantityText->SetVisibility(ESlateVisibility::Collapsed); } // 희귀도에 따른 테두리 색상 RarityBorder->SetColorAndOpacity(GetRarityColor(Item.Rarity)); CachedItem = Item; } void ClearSlot() { ItemIcon->SetVisibility(ESlateVisibility::Hidden); QuantityText->SetVisibility(ESlateVisibility::Collapsed); CachedItem = FInventoryItem(); } private: FInventoryItem CachedItem; int32 SlotIndex = -1; };
02

드래그 앤 드롭

UMG의 드래그 앤 드롭 시스템 구현

C++ - 드래그 앤 드롭 구현
// 드래그 오퍼레이션 정의 UCLASS() class UItemDragDropOperation : public UDragDropOperation { GENERATED_BODY() public: UPROPERTY() FInventoryItem DraggedItem; UPROPERTY() int32 SourceSlotIndex; }; // 슬롯 위젯에서 드래그 시작 virtual FReply UInventorySlotWidget::NativeOnMouseButtonDown( const FGeometry& Geometry, const FPointerEvent& MouseEvent) override { if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && CachedItem.IsValid()) { return FReply::Handled().DetectDrag( TakeWidget(), EKeys::LeftMouseButton); } return FReply::Unhandled(); } virtual void UInventorySlotWidget::NativeOnDragDetected( const FGeometry& Geometry, const FPointerEvent& MouseEvent, UDragDropOperation*& OutOperation) override { // 드래그 오퍼레이션 생성 UItemDragDropOperation* DragOp = NewObject<UItemDragDropOperation>(); DragOp->DraggedItem = CachedItem; DragOp->SourceSlotIndex = SlotIndex; DragOp->DefaultDragVisual = this; // 드래그 비주얼 DragOp->Pivot = EDragPivot::CenterCenter; OutOperation = DragOp; } // 드롭 대상에서 받기 virtual bool UInventorySlotWidget::NativeOnDrop( const FGeometry& Geometry, const FDragDropEvent& DragDropEvent, UDragDropOperation* InOperation) override { if (auto* ItemOp = Cast<UItemDragDropOperation>(InOperation)) { // 아이템 스왑 또는 이동 로직 OnItemDropped.Broadcast(ItemOp->SourceSlotIndex, SlotIndex); return true; } return false; }
03

아이템 툴팁

호버 시 상세 정보를 표시하는 동적 툴팁

C++ - 커스텀 툴팁
// 커스텀 툴팁 위젯 UCLASS() class UItemTooltipWidget : public UUserWidget { GENERATED_BODY() public: UPROPERTY(meta = (BindWidget)) UTextBlock* ItemName; UPROPERTY(meta = (BindWidget)) UTextBlock* Description; UPROPERTY(meta = (BindWidget)) URichTextBlock* StatsText; void SetItemData(const FInventoryItem& Item) { ItemName->SetText(Item.DisplayName); Description->SetText(Item.Description); StatsText->SetText(FormatStats(Item)); } }; // 슬롯에서 툴팁 표시 virtual void UInventorySlotWidget::NativeOnMouseEnter( const FGeometry& Geometry, const FPointerEvent& MouseEvent) override { if (CachedItem.IsValid()) { // 툴팁 위젯 생성 및 위치 지정 if (!TooltipWidget) { TooltipWidget = CreateWidget<UItemTooltipWidget>( GetOwningPlayer(), TooltipClass); } TooltipWidget->SetItemData(CachedItem); TooltipWidget->AddToViewport(1000); // 최상위 Z-Order } } virtual void UInventorySlotWidget::NativeOnMouseLeave( const FPointerEvent& MouseEvent) override { if (TooltipWidget) { TooltipWidget->RemoveFromParent(); } }
04

그리드 인벤토리 구현

WrapBox 또는 UniformGrid를 활용한 그리드 레이아웃

C++ - 동적 그리드 인벤토리
UPROPERTY(meta = (BindWidget)) UUniformGridPanel* InventoryGrid; void UInventoryPanel::BuildGrid(int32 Rows, int32 Columns) { InventoryGrid->ClearChildren(); SlotWidgets.Empty(); for (int32 i = 0; i < Rows * Columns; ++i) { UInventorySlotWidget* Slot = CreateWidget<UInventorySlotWidget>( GetOwningPlayer(), SlotWidgetClass); Slot->SlotIndex = i; Slot->OnItemDropped.AddDynamic( this, &UInventoryPanel::HandleItemMoved); // 그리드에 추가 (행, 열 자동 계산) InventoryGrid->AddChildToUniformGrid( Slot, i / Columns, i % Columns); SlotWidgets.Add(Slot); } } void UInventoryPanel::RefreshFromData( const TArray<FInventoryItem>& Items) { for (int32 i = 0; i < SlotWidgets.Num(); ++i) { if (Items.IsValidIndex(i) && Items[i].IsValid()) SlotWidgets[i]->SetItem(Items[i]); else SlotWidgets[i]->ClearSlot(); } }
대량 아이템 최적화

아이템이 수백 개인 경우 모든 슬롯을 한 번에 생성하면 성능이 저하됩니다. Part 7에서 다루는 ListView/TileView 위젯 풀링을 사용하면 화면에 보이는 슬롯만 생성하여 메모리와 CPU 비용을 절약할 수 있습니다.

SUMMARY

핵심 요약

  • 인벤토리 슬롯은 아이콘, 수량, 희귀도 테두리를 가진 재사용 가능한 위젯으로 설계합니다
  • UMG의 NativeOnDragDetectedNativeOnDrop으로 드래그 앤 드롭을 구현합니다
  • 호버 시 커스텀 툴팁 위젯을 동적으로 생성하여 아이템 상세 정보를 표시합니다
  • UUniformGridPanel으로 동적 그리드를 구성하고, 대량 아이템은 위젯 풀링으로 최적화합니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 그리드 인벤토리 슬롯 시스템

UUniformGridPanel을 사용하여 4x6 그리드 인벤토리를 구현하세요. 각 슬롯은 아이템 아이콘, 수량 텍스트, 등급 테두리를 표시하고, 빈 슬롯과 아이템이 있는 슬롯을 구분합니다.

실습 2: 드래그 앤 드롭 구현

UDragDropOperation을 상속한 커스텀 드래그 오퍼레이션을 만들고, OnDragDetected/OnDrop 이벤트를 처리하여 인벤토리 슬롯 간 아이템 이동과 장비 착용/해제를 구현하세요.

심화 과제

아이템 정렬(이름/등급/타입), 필터링, 아이템 분할(Shift+드래그), 우클릭 컨텍스트 메뉴(사용/버리기/분해), 툴팁 표시를 포함한 완전한 RPG 인벤토리 시스템을 구현하세요.