Steamworks-Dokumentation
Schritt-für-Schritt: Errungenschaften
Ein kurzer Überblick
Setzen Sie Errungenschaften ein, um Spiele für das Erreichen bestimmter Meilensteine und bestimmter Aktionen im Spiel zu belohnen.
Integrationslevel
10 Minuten und weniger als 10 Zeilen Code. Erfordert Integration von Steamworks-SDK.

Einführung

Errungenschaften muntern Spieler an und belohnen sie bestimmte Meilensteine zu erreichen sowie bestimmte Aktionen im Spiel vorzunehmen. Sie werden häufig verwendet, um die Anzahl der Kills, der gefahrenen Kilometer, geöffneten Truhen etc. in Ihrem Spiel zu markieren. Sie können Spieler auch anregen, Ihr Spiel auf unterschiedliche Weise zu spielen. Erreichte Errungenschaften werden in der Ecke des Spielfensters und auf der Seite für Errungenschaft des entsprechenden Spielers angezeigt.

Technische Details

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ßartige 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: Definition 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-Beispielapp Spacewar:

spacewar_achievement_examplescreenshot.jpg

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 von Steam empfangenen Errungenschaftsdaten aufgenommen werden, und stellen ein Makro zur Erstellung 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ückrufe 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ückgabe: - k. A.
Funktionsweise: - Der Konstruktor initialisiert eine Reihe von Instanzvariablen und ruft die App-ID ab, mit der wir gerade spielen. Außerdem werden der 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: - Nichts
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: 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ückgabe: - Boolescher Wert, der angibt, ob der Aufruf erfolgreich war oder nicht. Wenn der Aufruf fehlgeschlagen ist, wurde entweder Steam nicht initialisiert oder Sie haben der Rückruf aus dem Erstaufruf von RequestStats nicht bearbeitet. Sie können keine Errungenschaften freischalten, solange dieser Rückruf noch nicht empfangen wurde.
Funktionsweise: - Diese Methode setzt eine bestimmte Errungenschaft auf „Freigeschaltet“ und sendet die Ergebnisse an Steam. Sie können eine Errungenschaft mehrmals festlegen, sodass Sie sich nicht darum kümmern müssen, nur diejenigen festzulegen, die Sie noch nicht konfiguriert haben. Dies ist ein asynchroner Aufruf, der zwei Rückrufe 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: - k. A.
Rückgabe: - Nichts
Funktionsweise: - Diese Methode ist ein Rückruf, der 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: - k. A.
Rückgabe: - Nichts
Funktionsweise: - Diese Methode ist ein Rückruf, der 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: - k. A.
Rückgabewert: - Nichts
Funktionsweise: - Diese Methode ist ein Rückruf, der 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-Anweisungen, die für die Erstellung von Errungenschaften erforderlich sind, die Aufzählung unserer spielspezifischen Errungenschaften und einen globalen Zeiger zu unserem Helper-Objekt. Hinweis: Die Errungenschaften entsprechen denen auf der Verwaltungsseite auf Steamworks.
... #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); } ...

Verarbeitung von Rückrufen

Um sicherzustellen, dass alle Steam-Rückrufe 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


Über die Steam-Client-Konsole können Sie Statistiken oder Errungenschaften festlegen oder entfernen, ohne Code zu Ihrem Spiel hinzuzufügen. Starten Sie Steam mit „steam.exe -console“ und geben Sie dann die folgenden Befehle ein:
  • achievement_clear <appid> <achievement name>
  • reset_all_stats <appid>