public:
UMyInventoryEntryWidget(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
public:
void Init(UMyInventorySlotsWidget* InSlotsWidget, UMyItemInstance* InItemInstance, int32 InItemCount);
protected:
virtual void NativeConstruct() override;
private:
FIntPoint CachedFromSlotPos = FIntPoint::ZeroValue;
FVector2D CachedDeltaWidgetPos = FVector2D::ZeroVector;
int32 ItemCount = 0;
protected:
UPROPERTY()
TObjectPtr<UMyInventorySlotsWidget> SlotsWidget;
UPROPERTY()
TObjectPtr<UMyItemInstance> ItemInstance;
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<USizeBox> SizeBox_Root;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> Text_Count;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UImage> Image_Icon;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UImage> Image_Hover;
- SizeBox를 통해 아이템의 사이즈를 정하고
- Image_Icon 위에 마우스를 갖다대면 Image_Hover의 불투명도를 조절하여 호버링 표시
- 아이템에 대한 정보와 아이템의 아이콘으로 InventorySlots에서 관리
void UMyInventoryEntryWidget::Init(UMyInventorySlotsWidget* InSlotsWidget, UMyItemInstance* InItemInstance, int32 InItemCount)
{
SlotsWidget = InSlotsWidget;
ItemInstance = InItemInstance;
ItemCount = InItemCount;
}
void UMyInventoryEntryWidget::NativeConstruct()
{
Super::NativeConstruct();
Text_Count->SetText(FText::GetEmpty());
}
이후 이름에 맞게 위젯 블루프린트에서 생성

마우스 및 드래그 이벤트 감지 추가
protected:
virtual void NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent) override;
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
virtual void NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation) override;
virtual void NativeOnDragCancelled(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
void UMyInventoryEntryWidget::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
Super::NativeOnMouseEnter(InGeometry, InMouseEvent);
Image_Hover->SetRenderOpacity(1.f);
}
void UMyInventoryEntryWidget::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
Super::NativeOnMouseLeave(InMouseEvent);
Image_Hover->SetRenderOpacity(0.f);
}
FReply UMyInventoryEntryWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
FReply Reply = Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
{
Reply.DetectDrag(TakeWidget(), EKeys::LeftMouseButton);
}
return Reply;
}
- 이미지의 SetRenderOpacity를 이용해서 호버링되는 이미지가 마우스를 갖다 댔을때 보였다가 떼면 안보이도록 조정

InventorySlotsWidget에 InventoryEntryWidget을 추가
protected:
UPROPERTY()
TSubclassOf<UR1InventoryEntryWidget> EntryWidgetClass;
UPROPERTY()
TArray<TObjectPtr<UR1InventoryEntryWidget>> EntryWidgets;
- 아이템이 여러개일 수 있으므로 TArray로 가지고 있는다
UMyInventorySlotsWidget::UMyInventorySlotsWidget(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ConstructorHelpers::FClassFinder<UMyInventorySlotWidget> FindSlotWidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UI/Item/Inventory/WBP_InventorySlot.WBP_InventorySlot_C'"));
if (FindSlotWidgetClass.Succeeded())
{
SlotWidgetClass = FindSlotWidgetClass.Class;
}
ConstructorHelpers::FClassFinder<UMyInventoryEntryWidget> FindEntryWidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UI/Item/Inventory/WBP_InventoryEntry.WBP_InventoryEntry_C'"));
if (FindEntryWidgetClass.Succeeded())
{
EntryWidgetClass = FindEntryWidgetClass.Class;
}
}
- Slot과 마찬가지로 Entry를 FClassFinder로 가져온다
void UMyInventorySlotsWidget::NativeConstruct()
{
Super::NativeConstruct();
SlotWidgets.SetNum(X_COUNT * Y_COUNT);
for (int32 y = 0; y < Y_COUNT; y++)
{
for (int32 x = 0; x < X_COUNT; x++)
{
int32 index = y * X_COUNT + x;
URMynventorySlotWidget* SlotWidget = CreateWidget<UMyInventorySlotWidget>(GetOwningPlayer(), SlotWidgetClass);
SlotWidgets[index] = SlotWidget;
GridPanel_Slots->AddChildToUniformGrid(SlotWidget, y, x);
}
}
EntryWidgets.SetNum(X_COUNT * Y_COUNT);
UMyInventorySubsystem* Inventory = Cast<UMyInventorySubsystem>(USubsystemBlueprintLibrary::GetWorldSubsystem(this, UMyInventorySubsystem::StaticClass()));
const TArray<TObjectPtr<UMyItemInstance>>& Items = Inventory->GetItems();
for (int32 i = 0; i < Items.Num(); i++)
{
const TObjectPtr<UMyItemInstance>& Item = Items[i];
FIntPoint ItemSlotPos = FIntPoint(i % X_COUNT, i / X_COUNT);
OnInventoryEntryChanged(ItemSlotPos, Item);
}
}
- Slot과 마찬가지로 Entry도 5x10 크기 만큼 할당하고
- Subsystem에서 만든 Inventory의 아이템들을 가져와서 ItemSlotPos 위치에 아이템을 배치
인벤토리에 아이템을 해당 위치에 배치하기
protected:
void OnInventoryEntryChanged(const FIntPoint& ItemSlotPos, TObjectPtr<UMyItemInstance> Item);
void UMyInventorySlotsWidget::OnInventoryEntryChanged(const FIntPoint& InItemSlotPos, TObjectPtr<UMyItemInstance> Item)
{
int32 SlotIndex = InItemSlotPos.Y * X_COUNT + InItemSlotPos.X;
if (UMyInventoryEntryWidget* EntryWidget = EntryWidgets[SlotIndex])
{
if (Item == nullptr)
{
CanvasPanel_Entries->RemoveChild(EntryWidget);
EntryWidgets[SlotIndex] = nullptr;
}
}
else
{
EntryWidget = CreateWidget<UMyInventoryEntryWidget>(GetOwningPlayer(), EntryWidgetClass);
EntryWidgets[SlotIndex] = EntryWidget;
UCanvasPanelSlot* CanvasPanelSlot = CanvasPanel_Entries->AddChildToCanvas(EntryWidget);
CanvasPanelSlot->SetAutoSize(true);
CanvasPanelSlot->SetPosition(FVector2D(InItemSlotPos.X * 50, InItemSlotPos.Y * 50));
EntryWidget->Init(this, Item, 1);
}
}
Drag & Drop 구현
DragDrop Operation을 상속받은 클래스 생성
public:
UMyDragDropOperation(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
public:
FIntPoint FromItemSlotPos = FIntPoint::ZeroValue;
UPROPERTY()
TObjectPtr<UR1ItemInstance> ItemInstance;
FVector2D DeltaWidgetPos = FVector2D::ZeroVector;
- InventoryEntryWidget: 인벤토리 위에 아이템이 그려지는 위젯
- ItemDragWidget: 드래그해서 들고있는 아이템을 그려지는 위젯
public:
UMyItemDragWidget(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
public:
void Init(const FVector2D& InWidgetSize, UTexture2D* InItemIcon, int32 InItemCount);
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<USizeBox> SizeBox_Root;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UImage> Image_Icon;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> Text_Count;
void UMyItemDragWidget::Init(const FVector2D& InWidgetSize, UTexture2D* InItemIcon, int32 InItemCount)
{
SizeBox_Root->SetWidthOverride(InWidgetSize.X);
SizeBox_Root->SetHeightOverride(InWidgetSize.Y);
Image_Icon->SetBrushFromTexture(InItemIcon);
Text_Count->SetText((InItemCount >= 2) ? FText::AsNumber(InItemCount) : FText::GetEmpty());
}
EntryWidget에서 마우스로 누른 아이템의 위치 가져오기
FReply UMyInventoryEntryWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
FReply Reply = Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
{
Reply.DetectDrag(TakeWidget(), EKeys::LeftMouseButton);
}
const FIntPoint UnitInventorySlotSize = FIntPoint(50, 50);
FVector2D MouseWidgetPos = SlotsWidget->GetCachedGeometry().AbsoluteToLocal(InMouseEvent.GetScreenSpacePosition());
FVector2D ItemWidgetPos = SlotsWidget->GetCachedGeometry().AbsoluteToLocal(InGeometry.LocalToAbsolute(UnitInventorySlotSize / 2.f));
FIntPoint ItemSlotPos = FIntPoint(ItemWidgetPos.X / UnitInventorySlotSize.X, ItemWidgetPos.Y / UnitInventorySlotSize.Y);
CachedFromSlotPos = ItemSlotPos;
CachedDeltaWidgetPos = MouseWidgetPos - ItemWidgetPos;
return Reply;
}
EntryWidget에서 드래그 시 드래그 위젯생성
protected:
UPROPERTY()
TSubclassOf<UMyItemDragWidget> DragWidgetClass;
void UMyInventoryEntryWidget::NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, OUT UDragDropOperation*& OutOperation)
{
Super::NativeOnDragDetected(InGeometry, InMouseEvent, OutOperation);
UMyItemDragWidget* DragWidget = CreateWidget<UMyItemDragWidget>(GetOwningPlayer(), DragWidgetClass);
FVector2D EntityWidgetSize = FVector2D(1 * 50, 1 * 50);
DragWidget->Init(EntityWidgetSize, nullptr, ItemCount);
UMyDragDropOperation* DragDrop = NewObject<UMyDragDropOperation>();
DragDrop->DefaultDragVisual = DragWidget;
DragDrop->Pivot = EDragPivot::MouseDown;
DragDrop->FromItemSlotPos = CachedFromSlotPos;
DragDrop->ItemInstance = ItemInstance;
DragDrop->DeltaWidgetPos = CachedDeltaWidgetPos;
OutOperation = DragDrop;
}
아이템 개수와 투명도를 조절하는 기능 추가
protected:
void RefreshWidgetOpacity(bool bClearlyVisible);
void RefreshItemCount(int32 NewItemCount);
void UMyInventoryEntryWidget::RefreshWidgetOpacity(bool bClearlyVisible)
{
SetRenderOpacity(bClearlyVisible ? 1.f : 0.5f);
}
void UMyInventoryEntryWidget::RefreshItemCount(int32 NewItemCount)
{
ItemCount = NewItemCount;
Text_Count->SetText((ItemCount >= 2) ? FText::AsNumber(ItemCount) : FText::GetEmpty());
}
InventorySlots에 드래그 관련 함수 추가
protected:
virtual bool NativeOnDragOver(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
virtual void NativeOnDragLeave(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
private:
void FinishDrag();
private:
FIntPoint PrevDragOverSlotPos = FIntPoint(-1, -1);
bool UMyInventorySlotsWidget::NativeOnDragOver(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
Super::NativeOnDragOver(InGeometry, InDragDropEvent, InOperation);
UMyDragDropOperation* DragDrop = Cast<UMyDragDropOperation>(InOperation);
check(DragDrop);
FVector2D MouseWidgetPos = InGeometry.AbsoluteToLocal(InDragDropEvent.GetScreenSpacePosition());
FVector2D ToWidgetPos = MouseWidgetPos - DragDrop->DeltaWidgetPos;
FIntPoint ToSlotPos = FIntPoint(ToWidgetPos.X / 50.f, ToWidgetPos.Y / 50.f);
if (PrevDragOverSlotPos == ToSlotPos)
{
return true;
}
PrevDragOverSlotPos = ToSlotPos;
return false;
}
void UMyInventorySlotsWidget::NativeOnDragLeave(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
Super::NativeOnDragLeave(InDragDropEvent, InOperation);
FinishDrag();
}
bool UMyInventorySlotsWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
FinishDrag();
UMyDragDropOperation* DragDrop = Cast<UMyDragDropOperation>(InOperation);
check(DragDrop);
FVector2D MouseWidgetPos = InGeometry.AbsoluteToLocal(InDragDropEvent.GetScreenSpacePosition());
FVector2D ToWidgetPos = MouseWidgetPos - DragDrop->DeltaWidgetPos;
FIntPoint ToItemSlotPos = FIntPoint(ToWidgetPos.X / Item::UnitInventorySlotSize.X, ToWidgetPos.Y / Item::UnitInventorySlotSize.Y);
if (DragDrop->FromItemSlotPos != ToItemSlotPos)
{
OnInventoryEntryChanged(DragDrop->FromItemSlotPos, nullptr);
OnInventoryEntryChanged(ToItemSlotPos, DragDrop->ItemInstance);
}
return false;
}
void UMyInventorySlotsWidget::FinishDrag()
{
PrevDragOverSlotPos = FIntPoint(-1, -1);
}