Adding UI - Store Item Purchase - (Unreal Engine module)
Game setup
To simplify the implementation, Byte Wars includes a custom UObject class named UStoreItemPriceDataObject, which stores only the essential price data for items. The data stored is shown below:
UCLASS(BlueprintType)
class UStoreItemPriceDataObject : public UObject
{
// ...
private:
UPROPERTY(EditAnywhere)
ECurrencyType CurrencyType;
UPROPERTY(EditAnywhere)
int64 RegularPrice;
UPROPERTY(EditAnywhere)
int64 FinalPrice;
};
What's on the menu
In this section, you'll learn how to prepare the widgets used to purchase items from the in-game store. The related files are available in the Resources section.
Item purchase button
ItemPurchaseButton: 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/StoreItemPurchase/UI/Component/ItemPurchaseButton.h - CPP file:
Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/UI/Component/ItemPurchaseButton.cpp - Blueprint widget:
Content/TutorialModules/Monetization/StoreItemPurchase/UI/Components/W_ItemPurchaseButton.uasset
- Header file:
The ItemPurchaseButton is a button that includes an item price widget component. You’ll later bind a function to trigger the purchase request to this widget’s pressed event, but the widget itself does not contain purchase-specific logic.
Here is a preview of the ItemPurchaseButton Blueprint widget:

The components used in this widget are defined in the ItemPurchaseButton class header file.
private:
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UStoreItemPriceListEntry* W_Price;
To initialize this widget, use the SetPrice() function. It updates the UI components based on the provided price data and amount multiplier.
public:
void SetPrice(const UStoreItemPriceDataObject* PriceData, const int32 PriceMultiplier = 1) const;
Item purchase widget
ItemPurchaseWidget_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/StoreItemPurchase/UI/ItemPurchaseWidget_Starter.h - CPP file:
Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/UI/ItemPurchaseWidget_Starter.cpp - Blueprint widget:
Content/TutorialModules/Monetization/StoreItemPurchase/UI/W_StoreItemPurchase_Starter.uasset
- Header file:
Here is a preview of the ItemPurchaseWidget_Starter Blueprint widget:

The components used in this widget are defined in the ItemPurchaseWidget_Starter class header file.
private:
// ...
UPROPERTY()
UStoreItemDetailWidget* W_Parent;
UPROPERTY(EditAnywhere)
TSubclassOf<UItemPurchaseButton> PurchaseButtonClass;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Root;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UPanelWidget* W_PurchaseButtonsOuter;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_Success;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_Error;
This widget includes pre-configured variables and helper functions tailored for Byte Wars integration.
- Variable that stores the item data represented by this purchase widget.
private:
// ...
UPROPERTY()
UStoreItemDataObject* StoreItemDataObject;
void UItemPurchaseWidget_Starter::NativeOnActivated()
{
// ...
W_Parent = GetFirstOccurenceOuter<UStoreItemDetailWidget>();
ensure(W_Parent);
StoreItemDataObject = W_Parent->StoreItemDataObject;
ensure(StoreItemDataObject);
// ...
}
- Reference to the subsystem that handles the item purchase logic.
private:
UPROPERTY()
UStoreItemPurchaseSubsystem_Starter* PurchaseSubsystem;
void UItemPurchaseWidget_Starter::NativeOnActivated()
{
// ...
PurchaseSubsystem = GetGameInstance()->GetSubsystem<UStoreItemPurchaseSubsystem_Starter>();
ensure(PurchaseSubsystem);
// ...
}
- Static delegate that lets other objects respond after a purchase is completed. This delegate will be used in the Entitlement Essentials module.
public:
inline static TMulticastDelegate<void(const APlayerController*)> OnPurchaseCompleteMulticastDelegate;
- Function to set up the item purchase buttons.
private:
void SetupPurchaseButtons(TArray<UStoreItemPriceDataObject*> Prices);
- Function to update the price shown on the purchase buttons based on the selected amount.
private:
// ...
void UpdatePrice(const int32 SelectedIndex);
- Function to get the currently selected amount.
private:
// ...
int32 GetSelectedAmount() const;
Ready the menu
-
Open the
ItemPurchaseWidget_Starterheader file and add the following function declarations.private:
// ...
void OnClickPurchase(const int32 PriceIndex) const;
void OnPurchaseComplete(const FOnlineError& Error) const; -
Open the
ItemPurchaseWidget_StarterCPP file and implement theOnClickPurchase()function. At this stage, it only shows the loading state. You’ll call the purchase request in this function later.void UItemPurchaseWidget_Starter::OnClickPurchase(const int32 PriceIndex) const
{
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Loading);
// ...
} -
Find the
SetupPurchaseButtons()function and replace the existing implementation with the code below. This code binds theOnClickPurchase()function to the item purchase button and set up the price to be shown.void UItemPurchaseWidget_Starter::SetupPurchaseButtons(TArray<UStoreItemPriceDataObject*> Prices)
{
W_PurchaseButtonsOuter->ClearChildren();
for (int i = 0; i < Prices.Num(); ++i)
{
UItemPurchaseButton* Entry = CreateWidget<UItemPurchaseButton>(this, PurchaseButtonClass);
ensure(Entry);
Entry->SetPrice(Prices[i], GetSelectedAmount());
Entry->OnClicked().AddUObject(this, &ThisClass::OnClickPurchase, i);
W_PurchaseButtonsOuter->AddChild(Entry);
}
} -
Implement the
OnPurchaseComplete()function. This function triggers theOnPurchaseCompleteMulticastDelegatedelegate so that other objects can respond as soon as the request completes. It also displays the result, whether it succeeded or failed.void UItemPurchaseWidget_Starter::OnPurchaseComplete(const FOnlineError& Error) const
{
OnPurchaseCompleteMulticastDelegate.Broadcast(GetOwningPlayer());
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Not_Empty);
if (Error.bSucceeded)
{
Tb_Success->SetVisibility(ESlateVisibility::Visible);
Tb_Error->SetVisibility(ESlateVisibility::Collapsed);
// update wallet
if (TWeakObjectPtr<UAccelByteWarsActivatableWidget> Widget = W_Parent->GetBalanceWidget(); Widget.IsValid())
{
Widget->DeactivateWidget();
Widget->ActivateWidget();
}
}
else
{
Tb_Error->SetText(FText::FromString(Error.ErrorRaw));
Tb_Success->SetVisibility(ESlateVisibility::Collapsed);
Tb_Error->SetVisibility(ESlateVisibility::Visible);
}
} -
Find the
NativeOnActivated()function and replace the existing implementation with the code below. This code resets the UI.void UItemPurchaseWidget_Starter::NativeOnActivated()
{
Super::NativeOnActivated();
W_Parent = GetFirstOccurenceOuter<UStoreItemDetailWidget>();
ensure(W_Parent);
StoreItemDataObject = W_Parent->StoreItemDataObject;
ensure(StoreItemDataObject);
PurchaseSubsystem = GetGameInstance()->GetSubsystem<UStoreItemPurchaseSubsystem_Starter>();
ensure(PurchaseSubsystem);
// ...
// Setup UI
SetupPurchaseButtons(StoreItemDataObject->GetPrices());
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Not_Empty);
Tb_Success->SetVisibility(ESlateVisibility::Collapsed);
Tb_Error->SetVisibility(ESlateVisibility::Collapsed);
Ss_Amount->SetSelectedIndex(0);
Ss_Amount->OnSelectionChangedDelegate.AddUObject(this, &ThisClass::UpdatePrice);
// Show amount if consumable
Ss_Amount->SetVisibility(StoreItemDataObject->GetIsConsumable() ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
// ...
// Set focus
if (W_PurchaseButtonsOuter->HasAnyChildren())
{
W_PurchaseButtonsOuter->GetChildAt(0)->SetUserFocus(GetOwningPlayer());
}
FTUESetup();
} -
Navigate to the
NativeOnDeactivated()function and replace the existing implementation with the code below. This code unbinds theOnSelectionChangedDelegateto ensure the widget is cleanly closed.void UItemPurchaseWidget_Starter::NativeOnDeactivated()
{
Super::NativeOnDeactivated();
// ...
Ss_Amount->OnSelectionChangedDelegate.RemoveAll(this);
// ...
} -
Build the project and open it with the Unreal Editor once it's done.
-
Open
Content/TutorialModules/Monetization/StoreItemPurchase/DA_StoreItemPurchase.uassetand 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/StoreItemPurchase/UI/ItemPurchaseWidget_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/UI/ItemPurchaseWidget_Starter.cpp
- AccelByteWars/Content/TutorialModules/Monetization/StoreItemPurchase/UI/W_StoreItemPurchase_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/StoreItemPurchase/DA_StoreItemPurchase.uasset