Adding UI - Entitlements Essentials - (Unreal Engine module)
注釈:本資料はAI技術を用いて翻訳されています。
このセクションの内容
このセクションでは、プレイヤーが所有するアイテムを表示するために使用するウィジェットの準備方法を学びます。関連ファイルはリソースセクションで入手できます。
所有数ウィジェット
OwnedCountWidgetEntry_Starter: アイテムの詳細を表示するために使用されるC++クラスです。このメニューはすぐに使用できるコンポーネントとして提供されているため、変更する必要はありません。- ヘッダーファイル:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.h - CPPファイル:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/OwnedCountWidgetEntry_Starter.cpp - Blueprintウィジェット:
Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_OwnedIndicator_Starter.uasset
- ヘッダーファイル:
OwnedCountWidgetEntry_Starterは、プレイヤーが特定のアイテムを何個所有しているかを表示します。耐久アイテムの場合は、数の代わりに所有済みと表示されます。このウィジェットは、 W_StoreItemEntry ウィジェットにByte Warsのモジュラーシステムを使用してアタッチされます。つまり、 W_StoreItemEntry には常に OwnedCountWidgetEntry_Starter が含まれます(エンタイトルメントモジュールがアクティブな場合)。
以下はOwnedCountWidgetEntry_Starter Blueprintウィジェットのプレビューです:

このウィジェットで使用されるコンポーネントは、 OwnedCountWidgetEntry_Starter クラスのヘッダーファイルで定義されています。
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_OwnedCount;
このウィジェットには、Byte Wars統合用に調整された事前設定済みの変数とヘルパー関数が含まれています。
-
エンタイトルメントロジックを処理するサブシステムへの参照。
private:
UPROPERTY()
UEntitlementsEssentialsSubsystem_Starter* EntitlementsSubsystem;void UOwnedCountWidgetEntry_Starter::NativeOnActivated()
{
// ...
EntitlementsSubsystem = GetGameInstance()->GetSubsystem<UEntitlementsEssentialsSubsystem_Starter>();
ensure(EntitlementsSubsystem);
// ...
} -
親ウィジェット
W_StoreItemEntryへの参照。このウィジェットはそのコンテキスト内でのみ使用されるため、W_StoreItemEntryを親として安全に想定します。private:
UPROPERTY()
UStoreItemListEntry* W_Parent;
装備インベントリウィジェット
このウィジェットは、船のカスタマイズ、ビジュアルエフェクト、パワーアップを含む、装備可能なすべてのアイテムを表示します。このウィジェットは以下のファイルで定義されています:
-
EquipmentInventoryWidget_Starter: 所有アイテムのリストとその詳細を表示するために使用されるC++クラスです。- ヘッダーファイル:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/EquipmentInventoryWidget_Starter.h - CPPファイル:
Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/EquipmentInventoryWidget_Starter.cpp - Blueprintウィジェット:
Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_ShipCustomization_Starter.uassetContent/TutorialModules/Monetization/EntitlementEssentials/UI/W_FxCustomization_Starter.uassetContent/TutorialModules/Monetization/EntitlementEssentials/UI/W_PowerUpCustomization_Starter.uasset
- ヘッダーファイル:
このインベントリウィジェットにはいくつかのバリエーションがありますが、すべて単一の EquipmentInventoryWidget_Starter クラスによって制御されます。これらのウィジェットのバリエーションは次のとおりです:
-
船のカスタマイズウィジェット
The
W_ShipCustomization_Starterウィジェットは、装備する所有している船の形状と色のリストを表示します。このウィジェットのプレビューは次のとおりです:
-
ビジュアルエフェクトのカスタマイズ
The
W_FxCustomization_Starterウィジェットは、爆発やミサイルの軌跡など、装備する所有しているビジュアルエフェクトのリストを表示します。このウィジェットのプレビューは次のとおりです:
-
パワーアップのカスタマイズ
The
W_PowerUpCustomization_Starterウィジェットは、装備する所有しているパワーアップのリストを表示します。このウィジェットのプレビューは次のとおりです:
これらのウィジェットで使用されるコンポーネントは、 EquipmentInventoryWidget_Starter クラスのヘッダーファイルで定義されています。
-
エンタイトルメントロジックを処理するサブシステムへの参照。
protected:
// ...
UPROPERTY()
UEntitlementsEssentialsSubsystem_Starter* EntitlementsSubsystem;void UEquipmentInventoryWidget_Starter::NativeOnActivated()
{
// ...
EntitlementsSubsystem = GetGameInstance()->GetSubsystem<UEntitlementsEssentialsSubsystem_Starter>();
ensure(EntitlementsSubsystem);
// ...
} -
ウィジェットコンポーネントとヘルパー変数への参照。
protected:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Root;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UInventoryWidget* W_Inventory;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Back;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TSubclassOf<AActor> PreviewActorClass;
UPROPERTY(BlueprintReadOnly)
AActor* PreviewActor;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
FName PreviewActorTag;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
FTransform PreviewActorTransform;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
FSlateBrush PreviewActorRenderTarget;
UPROPERTY()
UEntitlementsEssentialsSubsystem_Starter* EntitlementsSubsystem;
UPROPERTY()
TArray<UStoreItemDataObject*> CachedEntitlements;
UPROPERTY()
FPlayerEquipments CachedEquipments;
メニューの準備
-
OwnedCountWidgetEntry_Starterヘッダーファイルを開き、以下の関数宣言を追加します。private:
// ...
void RetrieveEntitlementWithForceRequest(const bool bForceRequest);
void ShowOwnedCount(const FOnlineError& Error, const UStoreItemDataObject* Entitlement); -
OwnedCountWidgetEntry_StarterCPPファイルを開き、RetrieveEntitlementWithForceRequest()関数を実装します。現時点では、この関数はウィジェットが表すアイテムのみを取得します。後でエンタイトルメント取得を含むように拡張します。void UOwnedCountWidgetEntry_Starter::RetrieveEntitlementWithForceRequest(const bool bForceRequest)
{
SetVisibility(ESlateVisibility::Collapsed);
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
W_Parent = GetFirstOccurenceOuter<UStoreItemListEntry>();
if (W_Parent && LocalPlayer)
{
// Get item.
const UStoreItemDataObject* ItemData = W_Parent->GetItemData();
if (!ItemData)
{
return;
// ...
}
} -
ShowOwnedCount()関数を実装します。これは、バックエンドのレスポンスに基づいて、所有しているアイテムの数、またはアイテムが耐久性の場合は所有済みインジケーターを表示します。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);
} -
NativeOnActivated()関数を見つけて、既存の実装を以下のコードに置き換えます。これにより、ウィジェットがアクティブ化されたときにRetrieveEntitlementWithForceRequest()がトリガーされ、関数がアイテム購入ウィジェットのデリゲートにバインドされるため、購入が成功した直後にアイテム数が更新されます。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);
});
} -
NativeOnDeactivated()関数に移動し、既存の実装を以下のコードに置き換えます。これにより、ウィジェットが非アクティブ化されたときに残存する参照がないように、ウィジェットが使用するすべてのデリゲートがアンバインドされます。void UOwnedCountWidgetEntry_Starter::NativeOnDeactivated()
{
Super::NativeOnDeactivated();
UItemPurchaseWidget::OnPurchaseCompleteMulticastDelegate.RemoveAll(this);
UItemPurchaseWidget_Starter::OnPurchaseCompleteMulticastDelegate.RemoveAll(this);
} -
次に、
EquipmentInventoryWidget_Starterヘッダーファイルを開き、以下の関数宣言を追加します。protected:
void SpawnPreviewActor();protected:
// ...
void QueryEquipmentItems();protected:
// ...
void DisplayEquipmentItems(FInventoryCategory* Category);protected:
// ...
void OnEquipmentItemClicked(UObject* Item, FInventoryCategory* Category);
void OnEquipmentItemEquipped(UObject* Item, FInventoryCategory* Category);
void OnEquipmentItemUnequipped(UObject* Item, FInventoryCategory* Category);protected:
// ...
void SaveEquipments(); -
EquipmentInventoryWidget_StarterCPPファイルを開いて、上記の関数を定義します。まず、SpawnPreviewActor()関数から始めましょう。この関数は、適用された船のカスタマイズを表示するために使用されるアクターをスポーンします。void UEquipmentInventoryWidget_Starter::SpawnPreviewActor()
{
if (PreviewActorClass && !PreviewActor)
{
// Try to get existing preview actor.
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsOfClassWithTag(this, PreviewActorClass, { PreviewActorTag }, Actors);
if (!Actors.IsEmpty() && Actors[0]->IsValidLowLevel())
{
PreviewActor = Actors[0];
}
// Otherwise, try to spawn a new preview actor.
if (!PreviewActor && GetWorld())
{
PreviewActor = GetWorld()->SpawnActor<AActor>(PreviewActorClass, PreviewActorTransform);
if (PreviewActor)
{
PreviewActor->Tags.Add(PreviewActorTag);
}
}
ensureMsgf(PreviewActor, TEXT("Preview actor is not found. Failed to spawn or find existing actor."));
}
if (PreviewActor)
{
W_Inventory->SetPreview(PreviewActorRenderTarget);
}
} -
次に、
QueryEquipmentItems()関数を定義します。後で、この関数を完成させて、ウィジェットに表示するプレイヤーのアイテムエンタイトルメントをクエリします。今のところ、データが読み込まれていることを示すローディングスピナーを表示するために、以下のコードを追加します。void UEquipmentInventoryWidget_Starter::QueryEquipmentItems()
{
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!LocalPlayer)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed to save customization. Local player is invalid.");
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error);
return;
}
const int32 LocalUserNum = LocalPlayer->GetControllerId();
const FUniqueNetIdPtr UserId = LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId();
Ws_Root->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Loading);
// ...
} -
次に、
DisplayEquipmentItems()関数を定義して、プレイヤーが選択したカテゴリに基づいてインベントリアイテムを表示します。void UEquipmentInventoryWidget_Starter::DisplayEquipmentItems(FInventoryCategory* Category)
{
if (!Category)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed to display customization items. Category is invalid.");
return;
}
const FString CategoryId = Category->CategoryId.ToString();
const EItemSkuPlatform PlatformSku = EItemSkuPlatform::AccelByte;
TArray<UStoreItemDataObject*> FilteredEntitlements = CachedEntitlements;
UObject* EquippedItemData = nullptr;
const FString EquippedItemId = [&]() -> FString
{
switch (Category->ItemType)
{
case EItemType::Skin:
return CachedEquipments.SkinId;
case EItemType::Color:
return CachedEquipments.ColorId;
case EItemType::ExplosionFx:
return CachedEquipments.ExplosionFxId;
case EItemType::MissileTrailFx:
return CachedEquipments.MissileTrailFxId;
case EItemType::PowerUp:
return CachedEquipments.PowerUpId;
default:
return FString();
}
}();
FString EquippedItemSku = TEXT("");
if (const UInGameItemDataAsset* EquippedItemDataAsset = UInGameItemUtility::GetItemDataAsset(EquippedItemId))
{
EquippedItemSku = EquippedItemDataAsset->SkuMap.Contains(PlatformSku) ? EquippedItemDataAsset->SkuMap[PlatformSku] : TEXT("");
}
// If there is a default item, add it as the first item in the list.
if (const UInGameItemDataAsset* DefaultItemDataAsset = Category->DefaultItemDataAsset)
{
if (UStoreItemDataObject* DefaultItemData = NewObject<UStoreItemDataObject>())
{
DefaultItemData->Setup(
DefaultItemDataAsset->DisplayName,
FText::FromName(Category->CategoryId),
UEnum::GetValueAsString(Category->ItemType),
DefaultItemDataAsset->SkuMap[PlatformSku],
TEXT(""),
DefaultItemDataAsset->Icon->GetPathName(),
DefaultItemDataAsset->SkuMap,
{}, 1, false);
EquippedItemData = DefaultItemData;
FilteredEntitlements.Insert(DefaultItemData, 0);
}
}
// Filter entitlements based on category.
FilteredEntitlements.RemoveAll(
[CategoryId, EquippedItemSku, PlatformSku, &EquippedItemData](UStoreItemDataObject* Item)
{
if (!Item)
{
return false;
}
const bool bIsValid = Item->GetCategory().ToString().Contains(CategoryId);
if (bIsValid)
{
if (Item->GetSkuMap().Contains(PlatformSku) && Item->GetSkuMap()[PlatformSku].Equals(EquippedItemSku))
{
EquippedItemData = Item;
}
Item->SetIsShowItemCount(Item->GetIsConsumable());
}
return !bIsValid;
});
// Initialize preview actor based on equipped items.
if (APlayerShipBase* ShipPreview = Cast<APlayerShipBase>(PreviewActor))
{
if (const UInGameItemDataAsset* SkinDataAsset = UInGameItemUtility::GetItemDataAsset(CachedEquipments.SkinId))
{
ShipPreview->SetAlphaTexture(SkinDataAsset->Icon);
}
if (const UInGameItemDataAsset* ColorDataAsset = UInGameItemUtility::GetItemDataAsset(CachedEquipments.ColorId))
{
ShipPreview->SetColorTexture(ColorDataAsset->Icon);
}
}
// Set initial equipped item and update the list items.
if (EquippedItemData)
{
Category->EquippedItem = EquippedItemData;
}
W_Inventory->SetListItems(FilteredEntitlements);
} -
インベントリアイテムがクリック、装備、または装備解除されたときに呼び出されるこれらの関数を定義します。アイテムがクリックされると、ウィジェットはアイテムの情報とそのプレビューを表示します。アイテムが装備されると、ウィジェットは装備されたアイテムを一時的に保存します。アイテムの装備が解除されると、アイテムは一時データから削除されます。この一時データは、ユーザーがメニューを閉じると、装備されたアイテムとして確定されます。
void UEquipmentInventoryWidget_Starter::OnEquipmentItemClicked(UObject* Item, FInventoryCategory* Category)
{
const UStoreItemDataObject* ItemData = Cast<UStoreItemDataObject>(Item);
if (!Category || !ItemData)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed handle item clicked event. Item is invalid.");
return;
}
const EItemSkuPlatform PlatformSku = EItemSkuPlatform::AccelByte;
const UInGameItemDataAsset* ItemDataAsset =
UInGameItemUtility::GetItemDataAssetBySku(PlatformSku, ItemData->GetSkuMap()[PlatformSku]);
if (!ItemDataAsset)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed handle item clicked event. Item data asset is invalid.");
return;
}
// Display preview based on item type.
W_Inventory->SetPreviewDetails(ItemDataAsset->DisplayName, ItemDataAsset->Description);
switch (Category->ItemType)
{
case EItemType::Skin:
if (APlayerShipBase* ShipPreview = Cast<APlayerShipBase>(PreviewActor))
{
ShipPreview->SetAlphaTexture(ItemDataAsset->Icon);
}
break;
case EItemType::Color:
if (APlayerShipBase* ShipPreview = Cast<APlayerShipBase>(PreviewActor))
{
ShipPreview->SetColorTexture(ItemDataAsset->Icon);
}
break;
default:
if (ItemDataAsset->PreviewVideo)
{
W_Inventory->SetPreview(ItemDataAsset->PreviewVideo);
}
break;
}
}void UEquipmentInventoryWidget_Starter::OnEquipmentItemEquipped(UObject* Item, FInventoryCategory* Category)
{
const EItemSkuPlatform PlatformSku = EItemSkuPlatform::AccelByte;
const UStoreItemDataObject* ItemData = Cast<UStoreItemDataObject>(Item);
if (!Category || !ItemData || !ItemData->GetSkuMap().Contains(PlatformSku))
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed handle item unequipped event. Item is invalid.");
return;
}
const UInGameItemDataAsset* ItemDataAsset =
UInGameItemUtility::GetItemDataAssetBySku(PlatformSku, ItemData->GetSkuMap()[PlatformSku]);
if (!ItemDataAsset)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed handle item unequipped event. Item data asset is invalid.");
return;
}
// Update cached equipments based on item type.
const FString& ItemId = ItemDataAsset->Id;
switch (Category->ItemType)
{
case EItemType::Skin:
CachedEquipments.SkinId = ItemId;
break;
case EItemType::Color:
CachedEquipments.ColorId = ItemId;
break;
case EItemType::ExplosionFx:
CachedEquipments.ExplosionFxId = ItemId;
break;
case EItemType::MissileTrailFx:
CachedEquipments.MissileTrailFxId = ItemId;
break;
case EItemType::PowerUp:
CachedEquipments.PowerUpId = ItemId;
break;
}
}void UEquipmentInventoryWidget_Starter::OnEquipmentItemUnequipped(UObject* Item, FInventoryCategory* Category)
{
if (!Category)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed handle item equipped event. Item is invalid.");
return;
}
// Unequip cached equipments based on item type.
switch (Category->ItemType)
{
case EItemType::Skin:
CachedEquipments.SkinId = TEXT("");
break;
case EItemType::Color:
CachedEquipments.ColorId = TEXT("");
break;
case EItemType::ExplosionFx:
CachedEquipments.ExplosionFxId = TEXT("");
break;
case EItemType::MissileTrailFx:
CachedEquipments.MissileTrailFxId = TEXT("");
break;
case EItemType::PowerUp:
CachedEquipments.PowerUpId = TEXT("");
break;
}
} -
SaveEquipments()関数を定義します。後で、この関数を完成させて、プレイヤーの装備をクラウドストレージに保存します。今のところ、データが保存されていることを示すローディングスピナーを表示するために、以下のコードを追加します。void UEquipmentInventoryWidget_Starter::SaveEquipments()
{
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!LocalPlayer)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed to save customization. Local player is invalid.");
return;
}
const int32 LocalUserNum = LocalPlayer->GetControllerId();
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
if (!GameInstance)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed to save customization. Game instance is invalid.");
return;
}
UPromptSubsystem* PromptSubsystem = GameInstance->GetSubsystem<UPromptSubsystem>();
if (!PromptSubsystem)
{
UE_LOG_ENTITLEMENTS_ESSENTIALS(Warning, "Failed to save customization. Prompt subsystem is invalid.");
return;
}
PromptSubsystem->ShowLoading(TEXT_SAVING_EQUIPMENTS);
// ...
} -
次に、以下のコードを
NativeOnActivated()関数に追加して、上記の関数をそれぞれのウィジェットコンポーネントにバインドします。void UEquipmentInventoryWidget_Starter::NativeOnActivated()
{
// ...
W_Inventory->OnCategorySwitched.AddUObject(this, &ThisClass::DisplayEquipmentItems);
W_Inventory->OnItemClicked.AddUObject(this, &ThisClass::OnEquipmentItemClicked);
W_Inventory->OnItemEquipClicked.AddUObject(this, &ThisClass::OnEquipmentItemEquipped);
W_Inventory->OnItemUnequipClicked.AddUObject(this, &ThisClass::OnEquipmentItemUnequipped);
Btn_Back->OnClicked().AddUObject(this, &ThisClass::SaveEquipments);
SpawnPreviewActor();
QueryEquipmentItems();
} -
プロジェクトをビルドし、Unreal Engine Editorで開きます。Editorで、
/Content/TutorialModules/Monetization/EntitlementEssentials/に移動します。DA_EntitlementsEssentialsという名前のデータアセットがあります。それを開いて、Is Starter Mode Activeを有効にします。次に、データアセットを保存します。これによりウィジェットがアクティブ化され、ゲームをプレイするときにそれらをナビゲートできるようになります。 -
Editorでゲームをプレイし、ログインして、メインメニューからStore > Inventoryに移動し、インベントリカスタマイズタイプの1つを選択します。メニューが現在ローディング状態にあることがわかります。後で、この動作を変更して、実際にエンタイトルメントアイテムリストを読み込むようにします。
リソース
- このチュートリアルセクションで使用されるファイルは、Byte Wars GitHubリポジトリで入手できます。
- 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/EquipmentInventoryWidget_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Monetization/EntitlementsEssentials/UI/EquipmentInventoryWidget_Starter.cpp
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_ShipCustomization_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_FxCustomization_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/UI/W_PowerUpCustomization_Starter.uasset
- AccelByteWars/Content/TutorialModules/Monetization/EntitlementEssentials/DA_EntitlementsEssentials.uasset