Steamworks-Dokumentation
Schritt-für-Schritt: Errungenschaften

Einführung

Im Folgenden finden Sie eine kurze Schritt-für-Schritt-Anleitung, wie Sie einfache Steam-Errungenschaften in weniger als 10 Minuten und mit weniger als 10 Zeilen Code in Ihre Anwendung integrieren können. Im Lieferumfang des Steamworks SDKs ist eine große Beispielanwendung namens Spacewar enthalten, welche die gesamte Bandbreite der Steam-Funktionen demonstriert und Ihre erste Anlaufstelle sein sollte, wenn Sie Funktionen in Aktion erleben möchten. Dieses Tutorial fasst die Informationen aus Spacewar und der API für Statistiken und Errungenschaften auf die wesentlichen für Steam benötigten Informationen zusammen, damit alles so einfach wie möglich bleibt. Beachten Sie, dass es zwischen Statistiken und Errungenschaften erhebliche Überschneidungen gibt. Wenn Sie beides implementieren, können Sie daher zahlreiche Aufrufe konsolidieren.

Schritt 1 – Definieren der Errungenschaften für Ihr Spiel

Errungenschaften sind anwendungsspezifisch und werden auf der Seite zur Errungenschaftenkonfiguration im Backend der Steamworks-Anwendungsverwaltung eingerichtet. Im Folgenden finden Sie die Liste der Errungenschaften aus der Steamworks-Beispielanwendung Spacewar:
spacewar_achievements.png

Schritt 2 – Kurzbeschreibung der Arbeit mit Errungenschaften

Der folgende Code ist spielübergreifend und kann entsprechend Ihrer Anforderungen in Ihr Spiel eingefügt werden. Die Klasse ist in dieser Form voll funktionsfähig, kann aber bei Bedarf problemlos erweitert werden. Der gesamte Code stammt direkt aus den Spacewar-Beispieldateien StatsAndAchievements.cpp/h.

Header-Datei

Zunächst definieren wir eine Struktur, in welche die vom Server empfangenen Errungenschaftsdaten aufgenommen werden, und stellen ein Makro zum Erstellen von Objekten dieses Typs bereit. Diese Daten entsprechen exakt denen auf der Seite Errungenschaftenkonfiguration.
#define _ACH_ID( id, name ) { id, #id, name, "", 0, 0 } struct Achievement_t { int m_eAchievementID; const char *m_pchAchievementID; char m_rgchName[128]; char m_rgchDescription[256]; bool m_bAchieved; int m_iIconImage; };

Als Nächstes definieren wir eine Helper-Klasse, die alle Aufrufe der Steam-API zusammenfasst und alle Steam-Rückruffunktionen erstellt.
class CSteamAchievements { private: int64 m_iAppID; // Unsere App-ID Achievement_t *m_pAchievements; // Errungenschaftsdaten int m_iNumAchievements; // Anzahl der Errungenschaften bool m_bInitialized; // Haben wir bereits die Statistiken abgerufen und den zugehörigen Rückruf erhalten? public: CSteamAchievements(Achievement_t *Achievements, int NumAchievements); ~CSteamAchievements(); bool RequestStats(); bool SetAchievement(const char* ID); STEAM_CALLBACK( CSteamAchievements, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived ); STEAM_CALLBACK( CSteamAchievements, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored ); STEAM_CALLBACK( CSteamAchievements, OnAchievementStored, UserAchievementStored_t, m_CallbackAchievementStored ); };

Codedatei

Konstruktor

Parameter – Der Konstruktor umfasst einen Zeiger auf ein Array mit Errungenschaften sowie die Länge des Arrays. Das Format dieses Arrays wird im Hauptcode des Spiels weiter unten behandelt.
Rückgabewert – Keiner
Funktionsweise – Der Konstruktor initialisiert eine Reihe von Instanzvariablen und ruft die App-ID ab, mit der wir gerade spielen. Außerdem werden die Rückrufmethoden eingebunden, mit denen asynchrone Aufrufe an Steam verarbeitet werden. Schließlich erfolgt der Erstaufruf von RequestStats(), um Statistiken und Errungenschaften des aktuellen Nutzers abzurufen.
CSteamAchievements::CSteamAchievements(Achievement_t *Achievements, int NumAchievements): m_iAppID( 0 ), m_bInitialized( false ), m_CallbackUserStatsReceived( this, &CSteamAchievements::OnUserStatsReceived ), m_CallbackUserStatsStored( this, &CSteamAchievements::OnUserStatsStored ), m_CallbackAchievementStored( this, &CSteamAchievements::OnAchievementStored ) { m_iAppID = SteamUtils()->GetAppID(); m_pAchievements = Achievements; m_iNumAchievements = NumAchievements; RequestStats(); }

RequestStats()

Parameter – Keine
Rückgabewert Boolescher Wert, der angibt, ob der Aufruf erfolgreich war oder nicht. Wenn der Aufruf fehlgeschlagen ist, wurde Steam höchstwahrscheinlich nicht initialisiert. Stellen Sie sicher, dass ein Steam-Client ausgeführt wird, wenn Sie diesen Aufruf ausführen, und dass zuvor SteamAPI_Init aufgerufen wurde.
Funktionsweise Funktionsweise – Diese Methode umschließt im Prinzip einen Aufruf von ISteamUserStats::RequestCurrentStats, bei dem es sich um einen asynchronen Aufruf an Steam handelt, durch den die Statistiken und Errungenschaften des aktuellen Nutzers angefordert werden. Dieser Aufruf muss ausgeführt werden, bevor Sie Statistiken und Errungenschaften festlegen bzw. freischalten können. Der Erstaufruf dieser Methode erfolgt im Konstruktor. Sie können die Methode später erneut aufrufen, wenn Sie auf aktualisierte Statistiken oder Errungenschaften prüfen möchten.
bool CSteamAchievements::RequestStats() { // Ist Steam geladen? Falls nicht, können wir keine Statistiken abrufen. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Ist der Nutzer angemeldet? Falls nicht, können wir keine Statistiken abrufen. if ( !SteamUser()->BLoggedOn() ) { return false; } // Abfrage der Nutzerstatistiken return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

Parameter – Die Zeichenfolge mit dem Bezeichner der Errungenschaft, die Sie einstellen möchten (z. B. "ACH_WIN_ONE_GAME")
Rückgabewert – Boolescher Wert, der angibt, ob der Aufruf erfolgreich war oder nicht. Wenn der Aufruf fehlgeschlagen ist, wurde entweder Steam nicht initialisiert oder Sie haben die Rückruffunktion aus dem Erstaufruf von RequestStats. Sie können keine Errungenschaften freischalten, solange diese Rückruffunktion noch nicht empfangen wurde.
Funktionsweise – Diese Methode setzt eine bestimmte Errungenschaft auf "Freigeschaltet" und sendet die Ergebnisse an Steam. Sie können dieselbe Errungenschaft nicht mehrmals freischalten, daher müssen Sie sich keine Sorgen machen, dass Sie bereits eingestellte Errungenschaften erneut freischalten. Dies ist ein asynchroner Aufruf, der zwei Rückruffunktionen auslöst: OnUserStatsStored() und OnAchievementStored().
bool CSteamAchievements::SetAchievement(const char* ID) { // Haben wir schon einen Rückruf von Steam erhalten? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Falls nicht, können wir noch keine Errungenschaften freischalten. return false; }

OnUserStatsReceived()

Parameter – Keine
Rückgabewert – Keiner
Funktionsweise – Diese Methode ist eine Rückruffunktion, die immer aufgerufen wird, wenn Sie versuchen, Statistiken anzufordern. Statistiken und Errungenschaften fordern Sie mit RequestStats() an. Die Methode aktualisiert die Instanzvariable m_pAchievements, um die neuesten von Steam zurückgegebenen Daten zu Statistiken und Errungenschaften widerzuspiegeln.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // Eventuell erhalten wir Rückrufe für Statistiken von anderen Spielen; wir möchten diese ignorieren. if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // Errungenschaften laden for ( int iAch = 0; iAch < m_iNumAchievements; ++iAch ) { Achievement_t &ach = m_pAchievements[iAch]; SteamUserStats()->GetAchievement(ach.m_pchAchievementID, &ach.m_bAchieved); _snprintf( ach.m_rgchName, sizeof(ach.m_rgchName), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "name")); _snprintf( ach.m_rgchDescription, sizeof(ach.m_rgchDescription), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "desc")); } } else { char buffer[128]; _snprintf( buffer, 128, "RequestStats - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

OnUserStatsStored()

Parameter – Keine
Rückgabewert – Keiner
Funktionsweise – Diese Methode ist eine Rückruffunktion, die immer aufgerufen wird, wenn Sie versuchen, Statistiken auf Steam zu speichern.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // Eventuell erhalten wir Rückrufe für Statistiken von anderen Spielen; wir möchten diese ignorieren. if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "Stored stats for Steam\n" ); } else { char buffer[128]; _snprintf( buffer, 128, "StatsStored - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

OnAchievementStored()

Parameter – Keine
Rückgabewert – Keiner
Funktionsweise – Diese Methode ist eine Rückruffunktion, die immer aufgerufen wird, wenn Errungenschaften erfolgreich auf Steam gespeichert wurden.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // Eventuell erhalten wir Rückrufe für Statistiken von anderen Spielen; wir möchten diese ignorieren. if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

Schritt 3 – Integration in Ihr Spiel

Im Folgenden finden Sie eine vollständige Liste der Codeausschnitte, die Sie an den entsprechenden Stellen in Ihr Spiel integrieren müssen.

Definitionen und Globals

Im Folgenden finden Sie die Liste der Include-Dateien, die für die Erstellung von Errungenschaften erforderlich sind, die Aufzählung unserer spielspezifischen Errungenschaften und einen globalen Zeiger zu unserem Helper-Objekt. Bitte beachten Sie, dass die Errungenschaften denen auf der Verwaltungsseite von Steamworks entsprechen.
... #include "steam_api.h" // Definition unserer Errungenschaften. enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Errungenschaften-Array, das Daten über unsere Errungenschaften und der Zustand hält. Achievement_t g_Achievements[] = { _ACH_ID( ACH_WIN_ONE_GAME, "Winner" ), _ACH_ID( ACH_WIN_100_GAMES, "Champion" ), _ACH_ID( ACH_TRAVEL_FAR_ACCUM, "Interstellar" ), _ACH_ID( ACH_TRAVEL_FAR_SINGLE, "Orbiter" ), }; // Globaler Zugriff auf das Errungenschaftenobjekt. CSteamAchievements* g_SteamAchievements = NULL; ...

Initialisierung

Der Aufruf von SteamAPI_Init initialisiert Steam vollständig und muss vor allen anderen Schritten erfolgen. Wenn dieser Aufruf erfolgreich ist, erstellen wir das Helper-Objekt, indem wir das Array der Errungenschaften und die Größe des Arrays übergeben.
... // Steam initialisieren bool bRet = SteamAPI_Init(); // SteamAchievements-Objekt erstellen, wenn Steam erfolgreich initialisiert wurde. if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Verarbeiten von Rückruffunktionen

Um sicherzustellen, dass alle Steam-Rückruffunktionen verarbeitet werden, müssen wir regelmäßig auf neue Meldungen prüfen. Dies erreichen wir, indem wir der Spielschleife diesen Aufruf hinzufügen.
... SteamAPI_RunCallbacks(); ...

Auslösen von Errungenschaften

Das Auslösen einer Errungenschaft umfasst lediglich einen einzigen Aufruf, mit dem der Bezeichner der Errungenschaft übergeben wird.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Beenden

Der Aufruf von SteamAPI_Shutdown ist wahrscheinlich bereits in Ihrem Code enthalten. Dadurch wird Steam beendet. Dieser Aufruf muss erfolgen, bevor Ihre Anwendung beendet wird. Schließlich löschen wir das erstellte Helper-Objekt.
... // Steam beenden SteamAPI_Shutdown(); // Löschen des SteamAchievements-Objekts if (g_SteamAchievements) delete g_SteamAchievements; ...

Schritt 4 – Testen und Problembehebung

Dieser Beispielcode gibt Debug-Informationen an die Debug-Konsole aus, anhand derer Sie erkennen können, welche Aufrufe erfolgreich sind und welche nicht. Im Folgenden finden Sie einige typische Fehlermeldungen und die zugehörigen Lösungen:

Diese Anwendung konnte nicht gestartet werden, da die steam_api.dll nicht gefunden wurde. Möglicherweise können Sie das Problem beheben, indem Sie die Anwendung neu installieren.
Stellen Sie sicher, dass sich die steam_api.dll im selben Verzeichnis wie die ausführbare Datei befindet.

[S_API FAIL] SteamAPI_Init() fehlgeschlagen; keine ausgeführte Steam-Instanz oder keine lokale steamclient.dll gefunden.
Höchstwahrscheinlich wird kein Steam-Client ausgeführt. Starten Sie Steam und melden Sie sich an.

[S_API FAIL] SteamAPI_Init() fehlgeschlagen; keine App-ID gefunden.
Höchstwahrscheinlich ist die Datei steam_appid.txt nicht vorhanden. Legen Sie die Datei im Quellordner ab und stellen Sie sicher, dass sie Ihre App-ID enthält.