Steamworks-dokumentation
Trinvis vejledning: Præstationer

Introduktion

Det følgende er en hurtig trinvis vejledning til at integrere helt grundlæggende Steam-præstationer i din applikation på under ti minutter og med under ti linjers kode intregreret i din kodebase. Steamworks-SDK'en har et godt applikationseksempel kaldet Spacewar, som viser Steam-funktionernes fulde bredde, og du bør starte her for at se, hvordan alle Steam-funktionerne fungerer. Denne introduktion koger oplysninger i Spacewar og statistik- og præstations-API'en ned til de mest nødvendige oplysninger til Steam-statistikker for at gøre det så overskueligt som muligt. Bemærk, at der er et betydeligt overlap mellem statistikker og præstationer, så hvis du integrerer begge dele, skal du være opmærksom på, at mange kald kan konsolideres.

Trin 1 – Definering af dit spils præstationer

Præstationer er applikationsspecifikke og er konfigureret på siden Præstationskonfiguration i App-administratoren i Steamworks-backend. Det følgende er en liste over præstationer fra Steamworks-applikationseksemplet Spacewar:
spacewar_achievements.png

Trin 2 – Indkapsling af præstationsarbejde

Den følgende kode er spiluafhængig, og du kan tilføje den til dit spil, som du synes. Klassen er fuldt ud funktionel, som den er, men kan nemt udvides for at opfylde yderligere behov. Al koden er taget direkte fra Spacewar-fileksemplerne StatsAndAchievements.cpp/h.

Headerfil

Vi definerer først en struktur, hvori vores præstationsdata ligger, som modtages fra Steam, og laver en makro til oprettelse af objekter af den type. Disse data henviser direkte til det, som findes på siden Præstationskonfiguration.
#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; };

Derefter definerer vi en hjælperklasse, som ombryder alle kald til Steam-statistik-API'en samt opretter alle Steam-tilbagekald.
class CSteamAchievements { private: int64 m_iAppID; // Vores nuværende app-ID Achievement_t *m_pAchievements; // Præstationsdata int m_iNumAchievements; // Antallet af præstationer bool m_bInitialized; // Har vi kaldt RequestStats og modtaget et tilbagekald? 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 ); };

Kodefil

Konstruktør

Parametre: Konstruktøren indeholder en reference til et array af præstationer samt længden af arrayet. Formateringen af arrayet dækkes i den primære spilkode senere.
Returværdier: Ingen.
Hvad det gør: Konstruktøren initialiserer et antal medlemmer og tager app-ID'et for den aktuelle applikation. Derudover forbinder den tilbagekaldsmetoderne til at håndtere asynkrone kald til Steam. Endelig foretager den det første kald til RequestStats() for at få statistikker og præstationer for den aktuelle bruger.
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()

Parametre: Ingen.
Returværdier: En boolsk værdi, som viser, om kaldet lykkedes eller ej. Hvis kaldet mislykkedes, er Steam sandsynligvis ikke initialiseret. Sørg for, at Steam-klienten er åben, når du prøver at foretage dette kald, og at SteamAPI_Init er blevet kaldt forinden.
Hvad det gør: Denne metode ombryder et kald til ISteamUserStats::RequestCurrentStats, som er et asynkront kald til Steam, der anmoder om statistikker og præstationer for den aktuelle bruger. Dette kald skal foretages, før du kan angive nogen statistikker eller præstationer. Det første kald til denne metode foretages i konstruktøren. Du kan foretage kaldet igen når som helst bagefter, hvis du vil tjekke opdaterede statistikker eller præstationer.
bool CSteamAchievements::RequestStats() { // Er Steam indlæst? Hvis ikke, kan vi ikke få statistikker. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Er brugeren logget på? Hvis ikke, kan vi ikke få statistikker. if ( !SteamUser()->BLoggedOn() ) { return false; } // Anmoder om brugerstatistikker. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

Parametre: Strengnavnet på den præstation, du vil angive (dvs. "ACH_WIN_ONE_GAME")
Returværdier: En boolsk værdi, som viser, om kaldet lykkedes eller ej. Hvis kaldet mislykkedes, er Steam enten ikke initialiseret, eller tilbagekaldet fra det første kald til RequestStats er ikke blevet behandlet endnu. Du kan ikke angive nogen præstationer, før tilbagekaldet er blevet modtaget.
Hvad det gør: Denne metode angiver, at en given præstation er opnået, og sender resultatet til Steam. Du kan angive en præstation flere gange, så du behøver ikke at bekymre dig om kun at angive præstationer, der ikke er angivet endnu. Dette er et asynkront kald, som vil udløse to tilbagekald: OnUserStatsStored() og OnAchievementStored().
bool CSteamAchievements::SetAchievement(const char* ID) { // Har vi modtaget et tilbagekald fra Steam endnu? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Hvis ikke, kan vi ikke angive nogen præstationer endnu. return false; }

OnUserStatsReceived()

Parametre: Ingen.
Returværdier: Ingen.
Hvad det gør: Denne metode er et tilbagekald, som kaldes, hver gang du forsøger at anmode om statistikker. Anmodninger om statistikker og præstationer foretages vha. RequestStats(). Metoden opdaterer medlemsvariablen m_pAchievements, så den afspejler de seneste statistik- og præstationsdata, som er returneret fra Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // Vi får måske tilbagekald for andre spils indkommende statistikker – ignorer dem if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // indlæse præstationer 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()

Parametre: Ingen.
Returværdier: Ingen.
Hvad det gør: Denne metode er et tilbagekald, som kaldes, hver gang du forsøger at lagre statistikker på Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // Vi får måske tilbagekald for andre spils indkommende statistikker – ignorer dem 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()

Parametre: Ingen.
Returværdier: Ingen.
Hvad det gør: Denne metode er et tilbagekald, som kaldes, hver gang præstationer lagres på Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // Vi får måske tilbagekald for andre spils indkommende statistikker – ignorer dem if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

Trin 3 – Integrering i dit spil

Det følgende er en komplet liste over kodestykker, som du skal integrere på de relevante steder i dit spil.

Definitioner og globale variabler

Det følgende er en liste over include-data, som kræves for at bygge med Achievements, en opremsning af vores spilspecifikke præstationer og en global reference til vores hjælperobjekt. Bemærk, at præstationerne matcher dem på administratorsiden i Steamworks.
... #include "steam_api.h" // Definerer vores præstationer enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Achievements-arrayet, som vil indeholde data om præstationerne og deres tilstand 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" ), }; // Global adang til Achievements-objektet CSteamAchievements* g_SteamAchievements = NULL; ...

Initialisering

Kaldet til SteamAPI_Init initialiserer hele Steam og skal kaldes før noget andet. Hvis kaldet lykkes, opretter vi hjælperobjektet ved at sende arrayet af præstationer med størrelsen på arrayet.
... // Initialiser Steam bool bRet = SteamAPI_Init(); // Opret SteamAchievements-objektet, hvis Steam blev initialiseret if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Behandling af tilbagekald

For at sikre, at vi behandler alle Steam-tilbagekald, skal vi jævnligt tjekke, om der er nye beskeder. Dette gøres ved at tilføje dette kald til spilløkken.
... SteamAPI_RunCallbacks(); ...

Udløsning af præstationer

Udløsning af en præstation involver kun et enkelt kald, som formidler præstationsidentifikatoren.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Nedlukning

Kaldet til SteamAPI_Shutdown er sikkert noget, du allerede har i din kode. Den lukker Steam ned og skal kaldes, før applikationen lukker. Til sidst sletter vi det hjælperobjekt, vi oprettede.
... // Luk Steam ned SteamAPI_Shutdown(); // Slet SteamAchievements-objektet if (g_SteamAchievements) delete g_SteamAchievements; ...

Trin 4 – Testning og fejlfinding

Denne eksempelkode giver fejlfindingsinformation til fejlfindingskonsollen, som kan hjælpe dig med at forstå, hvilke kald der lykkes og mislykkes. Det følgende er nogle typiske fejlbeskeder og løsninger:

Denne applikation kunne ikke starte, fordi steam_api.dll ikke kunne findes. Geninstallering af applikationen løser muligvis dette problem.
Sørg for, at steam_api.dll er i samme mappe som den eksekverbare fil.

[S_API FAIL] SteamAPI_Init() mislykkedes. Kunne ikke lokalisere en kørende instans af Steam eller en lokal steamclient.dll
Steam-klienten kører sandsynligvis ikke. Start Steam, og log på.

[S_API FAIL] SteamAPI_Init() mislykkedes. Intet app-ID fundet.
steam_appid.txt-filen er sandsynligvis ikke i den rigtige mappe. Placer den i kildemappen, og kontrollér, at den indeholder dit app-ID.