Game Center services
Use Game Center achievements, leaderboards, and saved games.
Authenticate with Game Center before using these services. Create achievement and leaderboard IDs in App Store Connect; use those exact IDs in your game.
Achievements and scores
Report achievement progress
Loading the interactive viewer.
Submit a score
Loading the interactive viewer.
Read top scores
Loading the interactive viewer.
Game module dependencies: GameCenterKit, BetideCore.
#include "Nodes/Achievements/GCIK_Achievements.h"
#include "Nodes/Leaderboards/GCIK_Leaderboards.h"
void ReportAppleAchievement(UObject *WorldContext, const FString &AchievementId, double Percent,
TFunction<void(const FBetideError &)> Completion)
{
auto *Request =
UGCIK_ReportAchievement::ReportAchievement(WorldContext, AchievementId, Percent, true);
if (!Request)
return;
Request->OnSuccessCallback = Completion;
Request->OnFailureCallback = MoveTemp(Completion);
Request->Activate();
}
void SubmitAppleScore(UObject *WorldContext, const FString &LeaderboardId, int64 Score,
TFunction<void(const FBetideError &)> Completion)
{
auto *Request = UGCIK_SubmitScore::SubmitScore(WorldContext, {LeaderboardId}, Score);
if (!Request)
return;
Request->OnSuccessCallback = Completion;
Request->OnFailureCallback = MoveTemp(Completion);
Request->Activate();
}void ReadAppleTopScores(
UObject *WorldContext, const FString &LeaderboardId,
TFunction<void(const TArray<FGCIK_LeaderboardEntry> &, int64, const FBetideError &)> Completion)
{
auto *Request = UGCIK_LoadLeaderboardEntries::LoadLeaderboardEntries(
WorldContext, LeaderboardId, EGCIK_LeaderboardPlayerScope::Global,
EGCIK_LeaderboardTimeScope::AllTime, 1, 10);
if (!Request)
return;
Request->OnSuccessCallback = Completion;
Request->OnFailureCallback = MoveTemp(Completion);
Request->Activate();
}Use weak UObject captures in completion handlers. UGCIK_LeaderboardsLibrary::ShowLeaderboardUI(LeaderboardId) opens Apple’s leaderboard UI.
Achievement progress is 0–100 percent; 100 unlocks it. Leaderboard scores use the board’s configured ordering and format. Entry ranges start at 1.
| Need | Node / C++ entry point |
|---|---|
| Player progress | Load Achievements / UGCIK_LoadAchievements::LoadAchievements |
| Titles and descriptions | Load Achievement Descriptions / UGCIK_LoadAchievementDescriptions::LoadAchievementDescriptions |
| Ranked entries | Load Leaderboard Entries / UGCIK_LoadLeaderboardEntries::LoadLeaderboardEntries |
| Entries around the player | Load Player Centered Entries / UGCIK_LoadPlayerCenteredEntries::LoadPlayerCenteredEntries |
| Previous recurring board | Load Previous Leaderboard Occurrence / UGCIK_LoadPreviousOccurrence::LoadPreviousOccurrence |
For shared gameplay calls across stores, use Crossplay achievements and leaderboards. Apple’s provider supports achievements and leaderboards; it does not provide the generic Crossplay stats service.
Saved games
Game Center saves use iCloud and stay within Apple’s ecosystem. Configure iCloud for the signed app and test with an iCloud account. They are separate from Crossplay cloud saves.
Save player data
Loading the interactive viewer.
List saved games
Loading the interactive viewer.
Load saved bytes
Loading the interactive viewer.
Resolve chosen save versions
Loading the interactive viewer.
#include "Nodes/SavedGames/GCIK_SavedGames.h"
void SaveAppleGame(UObject *WorldContext, const FString &Slot, const TArray<uint8> &Data,
const FScriptDelegate &OnSaved, const FScriptDelegate &OnFailed)
{
auto *Request = UGCIK_SaveGameData::SaveGameData(WorldContext, Slot, Data);
if (!Request)
return;
Request->OnSuccess.Add(OnSaved);
Request->OnFailure.Add(OnFailed);
Request->Activate();
}void ListAppleSaves(UObject *WorldContext, const FScriptDelegate &OnLoaded,
const FScriptDelegate &OnFailed)
{
auto *Request = UGCIK_LoadAllSavedGames::LoadAllSavedGames(WorldContext);
if (!Request)
return;
Request->OnSuccess.Add(OnLoaded);
Request->OnFailure.Add(OnFailed);
Request->Activate();
}
void LoadAppleSave(UObject *WorldContext, const FString &Slot, const FScriptDelegate &OnLoaded,
const FScriptDelegate &OnFailed)
{
auto *Request = UGCIK_LoadSavedGameData::LoadSavedGameData(WorldContext, Slot);
if (!Request)
return;
Request->OnSuccess.Add(OnLoaded);
Request->OnFailure.Add(OnFailed);
Request->Activate();
}
void ResolveAppleSaves(UObject *WorldContext, const TArray<FGCIK_SavedGame> &Conflicts,
const TArray<uint8> &MergedData, const FScriptDelegate &OnResolved,
const FScriptDelegate &OnFailed)
{
auto *Request = UGCIK_ResolveConflicts::ResolveConflicts(WorldContext, Conflicts, MergedData);
if (!Request)
return;
Request->OnSuccess.Add(OnResolved);
Request->OnFailure.Add(OnFailed);
Request->Activate();
}Bind the delegates to UFUNCTION handlers with (const FGCIK_SavedGame&, const FBetideError&). The load-data handler adds const TArray<uint8>& Data between the record and error. The list handler receives (const TArray<FGCIK_SavedGame>&, const FBetideError&); conflict resolution uses the save handler signature.
Keep a version in your save payload so later releases can read older saves. Load All Saved Games returns metadata; Load Saved Game Data reads a named save.
For conflicts, load the list first: this caches the native references required by Resolve Saved Game Conflicts. Pass the conflicting records and your chosen merged bytes. The wrapper does not merge game state for you. Its load node selects the first save with a matching name, so it cannot read each conflicting version by device or date. Only resolve with bytes your game has already chosen; do not assume this is a complete merge API. In C++, use UGCIK_ResolveConflicts::ResolveConflicts; Delete Saved Game is UGCIK_DeleteSavedGame::DeleteSavedGame.