Füge neues Inventar- und Ausrüstungs-UI-System hinzu
Ein neues modulares Inventar- und Ausrüstungs-UI-System wurde implementiert, das folgende Hauptfunktionen bietet: - Unterstützung für Ausrüstungs-Slots (`UEquipmentWidget`, `UEquipmentSlotWidget`). - Darstellung von Inventargegenständen in Listen- oder Rasteransicht (`UInventoryItemListWidget`, `UInventoryGridWidget`). - Schnellzugriffsleiste für große und kleine Slots (`UQuickSlotListWidget`). - Verwaltung von Inventarbehältern mit zusammenklappbaren Bereichen (`UInventoryContainerListWidget`). Neue Datenstrukturen (`InventoryTypes.h`) definieren Enums und Strukturen für Gegenstände, Ausrüstungs-Slots und Behälter. Der `PlayerController` wurde erweitert, um das Inventar-UI zu öffnen/schließen (`ToggleInventory`). Neue `.uasset`-Dateien wurden hinzugefügt, um die UI-Komponenten zu unterstützen.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InventoryTypes.generated.h"
|
||||
|
||||
class UTexture2D;
|
||||
|
||||
/**
|
||||
* Where an equipped item is displayed. Purely a display label for WBP_EquipmentSlot - there is
|
||||
* no attachment-point/socket hierarchy and no validation that an item is appropriate for the
|
||||
* slot it's put in. Multiple slots can share the same type (e.g. two Back slots, two Belt
|
||||
* slots) - disambiguate those via FEquipmentSlotData::SlotDisplayNameOverride.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EEquipmentSlotType : uint8
|
||||
{
|
||||
Head,
|
||||
Body,
|
||||
Hands,
|
||||
Legs,
|
||||
Feet,
|
||||
Back,
|
||||
Belt
|
||||
};
|
||||
|
||||
/**
|
||||
* A single inventory/equipment/quick-slot item as far as the UI is concerned. Mass and Volume
|
||||
* describe the item's physical footprint for weight/capacity tracking - they never affect how
|
||||
* large the item appears on screen, since every item always renders at the same fixed 2x2 UI
|
||||
* size (see UInventoryItemWidget). An ItemID of NAME_None means the slot is empty.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FItemData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
FName ItemID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
FText DisplayName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
TObjectPtr<UTexture2D> Icon = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
int32 Quantity = 1;
|
||||
|
||||
/** Physical mass in kg, used for carry-weight totals - not UI size. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
float Mass = 0.0f;
|
||||
|
||||
/** Physical volume in liters, used for carry-capacity totals - not UI size. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
float Volume = 0.0f;
|
||||
|
||||
/** Optional basic state label shown alongside the item (e.g. "Broken", "New", "Equipped"). Empty means no state to show. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
FText StateText;
|
||||
|
||||
bool IsEmpty() const { return ItemID.IsNone(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* One equipment slot's contents. SlotType is a display label only - WBP_EquipmentSlot shows
|
||||
* whatever it's given here and does not validate that EquippedItem actually belongs in SlotType.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FEquipmentSlotData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment")
|
||||
EEquipmentSlotType SlotType = EEquipmentSlotType::Head;
|
||||
|
||||
/** Optional override shown instead of SlotType's enum name - use this to tell apart slots that share a type (e.g. "Back 1"/"Back 2", "Belt 1"/"Belt 2"). Empty falls back to the SlotType display name. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment")
|
||||
FText SlotDisplayNameOverride;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment")
|
||||
FItemData EquippedItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* One inventory container's contents (the player's own bag, a chest being looted, ...). Items
|
||||
* are a flat, ordered list - there is no physical/spatial grid position per item, only UI-list
|
||||
* ordering (no Tetris-style placement).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FInventoryContainerInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
|
||||
FName ContainerID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
|
||||
FText DisplayName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
|
||||
TArray<FItemData> Items;
|
||||
};
|
||||
@@ -32,7 +32,9 @@ public class KingshearthLegacy : ModuleRules
|
||||
"KingshearthLegacy/AttributeSets",
|
||||
"KingshearthLegacy/GameplayEffects",
|
||||
"KingshearthLegacy/Interaction",
|
||||
"KingshearthLegacy/Inventory",
|
||||
"KingshearthLegacy/UI/Menu",
|
||||
"KingshearthLegacy/UI/Inventory",
|
||||
"KingshearthLegacy/Variant_Platforming",
|
||||
"KingshearthLegacy/Variant_Platforming/Animation",
|
||||
"KingshearthLegacy/Variant_Combat",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "UI/PlayerHUDWidget.h"
|
||||
#include "UI/Menu/InGameMenuWidget.h"
|
||||
#include "UI/Menu/MenuManagerSubsystem.h"
|
||||
#include "UI/Inventory/EquipmentInventoryTabWidget.h"
|
||||
#include "KingshearthLegacyCharacter.h"
|
||||
|
||||
void AKingshearthLegacyPlayerController::BeginPlay()
|
||||
@@ -70,6 +71,11 @@ void AKingshearthLegacyPlayerController::SetupInputComponent()
|
||||
{
|
||||
EnhancedInputComponent->BindAction(MenuAction, ETriggerEvent::Started, this, &AKingshearthLegacyPlayerController::ToggleInGameMenu);
|
||||
}
|
||||
|
||||
if (InventoryAction)
|
||||
{
|
||||
EnhancedInputComponent->BindAction(InventoryAction, ETriggerEvent::Started, this, &AKingshearthLegacyPlayerController::ToggleInventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,3 +143,27 @@ void AKingshearthLegacyPlayerController::ToggleInGameMenu()
|
||||
MenuManager->PushScreen(InGameMenuClass);
|
||||
}
|
||||
}
|
||||
|
||||
void AKingshearthLegacyPlayerController::ToggleInventory()
|
||||
{
|
||||
if (!IsLocalPlayerController() || !InventoryScreenClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UGameInstance* GI = GetGameInstance();
|
||||
UMenuManagerSubsystem* MenuManager = GI ? GI->GetSubsystem<UMenuManagerSubsystem>() : nullptr;
|
||||
if (!MenuManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MenuManager->IsAnyScreenOpen())
|
||||
{
|
||||
MenuManager->PopScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
MenuManager->PushScreen(InventoryScreenClass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class UInputAction;
|
||||
class UUserWidget;
|
||||
class UPlayerHUDWidget;
|
||||
class UInGameMenuWidget;
|
||||
class UEquipmentInventoryTabWidget;
|
||||
|
||||
/**
|
||||
* Basic PlayerController class for a third person game
|
||||
@@ -59,6 +60,14 @@ protected:
|
||||
UPROPERTY(EditAnywhere, Category = "Input|Input Mappings")
|
||||
TObjectPtr<UInputAction> MenuAction;
|
||||
|
||||
/** Inventory/equipment screen, pushed/popped through the menu manager by ToggleInventory */
|
||||
UPROPERTY(EditAnywhere, Category = "UI")
|
||||
TSubclassOf<UEquipmentInventoryTabWidget> InventoryScreenClass;
|
||||
|
||||
/** Input action that opens the inventory screen, or closes the top menu screen if one is already open */
|
||||
UPROPERTY(EditAnywhere, Category = "Input|Input Mappings")
|
||||
TObjectPtr<UInputAction> InventoryAction;
|
||||
|
||||
/** Gameplay initialization */
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
@@ -75,4 +84,8 @@ protected:
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
void ToggleInGameMenu();
|
||||
|
||||
/** Opens InventoryScreenClass, or closes the top menu screen if one is already open */
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
void ToggleInventory();
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "EquipmentInventoryTabWidget.h"
|
||||
#include "UI/Inventory/EquipmentWidget.h"
|
||||
#include "UI/Inventory/QuickSlotListWidget.h"
|
||||
#include "UI/Inventory/InventoryItemListWidget.h"
|
||||
#include "UI/Inventory/InventoryGridWidget.h"
|
||||
#include "KingshearthLegacy.h"
|
||||
#include "InputCoreTypes.h"
|
||||
|
||||
UEquipmentInventoryTabWidget::UEquipmentInventoryTabWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
// Needed so the menu manager's SetWidgetToFocus (see UMenuManagerSubsystem::RefreshInputMode)
|
||||
// can actually give this screen keyboard focus - otherwise NativeOnPreviewKeyDown below never fires.
|
||||
// Only takes effect set here, at construction - UUserWidget::SetIsFocusable asserts if called later.
|
||||
SetIsFocusable(true);
|
||||
}
|
||||
|
||||
FReply UEquipmentInventoryTabWidget::NativeOnPreviewKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent)
|
||||
{
|
||||
if (InKeyEvent.GetKey() == EKeys::Tab)
|
||||
{
|
||||
Close();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
return Super::NativeOnPreviewKeyDown(InGeometry, InKeyEvent);
|
||||
}
|
||||
|
||||
void UEquipmentInventoryTabWidget::InitializeInventory(const TArray<FEquipmentSlotData>& InEquipmentSlots, const TArray<FItemData>& InBigQuickSlots, const TArray<FItemData>& InSmallQuickSlots, const TArray<FItemData>& InInventoryItems)
|
||||
{
|
||||
UE_LOG(LogKingshearthLegacy, Log, TEXT("[Inventory] EquipmentInventoryTab InitializeInventory: WBP_Equipment bound: %s, WBP_QuickSlotList bound: %s, WBP_ItemList bound: %s, WBP_InventoryGrid bound: %s"),
|
||||
WBP_Equipment ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UEquipmentWidget"),
|
||||
WBP_QuickSlotList ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UQuickSlotListWidget"),
|
||||
WBP_ItemList ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UInventoryItemListWidget"),
|
||||
WBP_InventoryGrid ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UInventoryGridWidget"));
|
||||
|
||||
if (WBP_Equipment)
|
||||
{
|
||||
WBP_Equipment->SetEquipmentSlots(InEquipmentSlots);
|
||||
}
|
||||
|
||||
if (WBP_QuickSlotList)
|
||||
{
|
||||
WBP_QuickSlotList->SetQuickSlots(InBigQuickSlots, InSmallQuickSlots);
|
||||
}
|
||||
|
||||
if (WBP_ItemList)
|
||||
{
|
||||
WBP_ItemList->SetItems(InInventoryItems);
|
||||
}
|
||||
|
||||
if (WBP_InventoryGrid)
|
||||
{
|
||||
WBP_InventoryGrid->SetItems(InInventoryItems);
|
||||
}
|
||||
|
||||
ShowInventoryGridView();
|
||||
}
|
||||
|
||||
void UEquipmentInventoryTabWidget::ShowItemListView()
|
||||
{
|
||||
if (WBP_ItemList)
|
||||
{
|
||||
WBP_ItemList->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
|
||||
if (WBP_InventoryGrid)
|
||||
{
|
||||
WBP_InventoryGrid->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void UEquipmentInventoryTabWidget::ShowInventoryGridView()
|
||||
{
|
||||
if (WBP_ItemList)
|
||||
{
|
||||
WBP_ItemList->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
|
||||
if (WBP_InventoryGrid)
|
||||
{
|
||||
WBP_InventoryGrid->SetVisibility(ESlateVisibility::Visible);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UI/Menu/MenuScreenWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "EquipmentInventoryTabWidget.generated.h"
|
||||
|
||||
class UEquipmentWidget;
|
||||
class UQuickSlotListWidget;
|
||||
class UInventoryItemListWidget;
|
||||
class UInventoryGridWidget;
|
||||
|
||||
/**
|
||||
* Main inventory screen - a shallow wrapper coordinating its child widgets. Pushed/popped
|
||||
* through the menu manager like any other UMenuScreenWidget (see
|
||||
* AKingshearthLegacyPlayerController::ToggleInventory), so opening/closing it gets the usual UI
|
||||
* input mode + cursor handling for free, and an optional "BackButton" (inherited from
|
||||
* UMenuScreenWidget) closes it the same way any other menu screen closes:
|
||||
*
|
||||
* WBP_EquipmentInventoryTAB
|
||||
* |-- WBP_Equipment
|
||||
* | `-- WBP_EquipmentSlot
|
||||
* |-- WBP_QuickSlotList
|
||||
* `-- Inventory View
|
||||
* |-- WBP_ItemList
|
||||
* `-- WBP_InventoryGrid
|
||||
*
|
||||
* Owns no inventory logic itself - it only forwards data supplied by the inventory/equipment
|
||||
* system to its children, and switches which of WBP_ItemList / WBP_InventoryGrid is visible for
|
||||
* the "Inventory View" section (both are kept populated with the same items; only one is shown
|
||||
* at a time).
|
||||
*
|
||||
* Nest instances named exactly "WBP_Equipment", "WBP_QuickSlotList", "WBP_ItemList" and
|
||||
* "WBP_InventoryGrid" in the Designer tree, reparented to their respective C++ base classes.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UEquipmentInventoryTabWidget : public UMenuScreenWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UEquipmentInventoryTabWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** Populates every child widget. Call once the inventory/equipment system has data ready (e.g. right after this screen is pushed). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void InitializeInventory(const TArray<FEquipmentSlotData>& InEquipmentSlots, const TArray<FItemData>& InBigQuickSlots, const TArray<FItemData>& InSmallQuickSlots, const TArray<FItemData>& InInventoryItems);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void ShowItemListView();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void ShowInventoryGridView();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* While this screen is open, the menu manager puts input in UI-only mode - the PlayerController
|
||||
* stops receiving Tab presses entirely, so re-pressing Tab can only be caught here, at the
|
||||
* focused widget itself. Requires bIsFocusable (set true in the constructor) so the menu
|
||||
* manager's SetWidgetToFocus call actually succeeds.
|
||||
*
|
||||
* Uses the Preview (tunneling) pass rather than NativeOnKeyDown (bubbling) - Tab is Slate's
|
||||
* default "focus next widget" navigation key, and that default navigation runs as a fallback
|
||||
* once the bubbling pass goes unhandled, which pre-empts NativeOnKeyDown ever getting a
|
||||
* chance to close the screen. Intercepting in Preview, which runs before that fallback, avoids it.
|
||||
*/
|
||||
virtual FReply NativeOnPreviewKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
|
||||
/** Nested WBP_Equipment instance - named exactly "WBP_Equipment" in the Designer tree. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UEquipmentWidget> WBP_Equipment;
|
||||
|
||||
/** Nested WBP_QuickSlotList instance - named exactly "WBP_QuickSlotList" in the Designer tree. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UQuickSlotListWidget> WBP_QuickSlotList;
|
||||
|
||||
/** Nested WBP_ItemList instance - named exactly "WBP_ItemList" in the Designer tree, part of the Inventory View toggle. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UInventoryItemListWidget> WBP_ItemList;
|
||||
|
||||
/** Nested WBP_InventoryGrid instance - named exactly "WBP_InventoryGrid" in the Designer tree, part of the Inventory View toggle. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UInventoryGridWidget> WBP_InventoryGrid;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "EquipmentSlotListEntry.generated.h"
|
||||
|
||||
/**
|
||||
* Plain UObject wrapper around one FEquipmentSlotData - UListView items must be UObjects, not
|
||||
* structs. Used by WBP_Equipment before calling SetListItems, so each row's UEquipmentSlotWidget
|
||||
* receives one of these via IUserObjectListEntry.
|
||||
*/
|
||||
UCLASS()
|
||||
class UEquipmentSlotListEntry : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Equipment")
|
||||
FEquipmentSlotData Slot;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "EquipmentSlotWidget.h"
|
||||
#include "UI/Inventory/EquipmentSlotListEntry.h"
|
||||
#include "Components/Image.h"
|
||||
#include "Components/TextBlock.h"
|
||||
|
||||
void UEquipmentSlotWidget::SetSlotData(const FEquipmentSlotData& InSlotData)
|
||||
{
|
||||
SlotData = InSlotData;
|
||||
ApplySlotVisuals();
|
||||
}
|
||||
|
||||
void UEquipmentSlotWidget::NativeOnListItemObjectSet(UObject* ListItemObject)
|
||||
{
|
||||
if (const UEquipmentSlotListEntry* ListItem = Cast<UEquipmentSlotListEntry>(ListItemObject))
|
||||
{
|
||||
SetSlotData(ListItem->Slot);
|
||||
}
|
||||
}
|
||||
|
||||
void UEquipmentSlotWidget::ApplySlotVisuals()
|
||||
{
|
||||
const bool bHasItem = !SlotData.EquippedItem.IsEmpty();
|
||||
|
||||
if (Icon)
|
||||
{
|
||||
if (bHasItem && SlotData.EquippedItem.Icon)
|
||||
{
|
||||
Icon->SetBrushFromTexture(SlotData.EquippedItem.Icon);
|
||||
Icon->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (QuantityText)
|
||||
{
|
||||
if (bHasItem && SlotData.EquippedItem.Quantity > 1)
|
||||
{
|
||||
QuantityText->SetText(FText::AsNumber(SlotData.EquippedItem.Quantity));
|
||||
QuantityText->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
QuantityText->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (SlotLabel)
|
||||
{
|
||||
SlotLabel->SetText(!SlotData.SlotDisplayNameOverride.IsEmpty()
|
||||
? SlotData.SlotDisplayNameOverride
|
||||
: UEnum::GetDisplayValueAsText(SlotData.SlotType));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Blueprint/IUserObjectListEntry.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "EquipmentSlotWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* A single equipment slot's visuals. This is a display slot only - it shows whatever
|
||||
* FEquipmentSlotData it is given and does not validate that the item is appropriate for the
|
||||
* slot, and does not represent an attachment point/socket on the character mesh.
|
||||
*
|
||||
* Name your widgets exactly "Icon" (Image), "QuantityText" (TextBlock) and "SlotLabel"
|
||||
* (TextBlock) in the Designer and population is handled here in C++; no BP event graph logic
|
||||
* needed.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UEquipmentSlotWidget : public UUserWidget, public IUserObjectListEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Equipment")
|
||||
void SetSlotData(const FEquipmentSlotData& InSlotData);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Equipment")
|
||||
const FEquipmentSlotData& GetSlotData() const { return SlotData; }
|
||||
|
||||
protected:
|
||||
|
||||
/** IUserObjectListEntry - called by the owning ListView (WBP_Equipment) when this row is assigned an UEquipmentSlotListEntry. */
|
||||
virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override;
|
||||
|
||||
void ApplySlotVisuals();
|
||||
|
||||
/** Equipped item's icon - bind an Image named exactly "Icon" in the Designer. Hidden automatically when the slot is empty. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UImage> Icon;
|
||||
|
||||
/** Equipped item's stack count - bind a TextBlock named exactly "QuantityText" in the Designer. Hidden automatically when Quantity <= 1. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> QuantityText;
|
||||
|
||||
/** Slot type label (e.g. "Head", "Back 1") - bind a TextBlock named exactly "SlotLabel" in the Designer. Prefers SlotData.SlotDisplayNameOverride when set, falling back to the SlotType enum name. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> SlotLabel;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Equipment")
|
||||
FEquipmentSlotData SlotData;
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "EquipmentWidget.h"
|
||||
#include "UI/Inventory/EquipmentSlotListEntry.h"
|
||||
#include "Components/ListView.h"
|
||||
|
||||
void UEquipmentWidget::SetEquipmentSlots(const TArray<FEquipmentSlotData>& InSlots)
|
||||
{
|
||||
if (!SlotsList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SlotsList->ClearListItems();
|
||||
ListItems.Reset(InSlots.Num());
|
||||
|
||||
TArray<UObject*> SlotObjects;
|
||||
SlotObjects.Reserve(InSlots.Num());
|
||||
|
||||
for (const FEquipmentSlotData& SlotData : InSlots)
|
||||
{
|
||||
UEquipmentSlotListEntry* Entry = NewObject<UEquipmentSlotListEntry>(this);
|
||||
Entry->Slot = SlotData;
|
||||
ListItems.Add(Entry);
|
||||
SlotObjects.Add(Entry);
|
||||
}
|
||||
|
||||
SlotsList->SetListItems(SlotObjects);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "EquipmentWidget.generated.h"
|
||||
|
||||
class UEquipmentSlotListEntry;
|
||||
class UListView;
|
||||
|
||||
/**
|
||||
* The character's full set of equipment slots.
|
||||
*
|
||||
* Name a ListView exactly "SlotsList" in the Designer, with its Entry Widget Class set to a WBP
|
||||
* subclass of UEquipmentSlotWidget, and population is handled here in C++.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UEquipmentWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Equipment")
|
||||
void SetEquipmentSlots(const TArray<FEquipmentSlotData>& InSlots);
|
||||
|
||||
protected:
|
||||
|
||||
/** Named exactly "SlotsList" in the Designer, Entry Widget Class set to a WBP subclass of UEquipmentSlotWidget. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UListView> SlotsList;
|
||||
|
||||
/** UObject wrappers backing SlotsList - kept alive here so the ListView's UObjects don't get GC'd. */
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UEquipmentSlotListEntry>> ListItems;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "InventoryContainerListWidget.h"
|
||||
#include "UI/Inventory/InventoryGridWidget.h"
|
||||
#include "Components/TextBlock.h"
|
||||
|
||||
void UInventoryContainerListWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
SetExpanded(bExpanded);
|
||||
}
|
||||
|
||||
void UInventoryContainerListWidget::SetContainerData(const FInventoryContainerInfo& InContainerData)
|
||||
{
|
||||
ContainerData = InContainerData;
|
||||
|
||||
if (ContainerLabel)
|
||||
{
|
||||
ContainerLabel->SetText(ContainerData.DisplayName);
|
||||
}
|
||||
|
||||
if (WBP_InventoryGrid)
|
||||
{
|
||||
WBP_InventoryGrid->SetItems(ContainerData.Items);
|
||||
}
|
||||
}
|
||||
|
||||
void UInventoryContainerListWidget::SetExpanded(bool bInExpanded)
|
||||
{
|
||||
bExpanded = bInExpanded;
|
||||
|
||||
if (WBP_InventoryGrid)
|
||||
{
|
||||
WBP_InventoryGrid->SetVisibility(bExpanded ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void UInventoryContainerListWidget::ToggleExpanded()
|
||||
{
|
||||
SetExpanded(!bExpanded);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "InventoryContainerListWidget.generated.h"
|
||||
|
||||
class UInventoryGridWidget;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* A labeled, collapsible view of one inventory container (the player's own bag, a chest being
|
||||
* looted, ...):
|
||||
*
|
||||
* WBP_InventoryContainerList
|
||||
* |-- Container Label
|
||||
* `-- Collapsible Inventory Grid
|
||||
* `-- WBP_InventoryGrid
|
||||
* `-- WBP_Item
|
||||
*
|
||||
* Name a TextBlock exactly "ContainerLabel" in the Designer, and nest a WBP subclass of
|
||||
* UInventoryGridWidget named exactly "WBP_InventoryGrid". SetContainerData populates the label
|
||||
* and forwards the container's items to the nested grid; SetExpanded/ToggleExpanded show or
|
||||
* collapse it. Wire an expand/collapse button to ToggleExpanded in the Designer/event graph.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UInventoryContainerListWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void SetContainerData(const FInventoryContainerInfo& InContainerData);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventory")
|
||||
const FInventoryContainerInfo& GetContainerData() const { return ContainerData; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void SetExpanded(bool bInExpanded);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void ToggleExpanded();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventory")
|
||||
bool IsExpanded() const { return bExpanded; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
/** Named exactly "ContainerLabel" in the Designer. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> ContainerLabel;
|
||||
|
||||
/** Nested WBP_InventoryGrid instance - named exactly "WBP_InventoryGrid" in the Designer tree, reparented to a UInventoryGridWidget subclass. Collapsed/shown by SetExpanded. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UInventoryGridWidget> WBP_InventoryGrid;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Inventory")
|
||||
FInventoryContainerInfo ContainerData;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Inventory")
|
||||
bool bExpanded = true;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "InventoryGridWidget.h"
|
||||
#include "UI/Inventory/InventoryItemListEntry.h"
|
||||
#include "Components/TileView.h"
|
||||
|
||||
void UInventoryGridWidget::SetItems(const TArray<FItemData>& InItems)
|
||||
{
|
||||
Items = InItems;
|
||||
PopulateGrid();
|
||||
}
|
||||
|
||||
void UInventoryGridWidget::PopulateGrid()
|
||||
{
|
||||
if (!ItemsGrid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ItemsGrid->ClearListItems();
|
||||
ListItems.Reset(Items.Num());
|
||||
|
||||
TArray<UObject*> ItemObjects;
|
||||
ItemObjects.Reserve(Items.Num());
|
||||
|
||||
for (const FItemData& Item : Items)
|
||||
{
|
||||
UInventoryItemListEntry* Entry = NewObject<UInventoryItemListEntry>(this);
|
||||
Entry->Item = Item;
|
||||
ListItems.Add(Entry);
|
||||
ItemObjects.Add(Entry);
|
||||
}
|
||||
|
||||
ItemsGrid->SetListItems(ItemObjects);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "InventoryGridWidget.generated.h"
|
||||
|
||||
class UInventoryItemListEntry;
|
||||
class UTileView;
|
||||
|
||||
/**
|
||||
* A UI grid of items. This is purely a tiled layout - every item occupies the same fixed-size
|
||||
* 2x2 tile no matter its Mass/Volume, and items have no spatial position of their own (no
|
||||
* physical/Tetris-style grid placement). Carries no container/label concept of its own - that's
|
||||
* WBP_InventoryContainerList's job when a labeled, collapsible section is needed.
|
||||
*
|
||||
* Name a TileView exactly "ItemsGrid" in the Designer, with its Entry Widget Class set to a WBP
|
||||
* subclass of UInventoryItemWidget, and population is handled here in C++.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UInventoryGridWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventory")
|
||||
void SetItems(const TArray<FItemData>& InItems);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventory")
|
||||
const TArray<FItemData>& GetItems() const { return Items; }
|
||||
|
||||
protected:
|
||||
|
||||
void PopulateGrid();
|
||||
|
||||
/** Named exactly "ItemsGrid" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTileView> ItemsGrid;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Inventory")
|
||||
TArray<FItemData> Items;
|
||||
|
||||
/** UObject wrappers backing ItemsGrid - kept alive here so the TileView's UObjects don't get GC'd. */
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UInventoryItemListEntry>> ListItems;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "InventoryItemListEntry.generated.h"
|
||||
|
||||
/**
|
||||
* Plain UObject wrapper around one FItemData - UListView/UTileView items must be UObjects, not
|
||||
* structs. Used by WBP_ItemList, WBP_InventoryGrid and WBP_QuickSlotList before calling
|
||||
* SetListItems, so each row's UInventoryItemWidget receives one of these via
|
||||
* IUserObjectListEntry.
|
||||
*/
|
||||
UCLASS()
|
||||
class UInventoryItemListEntry : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Item")
|
||||
FItemData Item;
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "InventoryItemListWidget.h"
|
||||
#include "UI/Inventory/InventoryItemListEntry.h"
|
||||
#include "Components/ListView.h"
|
||||
|
||||
void UInventoryItemListWidget::SetItems(const TArray<FItemData>& InItems)
|
||||
{
|
||||
if (!ItemsList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ItemsList->ClearListItems();
|
||||
ListItems.Reset(InItems.Num());
|
||||
|
||||
TArray<UObject*> ItemObjects;
|
||||
ItemObjects.Reserve(InItems.Num());
|
||||
|
||||
for (const FItemData& Item : InItems)
|
||||
{
|
||||
UInventoryItemListEntry* Entry = NewObject<UInventoryItemListEntry>(this);
|
||||
Entry->Item = Item;
|
||||
ListItems.Add(Entry);
|
||||
ItemObjects.Add(Entry);
|
||||
}
|
||||
|
||||
ItemsList->SetListItems(ItemObjects);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "InventoryItemListWidget.generated.h"
|
||||
|
||||
class UInventoryItemListEntry;
|
||||
class UListView;
|
||||
|
||||
/**
|
||||
* A simple vertical list of items - not a physical/spatial grid, just an ordered list. Used as
|
||||
* the list-view alternative to UInventoryGridWidget inside WBP_EquipmentInventoryTAB's Inventory
|
||||
* View (see UEquipmentInventoryTabWidget::ShowItemListView), and reusable standalone anywhere
|
||||
* else that only needs a plain item list (a crafting recipe's ingredients, a shop's stock, ...).
|
||||
*
|
||||
* Name a ListView exactly "ItemsList" in the Designer, with its Entry Widget Class set to a WBP
|
||||
* subclass of UInventoryItemWidget, and population is handled here in C++.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UInventoryItemListWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Item List")
|
||||
void SetItems(const TArray<FItemData>& InItems);
|
||||
|
||||
protected:
|
||||
|
||||
/** Named exactly "ItemsList" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UListView> ItemsList;
|
||||
|
||||
/** UObject wrappers backing ItemsList - kept alive here so the ListView's UObjects don't get GC'd. */
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UInventoryItemListEntry>> ListItems;
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "InventoryItemWidget.h"
|
||||
#include "UI/Inventory/InventoryItemListEntry.h"
|
||||
#include "Components/Image.h"
|
||||
#include "Components/TextBlock.h"
|
||||
|
||||
void UInventoryItemWidget::SetItemData(const FItemData& InItemData)
|
||||
{
|
||||
ItemData = InItemData;
|
||||
ApplyItemVisuals();
|
||||
}
|
||||
|
||||
void UInventoryItemWidget::NativeOnListItemObjectSet(UObject* ListItemObject)
|
||||
{
|
||||
if (const UInventoryItemListEntry* ListItem = Cast<UInventoryItemListEntry>(ListItemObject))
|
||||
{
|
||||
SetItemData(ListItem->Item);
|
||||
}
|
||||
}
|
||||
|
||||
void UInventoryItemWidget::ApplyItemVisuals()
|
||||
{
|
||||
if (Icon)
|
||||
{
|
||||
if (!ItemData.IsEmpty() && ItemData.Icon)
|
||||
{
|
||||
Icon->SetBrushFromTexture(ItemData.Icon);
|
||||
Icon->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (ItemNameText)
|
||||
{
|
||||
if (!ItemData.IsEmpty())
|
||||
{
|
||||
ItemNameText->SetText(ItemData.DisplayName);
|
||||
ItemNameText->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemNameText->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (QuantityText)
|
||||
{
|
||||
if (!ItemData.IsEmpty() && ItemData.Quantity > 1)
|
||||
{
|
||||
QuantityText->SetText(FText::AsNumber(ItemData.Quantity));
|
||||
QuantityText->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
QuantityText->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (StateText)
|
||||
{
|
||||
if (!ItemData.IsEmpty() && !ItemData.StateText.IsEmpty())
|
||||
{
|
||||
StateText->SetText(ItemData.StateText);
|
||||
StateText->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateText->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Blueprint/IUserObjectListEntry.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "InventoryItemWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* A single inventory/equipment/quick-slot item. Always renders at the same fixed UI size
|
||||
* regardless of the item's Mass/Volume - physical footprint is inventory-property data, not a
|
||||
* UI dimension. Reused as the entry widget class for WBP_ItemList, WBP_InventoryGrid and
|
||||
* WBP_QuickSlotList (via IUserObjectListEntry), and can also be placed directly and driven with
|
||||
* SetItemData.
|
||||
*
|
||||
* Name your widgets exactly "Icon" (Image), "ItemNameText" (TextBlock), "QuantityText"
|
||||
* (TextBlock) and "StateText" (TextBlock) in the Designer and population is handled here in
|
||||
* C++; no BP event graph logic needed.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UInventoryItemWidget : public UUserWidget, public IUserObjectListEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Item")
|
||||
void SetItemData(const FItemData& InItemData);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Item")
|
||||
const FItemData& GetItemData() const { return ItemData; }
|
||||
|
||||
protected:
|
||||
|
||||
/** IUserObjectListEntry - called by the owning ListView/TileView when this row is assigned an UInventoryItemListEntry. */
|
||||
virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override;
|
||||
|
||||
void ApplyItemVisuals();
|
||||
|
||||
/** Item icon - bind an Image named exactly "Icon" in the Designer. Hidden automatically when the item slot is empty. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UImage> Icon;
|
||||
|
||||
/** Item name - bind a TextBlock named exactly "ItemNameText" in the Designer. Hidden automatically when the item slot is empty. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> ItemNameText;
|
||||
|
||||
/** Stack count - bind a TextBlock named exactly "QuantityText" in the Designer. Hidden automatically when Quantity <= 1. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> QuantityText;
|
||||
|
||||
/** Basic item state (e.g. "Broken", "New") - bind a TextBlock named exactly "StateText" in the Designer. Hidden automatically when the item has no StateText set. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTextBlock> StateText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Item")
|
||||
FItemData ItemData;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "QuickSlotListWidget.h"
|
||||
#include "UI/Inventory/InventoryItemListEntry.h"
|
||||
#include "Components/TileView.h"
|
||||
|
||||
void UQuickSlotListWidget::SetQuickSlots(const TArray<FItemData>& InBigQuickSlots, const TArray<FItemData>& InSmallQuickSlots)
|
||||
{
|
||||
PopulateSlots(BigQuickSlotsView, InBigQuickSlots, BigListItems);
|
||||
PopulateSlots(SmallQuickSlotsView, InSmallQuickSlots, SmallListItems);
|
||||
}
|
||||
|
||||
void UQuickSlotListWidget::PopulateSlots(UTileView* TargetView, const TArray<FItemData>& InSlots, TArray<TObjectPtr<UInventoryItemListEntry>>& OutListItems)
|
||||
{
|
||||
if (!TargetView)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TargetView->ClearListItems();
|
||||
OutListItems.Reset(InSlots.Num());
|
||||
|
||||
TArray<UObject*> ItemObjects;
|
||||
ItemObjects.Reserve(InSlots.Num());
|
||||
|
||||
for (const FItemData& Item : InSlots)
|
||||
{
|
||||
UInventoryItemListEntry* Entry = NewObject<UInventoryItemListEntry>(this);
|
||||
Entry->Item = Item;
|
||||
OutListItems.Add(Entry);
|
||||
ItemObjects.Add(Entry);
|
||||
}
|
||||
|
||||
TargetView->SetListItems(ItemObjects);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Inventory/InventoryTypes.h"
|
||||
#include "QuickSlotListWidget.generated.h"
|
||||
|
||||
class UInventoryItemListEntry;
|
||||
class UTileView;
|
||||
|
||||
/**
|
||||
* The player's quick-access hotbar: a row of "big" slots (primary, keybound actions) and a row
|
||||
* of "small" slots (secondary/utility). Both still render items at the standard fixed 2x2 size -
|
||||
* "big"/"small" describes the slot's role and on-screen footprint, not item icon scaling. Pass
|
||||
* an entry with an empty FItemData (see FItemData::IsEmpty) for slots that have nothing
|
||||
* assigned. The slot arrays may include slots granted by equipped items (e.g. a backpack) -
|
||||
* that's the inventory/equipment system's decision, this widget just displays whatever it's
|
||||
* given.
|
||||
*
|
||||
* Name two TileViews exactly "BigQuickSlotsView" and "SmallQuickSlotsView" in the Designer, both
|
||||
* with their Entry Widget Class set to a WBP subclass of UInventoryItemWidget (the same class
|
||||
* used by WBP_ItemList/WBP_InventoryGrid), and population is handled here in C++.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class UQuickSlotListWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Quick Slots")
|
||||
void SetQuickSlots(const TArray<FItemData>& InBigQuickSlots, const TArray<FItemData>& InSmallQuickSlots);
|
||||
|
||||
protected:
|
||||
|
||||
void PopulateSlots(UTileView* TargetView, const TArray<FItemData>& InSlots, TArray<TObjectPtr<UInventoryItemListEntry>>& OutListItems);
|
||||
|
||||
/** Named exactly "BigQuickSlotsView" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTileView> BigQuickSlotsView;
|
||||
|
||||
/** Named exactly "SmallQuickSlotsView" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true))
|
||||
TObjectPtr<UTileView> SmallQuickSlotsView;
|
||||
|
||||
/** UObject wrappers backing BigQuickSlotsView - kept alive here so the TileView's UObjects don't get GC'd. */
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UInventoryItemListEntry>> BigListItems;
|
||||
|
||||
/** UObject wrappers backing SmallQuickSlotsView - kept alive here so the TileView's UObjects don't get GC'd. */
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UInventoryItemListEntry>> SmallListItems;
|
||||
};
|
||||
Reference in New Issue
Block a user