メインコンテンツまでスキップ

Put it all together - Challenge - (Unreal Engine module)

Last updated on June 24, 2025

Connect the UI to display challenges

  1. Open the CPP file of the ChallengeWidget_Starter class and replace the GetChallengeGoalList() 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);
    }));
    }));
    }
  2. Next, open the CPP file of the ChallengeWidgetEntry_Starter class and replace the code in the pre-defined NativeOnListItemObjectSet() 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();
    }
  3. 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