Rajen Kishna, Technical Account Manager, Epic Games
January 25, 2022
Now that we’ve covered Stats and Leaderboards in the previous two blog posts, let’s take a look at adding achievements to your game using the Achievements Interface. Achievements can be configured to unlock automatically based on stat progress or manually using the API. In this article, we’ll go over:
Before we dive into the implementation of Epic Online Services achievements, I want to take a moment to explain the relationship and differences between Epic Online Services (EOS) achievements and Epic achievements (in Early Access at the time of writing), which were announced in October of 2021.
Epic Online Services (EOS) achievements
Epic Online Services (EOS) achievements are—like many Epic Online Services features—platform-agnostic achievements that you can implement in your game.
You can use EOS achievements to track progress and unlock achievements using your own in-game UI.
While EOS achievements will be rendered by the Overlay for convenience, they are not necessarily linked to an Epic Games Store game, and thus will not be shown in the Epic Game Store player profile or game pages.
EOS achievements have no XP attached to them. Due to their platform-agnostic nature, you are free to implement any scoring mechanism for your game.
Epic achievements
Epic achievements are a configuration-only (no code required) feature to extend EOS achievements for the Epic ecosystem, including the Epic Games Store.
Any unlocked (or previously unlocked) EOS achievements will result in the corresponding Epic achievements also being unlocked for that user.
Epic achievements add an XP value to each achievement. XP values can range from 5 XP to 200 XP. The game (as of this posting) must have a total of 1,000 XP across all achievements.
Epic achievements are classified into three tiers, based on the XP value assigned: Bronze = 5-45 XP, Silver = 50-95 XP, and Gold = 100-200 XP
Note that an additional Platinum achievement (250 XP) is automatically awarded by completing all other achievements, for a total of 1,250 XP that can be earned for each game
Epic achievements progress will automatically be displayed in the player’s Epic Games Store library, player profile, and on the Epic Games Store game page (on the web and in the Epic Games Launcher).
Games that have configured Epic achievements will have their Epic achievements displayed in the Overlay (including XP), instead of the EOS achievements that were previously displayed.
Lastly, as teased in the announcement blog, Epic achievements will be expanded to include other social features, like avatars, in the future.
Because Epic achievements are currently in Early Access and games must be set up to publish in the Epic Games Store, we’ll only focus on Epic Online Services achievements in this blog post.
Navigate to your product > Product Settings in the left menu and click on the Clients tab in the product settings screen.
Click on the three dots next to the client policy you’re using and click on Update policy
Scroll down to Features and click on the toggle button next to Achievements.
Tick the boxes next to the following actions:
findAchievementDefinitions: used to query achievement definitions
findAchievementsForLocalUser: used to query player achievement progress
unlockAchievementForLocalUser: used to unlock achievements for the logged in user
Note that this should be used with caution in production environments, as it could be reverse engineered and achievements could be unlocked by malicious users to unlock achievements without them being earned. A more robust approach is to unlock achievements via stats or unlock achievements via your own back-end server.
Click Save & Exit to confirm.
Achievements Client Policy allowed features and actions
Achievements are created in the Developer Portal and can be set up to unlock based on a stat or manually. Additionally, achievements can be set up as visible or hidden, depending on whether or not you want players to know what the achievement is before unlocking. Let’s set up a few achievements in the portal.
Navigate to your product > Game Services > Achievements in the left menu.
Here we can see the achievements for each deployment in our product (which will likely just be “Release in Live Sandbox” for now).
Note that there’s also a “Bulk Import / Export” button on the top right, which will let you streamline the back-up and creation process of achievements. We won’t go through this process in this post, however.
Click on “Create New” on the top right to add a new achievement.
In the flyout, you’ll see step one of two, which lets you define a stat to automatically unlock the achievement with. Select the SumStat stat in the drop-down. We also need to provide a threshold value in the box to the right of the stat. We’ll enter 50 here.
We can define up to three stats here to base our achievement unlocks on, but for now we’ll stick with just this one. Click on “Next” to move to step two of two.
If you followed the Storing and retrieving player-specific data blog post, you should have four stats defined already. If not, feel free to go back to that blog post and set up a stat first.
Here we define the bulk of our achievement settings. Enter “50_TOTAL_CLICKS” as the Achievement ID.
Keep or select the locale you’ll be testing in.
We’ll keep this achievement as “Shown” under the “Visibility” setting.
Upload the unlocked and locked icons for your achievement. This can be any 1024x1024 px or smaller image (JPG, PNG, BMP, or non-animated GIF) of 1.02 MB or less.
Note that the filename of the icon has to be unique across all your achievements, or a new upload with the same filename will overwrite the old file.
Enter the locked and unlocked name and description:
Locked Display Name: Click, click, click…
Locked Description: How many clicks will it take?
Unlocked Display Name: Click champion
Unlocked Description: Clicked 50 times total.
Note that you can also enter an option Flavor Text, which is an arbitrary localized string that you may use in your game. We’ll skip this for now.
Now we can click “Create” to finalize creating this achievement. After the flyout closes, you should see the achievement appear in the list.
“Click champion” achievement created
Let’s also create a second, hidden achievement that we can manually unlock:
Click on “Create New” to start adding a new achievement.
Click “Next” in the flyout without specifying a stat in step one of two.
Specify the details for this achievement:
Achievement ID: TOP_SECRET
Visibility: Hidden
Unlocked Display Name: You found it
Unlocked Description: Unlocked the hidden achievement.
Note that hidden visibility achievements do not have a locked display name or description.
Now let’s start implementing the code to query our achievement definitions:
Create a new User Control in the Views folder called AchievementView:
We’ll have two separate ListViews in our UI for Achievements, one to display all the definitions, and one to display the player progress. For now, we’ll start with the ListView for all definitions, which will only occupy part of the UI.
Open AchievementsView.xaml.cs to attach the ViewModel and hold the placeholder selection changed event handler:
Add an AchievementsViewModel.cs class to the ViewModels folder:
Add a reference to AchievementsViewModel in ViewModelLocator.cs:
Add an AchievementsService.cs class to the Services folder to hold the definition query logic:
Note that we’re using the V2 APIs in the SDK, which replace the deprecated older APIs (e.g. CopyAchievementDefinitionByIndex).
Add a AchievementsQueryDefinitionsCommand.cs class to the Commands folder:
Open AchievementsViewModel.cs to declare and instantiate the command:
Add the following line to the RaiseConnectCanExecuteChanged() method in ViewModelLocator.cs to ensure we can only query for Achievement definitions after successfully logging in through the Connect Interface:
Lastly, add the AchievementsView to our TabControl in MainWindow.xaml:
Now when we run the app and authenticate through Auth and Connect, we can navigate to the Achievements tab and query the achievement definitions using the Query definitions button.
Now that we know how to retrieve all achievement definitions, let’s take a look at retrieving the player’s progress towards them:
Open AchievementsView.xaml and add our second ListView and StackPanel right below the previous </ListView> tag:
We’re using a bit of a brute force method to display the stat that’s attached to the achievement (if any). We’re displaying the first stat in the array (even if there aren’t any). In a production environment you’ll have to validate the array of PlayerStatInfo returned in the StatInfo property before using it.
Open AchievementsViewModel.cs and declare the following collection below the SelectedAchievement to hold player achievement progress:
Open AchievementsService.cs and add the following method to hold our player achievement progress query logic:
Add an AchievementsQueryPlayerAchievementsCommand.cs class to the Commands folder:
Open AchievementsViewModel.cs to declare and instantiate the command:
Open ViewModelLocator.cs and add the following line to the RaiseConnectCanExecuteChanged() method to ensure we can only query for player achievement progress after successfully logging in through the Connect Interface:
Now we can launch our app again and use the Query progress button to retrieve the player’s achievement progress.
Player’s achievement progress queried
There are a few things to note here:
The DisplayName property returned from the QueryPlayerAchievements call is null, similar to the Description, IconURL, and FlavorText properties (which we’re not displaying in the UI). These properties are retrieved and stored into the SDK cache by the QueryDefinitions call, so we need to click the Query definitions button first to populate these properties in the cache.
The StatInfo property will be null unless the player has stat values ingested. In the screenshot above, I have the logged in player’s stats reset so there’s no value displayed.
Player progress reported as a double value between 0 and 1, representing a value between 0 and 100%. As the logged in player in the screenshot does not have the stat ingested, progress is 0.
After we navigate to the Stats tab in our sample app and ingest a number of clicks, we can return to the Achievements tab and Query definitions and Query progress sequentially to display the player’s progress:
We’ve set up one of our achievements to unlock manually and the other automatically using a stat. Let’s take a look at the code to manually unlock achievements and also how to get notified when an achievement is automatically unlocked.
Note that, as mentioned earlier, unlocking achievements manually in the game’s code may expose an attack vector for malicious users to unlock achievements that they haven’t earned.
Open AchievementService.cs and add the following method to hold our manual achievement unlock logic:
The process is fairly straightforward here: we call Achievements.UnlockAchievements, passing in an array of AchievementIDs to unlock the achievement(s).
Add an AchievementsUnlockAchievementCommand.cs class to the Commands folder:
Open AchievementsViewModel.cs to declare and instantiate the command:
Lastly, open AchievementsView.xaml.cs and add the following line to the AchievementsListView_SelectionChanged method, to make sure we can only click the Unlock button once an achievement is selected:
When you run the app and authenticate, navigate to the Achievements tab and click Query definitions. Then click on the TOP_SECRET achievement and click the Unlock button. The UI won’t change, but you should see an “UnlockAchievements Success” message in the Debug Output in Visual Studio. Note that this code applies to visible and hidden achievements.
Now we can click Query progress and our achievement will show up in the list. Hidden achievements are not returned by the QueryPlayerAchievements call until unlocked, as seen previously.
Hidden achievement manually unlocked
Finally, let’s subscribe to achievement unlock notifications, so we can run our own code when an achievement unlocks via stat progression:
Open AchievementsService.cs and add the following methods to subscribe to and unsubscribe from achievement notifications:
We’re calling Achievements.AddNotifyAchievementsUnlockedV2 to set up notifications, passing in a callback method that will get called when an achievement is unlocked by ingesting stats associated with the achievement. Note that this method will also be called when you manually unlock achievements.
AchievementsUnlockedCallback will just write a line to the Debug Output in this case, but this is where you’d handle any UI to inform the player that they’ve unlocked an achievement.
AddNotifyAchievementsUnlockedV2 will return a notificationId, which you can use to stop being notified about achievement unlocks. We’re not implementing that in this sample, but that’s what the RemoveNotification method does.
To keep things simple, add the following call to the QueryDefinitions method in AchievementsService.cs just above ViewModelLocator.Main.StatusBarText = string.Empty to start getting notified after calling QueryDefinitions:
Run the app again and authenticate, then navigate to the Achievements tab and click the Query definitions button. To check progress toward our achievement of 50 clicks total, click the Query progress button. If it’s not unlocked yet, note the progress and navigate to the Stats tab to ingest more clicks until it unlocks.
You should see a message pop up in the Debug Output in Visual Studio indicating the achievement was unlocked. If you already had the achievement unlocked, follow the steps in the next section to reset the achievement and also reset the player’s stats if necessary.
If at any point in time you need to manually adjust a player’s achievements, either unlocking or resetting/locking them, you can do this in the Developer Portal:
Navigate to your product > Game Services > Achievements in the left menu.
Click on the “Player Lookup” button on the top right.
In the flyout, enter the player’s Product User ID (PUID) or account-specific ID (e.g. Epic account ID) and click on “Search”.
A list of the player’s achievements will appear with their current progress and unlock status. In this list, you can click the ellipsis button to either unlock or reset this achievement.
Note that you can also use the “Reset All” or “Unlock All” buttons to quickly toggle all achievements, but be cautious using this in a production environment. This is most useful during development. Another good practice is to create a separate deployment for testing achievements during development, as all player data is scoped to a specific deployment.
Unlock or reset achievements in the Developer Portal
As with the other services we’ve looked at, Achievements has some usage limitations in place to ensure reliability and availability for all users. At the time of writing, these are the limitations (be sure to check the documentation for the most up-to-date information):
1000 total achievements maximum per deployment
Three stats maximum an achievement can automatically unlock from
Achievements also must adhere to the following requirements:
22 localized text variants maximum per achievement
1.02 MB maximum file size for achievement icons
1024 x 1024 pixels maximum resolution for achievement icons
PNG, JPG, BMP, or non-animated GIF format only for achievement icons
Lastly, there are per-user or per-deployment rate limits for API calls for client or server calls respectively, which you can find in the documentation.
Epic believes in an open, integrated games community.
By offering our online services to everyone for free, we aim to empower more developers to serve their own player communities.