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의
NativeOnDragDetected와NativeOnDrop으로 드래그 앤 드롭을 구현합니다 - 호버 시 커스텀 툴팁 위젯을 동적으로 생성하여 아이템 상세 정보를 표시합니다
UUniformGridPanel으로 동적 그리드를 구성하고, 대량 아이템은 위젯 풀링으로 최적화합니다
PRACTICE
도전 과제
배운 내용을 직접 실습해보세요
실습 1: 그리드 인벤토리 슬롯 시스템
UUniformGridPanel을 사용하여 4x6 그리드 인벤토리를 구현하세요. 각 슬롯은 아이템 아이콘, 수량 텍스트, 등급 테두리를 표시하고, 빈 슬롯과 아이템이 있는 슬롯을 구분합니다.
실습 2: 드래그 앤 드롭 구현
UDragDropOperation을 상속한 커스텀 드래그 오퍼레이션을 만들고, OnDragDetected/OnDrop 이벤트를 처리하여 인벤토리 슬롯 간 아이템 이동과 장비 착용/해제를 구현하세요.
심화 과제
아이템 정렬(이름/등급/타입), 필터링, 아이템 분할(Shift+드래그), 우클릭 컨텍스트 메뉴(사용/버리기/분해), 툴팁 표시를 포함한 완전한 RPG 인벤토리 시스템을 구현하세요.