Full guide to unlocking and querying EOS achievements with the Supabase UE5 Plugin. Covers the USupabaseEOSAchievements static API, the UAsyncEOSUnlockAchievement Blueprint latent node, and step-by-step setup for C++ and Blueprint workflows.
EOS Achievements lets you unlock achievements for the local player and query their unlock status and progress. The plugin provides two approaches:
| Approach | Class | Complexity | Best For |
|---|---|---|---|
| Async Blueprint Node | UAsyncEOSUnlockAchievement |
Low | Blueprint-driven workflows. Latent node for unlocking achievements. |
| Static API | USupabaseEOSAchievements |
Low | Both C++ and Blueprint. Stateless static methods for unlocking and querying achievements. |
All methods on USupabaseEOSAchievements are static and require a World Context object. No initialization or lifecycle management is needed -- call the functions directly.
Status: This feature is in Preview/Alpha. The API surface is stable, but async callback forwarding via
ClientDatais planned for a future update. Recommended for testing and prototyping in production projects.
--- Unlock flow: ---
+----------------+ +----------------+ +-------------------+
| UnlockAchieve- | ----> | EOS Achieve- | ----> | OnSuccess fires |
| ment() | | ment API call | | (FSimpleDelegate)|
| (static call) | | | | |
+----------------+ +----------------+ +-------------------+
|
v
+----------------+ +----------------+ +-------------------+
| Achievement | <---- | EOS validates | <---- | OnFailure fires |
| unlocked on | | and records | | (Error delegate) |
| server | | | | |
+----------------+ +----------------+ +-------------------+
--- Query flow: ---
+----------------+ +----------------+ +-------------------+
| QueryAchieve- | ----> | EOS Achieve- | ----> | OnSuccess fires |
| ments() | | ment API call | | (AchievementsQuery|
| (static call) | | | | Delegate) |
+----------------+ +----------------+ +-------------------+
|
v
+----------------+ +----------------+ +-------------------+
| TArray<FEOS- | <---- | EOS returns | <---- | OnFailure fires |
| Achievement> | | achievement | | (Error delegate) |
| | | data | | |
+----------------+ +----------------+ +-------------------+
| Step | Action | Description |
|---|---|---|
| 1 | Configure achievements in Epic Developer Portal | Define achievement IDs (e.g., first_win, speedrunner) with display names and descriptions. |
| 2 | UnlockAchievement() |
Unlock an achievement for the local player. Fires OnSuccess when accepted or OnFailure on error. |
| 3 | QueryAchievements() |
Fetch achievement data. Pass an empty array to query all, or a specific set of IDs. Returns TArray<FEOSAchievement> via OnSuccess. |
| 4 | Process results | Iterate over FEOSAchievement entries to display name, unlock status, progress, and timestamps. |
Before using EOS achievements, ensure you have the following:
| Requirement | Details |
|---|---|
| EOS SDK | The EOS SDK must be installed and configured. See the EOS Overview for setup instructions. |
| Achievement Definitions | Achievements must be configured in the Epic Developer Portal under your product's Achievements section. |
| Achievement IDs | String identifiers assigned in the portal (e.g., first_win, speedrunner). These are passed to UnlockAchievement() and QueryAchievements(). |
| Authenticated Player | The local player must be authenticated with EOS Connect before unlocking or querying achievements. |
| Supabase UE5 Plugin | v2.0.0 or later with SupabaseEOS module included. |
UnlockAchievement() and QueryAchievements()FEOSAchievement)For Blueprint workflows that need to unlock achievements, use the UAsyncEOSUnlockAchievement latent node.
Selffirst_win)FSupabaseError #include "Async/AsyncEOSUnlockAchievement.h"
// Unlock an achievement using the latent node
UAsyncEOSUnlockAchievement* UnlockTask = UAsyncEOSUnlockAchievement::UnlockAchievement(
this,
TEXT("first_win")
);
UnlockTask->OnSuccess.AddLambda([]()
{
UE_LOG(LogTemp, Log, TEXT("Achievement unlocked!"));
});
UnlockTask->OnFailure.AddLambda([](const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement unlock failed: %s"), *ErrorMessage);
});
USupabaseEOSAchievements exposes static methods for unlocking and querying achievements. No instantiation or lifecycle management is required.
Unlock an achievement for the currently authenticated local player:
#include "SupabaseEOSAchievements.h"
USupabaseEOSAchievements::UnlockAchievement(
GetWorld(), // World Context
TEXT("first_win"), // Achievement ID
FSimpleDelegate::CreateLambda([]()
{
UE_LOG(LogTemp, Log, TEXT("Achievement unlocked successfully!"));
}),
FSupabaseErrorDelegate::CreateLambda([](const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement unlock failed: %s"), *ErrorMessage);
})
);
Fetch achievement data. Pass a specific set of IDs, or an empty array to query all achievements defined for your product:
// Query specific achievements
TArray<FString> AchievementIds;
AchievementIds.Add(TEXT("first_win"));
AchievementIds.Add(TEXT("speedrunner"));
USupabaseEOSAchievements::QueryAchievements(
GetWorld(), // World Context
AchievementIds, // IDs to query
FSupabaseAchievementsQueryDelegate::CreateLambda(
[](const TArray<FEOSAchievement>& Achievements)
{
UE_LOG(LogTemp, Log, TEXT("Received %d achievements"), Achievements.Num());
for (const FEOSAchievement& Achievement : Achievements)
{
UE_LOG(LogTemp, Log, TEXT(" [%s] %s - %s"),
Achievement.bUnlocked ? TEXT("UNLOCKED") : TEXT("LOCKED"),
*Achievement.DisplayName,
*Achievement.AchievementId);
}
}),
FSupabaseErrorDelegate::CreateLambda([](const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement query failed: %s"), *ErrorMessage);
})
);
// Query ALL achievements (pass an empty array)
TArray<FString> EmptyIds;
USupabaseEOSAchievements::QueryAchievements(
GetWorld(),
EmptyIds, // Empty array = query all
FSupabaseAchievementsQueryDelegate::CreateLambda(
[](const TArray<FEOSAchievement>& Achievements)
{
UE_LOG(LogTemp, Log, TEXT("Fetched all %d achievements"), Achievements.Num());
}),
FSupabaseErrorDelegate::CreateLambda([](const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement query failed: %s"), *ErrorMessage);
})
);
Static utility class for EOS achievement operations. All methods are static and require a World Context object.
| Function | Signature | Description |
|---|---|---|
UnlockAchievement |
static void UnlockAchievement(UObject* WorldContext, const FString& AchievementId, FSimpleDelegate OnSuccess, FSupabaseErrorDelegate OnFailure) |
Unlock an achievement by ID for the local player. OnSuccess fires when the unlock is accepted. OnFailure fires with an error message on failure. |
QueryAchievements |
static void QueryAchievements(UObject* WorldContext, const TArray<FString>& AchievementIds, FSupabaseAchievementsQueryDelegate OnSuccess, FSupabaseErrorDelegate OnFailure) |
Query achievements by ID. Pass an empty AchievementIds array to query ALL achievements. Results are delivered via OnSuccess as TArray<FEOSAchievement>. |
| Parameter | Type | Description |
|---|---|---|
WorldContext |
UObject* |
Any valid world context object (e.g., this, GetWorld(), Self in Blueprint). Required for all static methods. |
AchievementId |
const FString& |
The achievement identifier as configured in the Epic Developer Portal (e.g., first_win). |
AchievementIds |
const TArray<FString>& |
Array of achievement identifiers to query. Pass an empty array to query all achievements defined for your product. |
OnSuccess |
FSimpleDelegate / FSupabaseAchievementsQueryDelegate |
Callback on success. For UnlockAchievement, this is a FSimpleDelegate (no parameters). For QueryAchievements, this receives TArray<FEOSAchievement>. |
OnFailure |
FSupabaseErrorDelegate |
Callback on failure. Receives an FString error message. |
| Delegate | Signature | Description |
|---|---|---|
FSimpleDelegate |
DECLARE_DELEGATE(FSimpleDelegate) |
Fired on successful achievement unlock. No parameters. |
FSupabaseAchievementsQueryDelegate |
DECLARE_DYNAMIC_DELEGATE_OneParam(FSupabaseAchievementsQueryDelegate, const TArray<FEOSAchievement>&, Achievements) |
Fired on successful achievement query with the returned achievements. |
FSupabaseErrorDelegate |
DECLARE_DYNAMIC_DELEGATE_OneParam(FSupabaseErrorDelegate, const FString&, ErrorMessage) |
Fired on failure with a human-readable error message. |
A struct representing a single achievement returned from a query. Received via FSupabaseAchievementsQueryDelegate.
| Property | Type | Description |
|---|---|---|
AchievementId |
FString |
The unique achievement identifier as configured in the Epic Developer Portal. |
DisplayName |
FString |
The display name of the achievement (e.g., "First Win"). |
Description |
FString |
The description of the achievement (e.g., "Win your first match"). |
bUnlocked |
bool |
Whether the achievement has been unlocked by the local player. |
UnlockedAt |
int64 |
UNIX timestamp (seconds) indicating when the achievement was unlocked. 0 if the achievement is locked. |
Progress |
float |
Achievement progress from 0.0 to 1.0. A value of 1.0 means the achievement is fully unlocked. |
Note:
UnlockedAtis a raw UNIX timestamp in seconds. To convert it to anFDateTime, useFDateTime::FromUnixTimestamp(Achievement.UnlockedAt). WhenbUnlockedisfalse,UnlockedAtwill be0andProgresswill be less than1.0.
A Blueprint-compatible latent action node for unlocking achievements asynchronously. Defined in Async/AsyncEOSUnlockAchievement.h.
| Method | Signature | Description |
|---|---|---|
UnlockAchievement |
static UAsyncEOSUnlockAchievement* UnlockAchievement(UObject* WorldContextObject, const FString& AchievementId) |
Starts an async achievement unlock. Returns the latent action object for delegate binding. |
| Delegate | Signature | Description |
|---|---|---|
OnSuccess |
DECLARE_DYNAMIC_MULTICAST_DELEGATE(OnSuccess) |
Fired when the achievement is unlocked successfully. No parameters. |
OnFailure |
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(OnFailure, const FString&, ErrorMessage) |
Fired when the unlock fails. Contains a human-readable error message. |
Supabase > EOS > Achievements
This example demonstrates unlocking an achievement when the player completes a key action:
#include "SupabaseEOSAchievements.h"
void AMyGameMode::OnPlayerFirstWin(APlayerController* WinningPlayer)
{
#if WITH_EOS_SDK && SUPABASE_EOS_INTEGRATION
const FString AchievementId = TEXT("first_win");
USupabaseEOSAchievements::UnlockAchievement(
GetWorld(),
AchievementId,
FSimpleDelegate::CreateUObject(this, &AMyGameMode::OnAchievementUnlocked),
FSupabaseErrorDelegate::CreateUObject(this, &AMyGameMode::OnAchievementUnlockFailed)
);
UE_LOG(LogTemp, Log, TEXT("Unlocking achievement '%s'..."), *AchievementId);
#else
UE_LOG(LogTemp, Warning, TEXT("EOS achievements not available in this build."));
#endif
}
void AMyGameMode::OnAchievementUnlocked()
{
UE_LOG(LogTemp, Log, TEXT("Achievement unlocked successfully!"));
// Show a notification to the player
if (UGameplayStatics::GetPlayerController(this, 0))
{
ShowAchievementPopup(TEXT("Achievement Unlocked!"));
}
}
void AMyGameMode::OnAchievementUnlockFailed(const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement unlock failed: %s"), *ErrorMessage);
// Optionally retry or log for analytics
LogAchievementError(TEXT("first_win"), ErrorMessage);
}
This example queries a specific set of achievements to display their status in a UI widget:
#include "SupabaseEOSAchievements.h"
void AMyAchievementWidget::RequestAchievementStatus()
{
#if WITH_EOS_SDK && SUPABASE_EOS_INTEGRATION
TArray<FString> AchievementIds;
AchievementIds.Add(TEXT("first_win"));
AchievementIds.Add(TEXT("speedrunner"));
AchievementIds.Add(TEXT("perfectionist"));
USupabaseEOSAchievements::QueryAchievements(
GetWorld(),
AchievementIds,
FSupabaseAchievementsQueryDelegate::CreateUObject(
this, &AMyAchievementWidget::OnAchievementsReceived),
FSupabaseErrorDelegate::CreateUObject(
this, &AMyAchievementWidget::OnAchievementsQueryFailed)
);
#else
UE_LOG(LogTemp, Warning, TEXT("EOS achievements not available."));
#endif
}
void AMyAchievementWidget::OnAchievementsReceived(const TArray<FEOSAchievement>& Achievements)
{
UE_LOG(LogTemp, Log, TEXT("Achievement query returned %d entries"), Achievements.Num());
// Clear previous data
CachedAchievements.Empty();
for (const FEOSAchievement& Achievement : Achievements)
{
if (Achievement.bUnlocked)
{
FDateTime UnlockedTime = FDateTime::FromUnixTimestamp(Achievement.UnlockedAt);
UE_LOG(LogTemp, Log, TEXT(" [UNLOCKED] %s - %s (unlocked at: %s)"),
*Achievement.AchievementId,
*Achievement.DisplayName,
*UnlockedTime.ToString());
}
else
{
UE_LOG(LogTemp, Log, TEXT(" [LOCKED] %s - %s (progress: %.0f%%)"),
*Achievement.AchievementId,
*Achievement.DisplayName,
Achievement.Progress * 100.0f);
}
CachedAchievements.Add(Achievement);
}
// Refresh the achievements display
RefreshAchievementUI();
}
void AMyAchievementWidget::OnAchievementsQueryFailed(const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Achievement query failed: %s"), *ErrorMessage);
ShowErrorToast(FString::Printf(TEXT("Failed to load achievements: %s"), *ErrorMessage));
}
This example queries every achievement defined for the product, useful for a full achievements screen:
#include "SupabaseEOSAchievements.h"
void AMyAchievementsScreen::LoadAllAchievements()
{
#if WITH_EOS_SDK && SUPABASE_EOS_INTEGRATION
// Pass an empty array to query ALL achievements
TArray<FString> AllAchievements;
USupabaseEOSAchievements::QueryAchievements(
GetWorld(),
AllAchievements,
FSupabaseAchievementsQueryDelegate::CreateUObject(
this, &AMyAchievementsScreen::OnAllAchievementsReceived),
FSupabaseErrorDelegate::CreateUObject(
this, &AMyAchievementsScreen::OnAchievementsQueryFailed)
);
#else
UE_LOG(LogTemp, Warning, TEXT("EOS achievements not available."));
#endif
}
void AMyAchievementsScreen::OnAllAchievementsReceived(const TArray<FEOSAchievement>& Achievements)
{
int32 UnlockedCount = 0;
for (const FEOSAchievement& Achievement : Achievements)
{
if (Achievement.bUnlocked)
{
UnlockedCount++;
}
UE_LOG(LogTemp, Log, TEXT(" %s | %s | unlocked=%s | progress=%.1f | desc: %s"),
*Achievement.AchievementId,
*Achievement.DisplayName,
Achievement.bUnlocked ? TEXT("yes") : TEXT("no"),
Achievement.Progress,
*Achievement.Description);
}
float CompletionPercent = Achievements.Num() > 0
? (float(UnlockedCount) / Achievements.Num()) * 100.0f
: 0.0f;
UE_LOG(LogTemp, Log, TEXT("Achievement summary: %d/%d unlocked (%.1f%%)"),
UnlockedCount, Achievements.Num(), CompletionPercent);
// Update UI
UpdateAchievementGrid(Achievements);
UpdateCompletionBar(UnlockedCount, Achievements.Num());
}
void AMyAchievementsScreen::OnAchievementsQueryFailed(const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Failed to query all achievements: %s"), *ErrorMessage);
ShowErrorBanner(ErrorMessage);
}
Follow the EOS Overview setup guide to enable EOS in your project. Ensure:
bEnableEOSIntegration = true in your Build.csSupabaseEOS module is listed in dependenciesOnlineSubsystemEOS is enabled in your .uprojectSelffirst_win)