Put it all together - Challenge - (Unreal Engine module)
Connect the UI to display challenges
Open the CPP file of the
ChallengeWidget_Starter
class and replace theGetChallengeGoalList()
function with the code below. This implementation uses the subsystem you created earlier to retrieve the challenge by period, and then uses its challenge code to get and display the list of goals.void UChallengeWidget_Starter::GetChallengeGoalList()
{
if (!GetOwningPlayer())
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. Invalid Player Controller."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!LocalPlayer)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. Invalid Local Player."));
return;
}
const FUniqueNetIdPtr UserId = LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId();
if (!UserId)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. Invalid User ID."));
return;
}
Ws_Challenge->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Loading);
Lv_Challenge->ClearListItems();
// Get challenge by period.
ChallengeEssentialsSubsystem->GetChallengeByPeriod(
Period,
FOnGetChallengeCodeComplete::CreateWeakLambda(this, [this, UserId](bool bWasSuccessful, const FString& ErrorMessage, const FAccelByteModelsChallenge& Challenge)
{
if (!bWasSuccessful)
{
Ws_Challenge->ErrorMessage = FText::FromString(ErrorMessage);
Ws_Challenge->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error);
return;
}
// Get and display challenge goal list.
ChallengeEssentialsSubsystem->GetChallengeGoalList(
UserId,
Challenge,
FOnGetChallengeGoalsComplete::CreateWeakLambda(this, [this]
(bool bWasSuccessful, const FString& ErrorMessage, const TArray<UChallengeGoalData*>& Goals)
{
if (!bWasSuccessful)
{
Ws_Challenge->ErrorMessage = FText::FromString(ErrorMessage);
Ws_Challenge->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error);
return;
}
if (Goals.IsEmpty())
{
Ws_Challenge->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Empty);
return;
}
Lv_Challenge->SetListItems(Goals);
Ws_Challenge->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Not_Empty);
}));
}));
}Next, open the CPP file of the
ChallengeWidgetEntry_Starter
class and replace the code in the pre-definedNativeOnListItemObjectSet()
function with the following. This code sets up the entry widget by displaying the challenge goal information.void UChallengeWidgetEntry_Starter::NativeOnListItemObjectSet(UObject* ListItemObject)
{
Super::NativeOnListItemObjectSet(ListItemObject);
GoalData = Cast<UChallengeGoalData>(ListItemObject);
if (!GoalData)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to set challenge widget entry. Invalid data."));
return;
}
const FAccelByteModelsChallengeGoal Goal = GoalData->Goal;
const FAccelByteModelsChallengeGoalProgress Progress = GoalData->Progress;
const bool bIsCompleted = Progress.Status == EAccelByteModelsChallengeGoalProgressStatus::COMPLETED;
const bool bIsRewardsClaimed = Progress.ToClaimRewards.IsEmpty();
// Display goal basic information.
Tb_Goal->SetText(FText::FromString(Goal.Name));
Tb_RemainingTime->SetText(FText::FromString(GoalData->GetEndTimeDuration()));
Cb_ChallengeStatus->SetCheckedState(bIsCompleted ? ECheckBoxState::Checked : ECheckBoxState::Unchecked);
// Display rewards
Deb_Reward->Reset(true);
for (const FChallengeGoalRewardData& Reward : GoalData->Rewards)
{
if (UChallengeGoalRewardWidgetEntry* Entry = Deb_Reward->CreateEntry<UChallengeGoalRewardWidgetEntry>())
{
Entry->Setup(Reward);
}
}
// Display claim reward button.
Btn_Claim->SetIsEnabled(!bIsRewardsClaimed);
Btn_Claim->SetButtonText(bIsRewardsClaimed ? CLAIMED_CHALLENGE_REWARD_LABEL : CLAIMABLE_CHALLENGE_REWARD_LABEL);
Ws_Progress->SetActiveWidget(bIsCompleted ? Cast<UWidget>(Btn_Claim) : Cast<UWidget>(Hb_Progress));
/* Select the progress with the highest progress value, as Byte Wars displays only one.
* If there is no player progress, set the default goal progress value from the requirement group.
* Else, use the actual player progress value. */
int32 CurrentProgress = 0, TargetProgress = 0;
if (Progress.Status == EAccelByteModelsChallengeGoalProgressStatus::NOT_STARTED)
{
TArray<FAccelByteModelsChallengeGoalRequirementPredicate> Predicates{};
Algo::ForEach(Progress.Goal.RequirementGroups, [&Predicates](const FAccelByteModelsChallengeGoalRequirement& Group){ Predicates.Append(Group.Predicates); });
const FAccelByteModelsChallengeGoalRequirementPredicate* Requirement =
Algo::MaxElementBy(Predicates, &FAccelByteModelsChallengeGoalRequirementPredicate::TargetValue);
TargetProgress = Requirement ? Requirement->TargetValue : 0;
}
else
{
const FAccelByteModelsChallengeGoalProgressRequirement* Requirement =
Algo::MaxElementBy(Progress.RequirementProgressions, &FAccelByteModelsChallengeGoalProgressRequirement::CurrentValue);
CurrentProgress = Requirement ? Requirement->CurrentValue : 0;
TargetProgress = Requirement ? Requirement->TargetValue : 0;
}
Tb_Progress->SetText(FText::FromString(FString::Printf(TEXT("%d/%d"), CurrentProgress, TargetProgress)));
OnListItemObjectSet.Broadcast();
}In the same file, replace the code in the pre-defined
OnClaimButtonClicked()
function with the following. This code uses the subsystem you created earlier to claim the challenge goal rewards.void UChallengeWidgetEntry_Starter::OnClaimButtonClicked()
{
if (!GoalData || !ChallengeEssentialsSubsystem)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to claim challenge reward. Invalid data and interface."));
return;
}
// Collect claimable reward IDs.
TArray<FString> ClaimableRewardIds{};
Algo::Transform(GoalData->Progress.ToClaimRewards, ClaimableRewardIds, [](const FAccelByteModelsChallengeClaimableUserReward Reward) { return Reward.Id; });
// Claim rewards.
Btn_Claim->SetIsEnabled(false);
ChallengeEssentialsSubsystem->ClaimChallengeGoalRewards(
ClaimableRewardIds,
FOnClaimChallengeGoalRewardsComplete::CreateWeakLambda(this, [this](bool bWasSuccessful, const FString& ErrorMessage)
{
Btn_Claim->SetIsEnabled(!bWasSuccessful);
Btn_Claim->SetButtonText(bWasSuccessful ? CLAIMED_CHALLENGE_REWARD_LABEL : CLAIMABLE_CHALLENGE_REWARD_LABEL);
// If failed, display a push notification to show the error message.
if (!bWasSuccessful)
{
GetPromptSubystem()->ShowMessagePopUp(ERROR_PROMPT_TEXT, FText::FromString(ErrorMessage));
}
// If success, clear the cached claimable rewards.
else
{
GoalData->Progress.ToClaimRewards.Empty();
}
}));
}
Resources
The files used in this tutorial section are available in the Unreal Byte Wars GitHub repository.