Adding UI - Entitlements Essentials - (Unreal Engine module)
What's on the menu
In this section, you'll learn how to prepare the widgets used to display items owned by the player. The related files are available in the Resources section.
Owned count widget
OwnedCountWidgetEntry_Starter
: A C++ class used to display item details. You won't need to modify this menu, as it’s provided as a ready-to-use component.- Header file:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.h
- CPP file:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.cpp
- Blueprint widget:
Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_OwnedIndicator_Starter.uasset
- Header file:
The OwnedCountWidgetEntry_Starter
shows how many units of a certain item the player owns. For durable items, it will display owned instead of a count. This widget is attached to the W_StoreItemEntry
widget using the Byte Wars modular system. That means W_StoreItemEntry
will always include OwnedCountWidgetEntry_Starter
when the entitlements module is active.
Here is a preview of the OwnedCountWidgetEntry_Starter
Blueprint widget:
The components used in this widget are defined in the OwnedCountWidgetEntry_Starter
class header file.
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_OwnedCount;
This widget includes pre-configured variables and helper functions tailored for Byte Wars integration.
- Reference to the subsystem that handles the entitlement logic.
private:
UPROPERTY()
UEntitlementsEssentialsSubsystem_Starter* EntitlementsSubsystem;
void UOwnedCountWidgetEntry_Starter::NativeOnActivated()
{
// ...
EntitlementsSubsystem = GetGameInstance()->GetSubsystem<UEntitlementsEssentialsSubsystem_Starter>();
ensure(EntitlementsSubsystem);
// ...
}
- Reference to its parent widget,
W_StoreItemEntry
. Since this widget is only used within that context, it safely assumesW_StoreItemEntry
as its parent.
private:
UPROPERTY()
UStoreItemListEntry* W_Parent;
- Function to check if an item is equipped and visually highlight it. This is a Byte Wars specific feature, not part of the service.
private:
// ...
UFUNCTION()
void CheckItemEquipped();
Inventory widget
InventoryWidget_Starter
: A C++ class used to display item details. You won't need to modify this menu, as it’s provided as a ready-to-use component.- Header file:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/InventoryWidget_Starter.h
- CPP file:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/InventoryWidget_Starter.cpp
- Blueprint widgets:
Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_Inventory_PowerUps_Starter.uasset
Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_Inventory_Cosmetics_Starter.uasset
- Header file:
The InventoryWidget_Starter
displays all items the player owns. Durable items are shown in W_Inventory_Cosmetics_Starter
, while consumable items are shown in W_Inventory_PowerUps_Starter
. These item types behave differently in-game, which is why they are separated.
Here is a preview of the W_Inventory_PowerUps_Starter
Blueprint widget:
Here is a preview of the W_Inventory_Cosmetics_Starter
Blueprint widget:
The components used in this widget are defined in the OwnedCountWidgetEntry_Starter
class header file.
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UMediaPlayer* MediaPlayer;
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Root;
UPROPERTY(EditAnywhere)
FSlateBrush DefaultPreviewImage;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UImage* W_SelectedItemPreview;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_SelectedItemTitle;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UMultiLineEditableText* Tb_Description;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTileView* Tv_Content;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Equip;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Unequip;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Back;
This widget includes pre-configured variables and helper functions tailored for Byte Wars integration.
- Reference to the subsystem that handles the entitlement logic.
private:
UPROPERTY()
UEntitlementsEssentialsSubsystem_Starter* EntitlementsSubsystem;
void UInventoryWidget_Starter::NativeOnActivated()
{
Super::NativeOnActivated();
EntitlementsSubsystem = GetGameInstance()->GetSubsystem<UEntitlementsEssentialsSubsystem_Starter>();
ensure(EntitlementsSubsystem);
// ...
}
- Reference to the selected
UStoreItemDataObject
data.
private:
UPROPERTY()
UStoreItemDataObject* SelectedItem;
- Variable that indicates which item type (durable or consumable) to display.
private:
// ...
UPROPERTY(EditAnywhere)
bool bIsConsumable = true;
- Functions to equip and unequip an item. These are Byte Wars specific and not part of the service.
private:
void OnClickListItem(UObject* Object);
void OnClickEquip() const;
void OnClickUnEquip() const;
- Delegates to allow other objects to respond when a specific action is performed.
public:
inline static FSimpleMulticastDelegate OnInventoryMenuDeactivated;
FSimpleMulticastDelegate OnEquipped;
FSimpleMulticastDelegate OnUnequipped;
Ready the menu
-
Open the
OwnedCountWidgetEntry_Starter
header file and add the following function declarations.private:
// ...
void RetrieveEntitlementWithForceRequest(const bool bForceRequest);
void ShowOwnedCount(const FOnlineError& Error, const UStoreItemDataObject* Entitlement); -
Open the
OwnedCountWidgetEntry_Starter
CPP file and implement theRetrieveEntitlementWithForceRequest()
function. For now, this function retrieves only the item represented by the widget. You'll expand this later to include entitlement retrieval.void UOwnedCountWidgetEntry_Starter::RetrieveEntitlementWithForceRequest(const bool bForceRequest)
{
SetVisibility(ESlateVisibility::Collapsed);
W_Parent = GetFirstOccurenceOuter<UStoreItemListEntry>();
if (W_Parent)
{
// Get item.
const UStoreItemDataObject* ItemData = W_Parent->GetItemData();
if (!ItemData)
{
return;
}
// ...
}
} -
Implement the
ShowOwnedCount()
function. This displays the number of items owned, or an Owned indicator if the item is durable, based on the backend response.void UOwnedCountWidgetEntry_Starter::ShowOwnedCount(const FOnlineError& Error, const UStoreItemDataObject* Entitlement)
{
if (!Error.bSucceeded || !Entitlement)
{
return;
}
// Set owned count.
FText Text;
if (Entitlement->GetCount() > 0)
{
Text = Entitlement->GetIsConsumable() ?
FText::FromString(FString::Printf(TEXT("%d x"), Entitlement->GetCount())) : TEXT_OWNED;
}
Tb_OwnedCount->SetText(Text);
// Show the owned count if not empty.
SetVisibility(Tb_OwnedCount->GetText().IsEmpty() ? ESlateVisibility::Collapsed : ESlateVisibility::Visible);
// Check if this item is equipped or not.
if (PARENT_WIDGET_CLASS* ParentWidget = GetFirstOccurenceOuter<PARENT_WIDGET_CLASS>())
{
CheckItemEquipped();
ParentWidget->OnEquipped.AddUObject(this, &ThisClass::CheckItemEquipped);
ParentWidget->OnUnequipped.AddUObject(this, &ThisClass::CheckItemEquipped);
}
} -
Find the
NativeOnActivated()
function and replace the existing implementation with the code below. This triggersRetrieveEntitlementWithForceRequest()
when the widget is activated and binds the function to the item purchase widget delegate, so the item count updates immediately after a successful purchase.void UOwnedCountWidgetEntry_Starter::NativeOnActivated()
{
Super::NativeOnActivated();
EntitlementsSubsystem = GetGameInstance()->GetSubsystem<UEntitlementsEssentialsSubsystem_Starter>();
ensure(EntitlementsSubsystem);
RetrieveEntitlementWithForceRequest(false);
UItemPurchaseWidget::OnPurchaseCompleteMulticastDelegate.AddWeakLambda(this, [this](const APlayerController* PC)
{
RetrieveEntitlementWithForceRequest(true);
});
UItemPurchaseWidget_Starter::OnPurchaseCompleteMulticastDelegate.AddWeakLambda(this, [this](const APlayerController* PC)
{
RetrieveEntitlementWithForceRequest(true);
});
} -
Navigate to the
NativeOnDeactivated()
function and replace the existing implementation with the code below. This unbinds all delegates used by the widget to ensure there are no lingering references when the widget is deactivated.void UOwnedCountWidgetEntry_Starter::NativeOnDeactivated()
{
Super::NativeOnDeactivated();
UItemPurchaseWidget::OnPurchaseCompleteMulticastDelegate.RemoveAll(this);
UItemPurchaseWidget_Starter::OnPurchaseCompleteMulticastDelegate.RemoveAll(this);
if (PARENT_WIDGET_CLASS* ParentWidget = GetFirstOccurenceOuter<PARENT_WIDGET_CLASS>())
{
ParentWidget->OnEquipped.RemoveAll(this);
ParentWidget->OnUnequipped.RemoveAll(this);
}
} -
Open the
InventoryWidget_Starter
header file and add the following function declarations.private:
// ...
void ShowEntitlements(const FOnlineError& Error, const TArray<UStoreItemDataObject*> Entitlements) const; -
Open the
InventoryWidget_Starter
CPP file and implement theShowEntitlements()
function. This function displays all items the player owns, filtered by thebIsConsumable
flag.void UInventoryWidget_Starter::ShowEntitlements(const FOnlineError& Error, const TArray<UStoreItemDataObject*> Entitlements) const
{
if (Error.bSucceeded)
{
// filter items
TArray<UStoreItemDataObject*> FilteredEntitlements = Entitlements;
FilteredEntitlements.RemoveAll([this](const UStoreItemDataObject* Item)
{
return bIsConsumable != Item->GetIsConsumable() || (Item->GetIsConsumable() && Item->GetCount() <= 0);
});
Tv_Content->ClearListItems();
Tv_Content->SetListItems(FilteredEntitlements);
Ws_Root->SetWidgetState(Tv_Content->GetListItems().IsEmpty()
? EAccelByteWarsWidgetSwitcherState::Empty
: EAccelByteWarsWidgetSwitcherState::Not_Empty);
Tv_Content->SetUserFocus(GetOwningPlayer());
}
else
{
Ws_Root->ErrorMessage = Error.ErrorMessage;
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error);
}
} -
Build the project and open it with the Unreal Editor once it's done.
-
Open
Content/TutorialModules/Monetization/EntitlementEssentials/DA_EntitlementsEssentials.uasset
and enable theIs Starter Mode Active
. Save the Data Asset.
Resources
- The files used in this tutorial section are available in the Byte Wars GitHub repository.
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.cpp
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_OwnedIndicator_Starter.uasset
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/InventoryWidget_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/InventoryWidget_Starter.cpp
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_Inventory_PowerUps_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_Inventory_Cosmetics_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/DA_EntitlementsEssentials.uasset