Steamworks-dokumentation
Trinvis vejledning: Præstationer
Kort opsummering
Brug præstationer til at belønne spillere for at nå bestemte milepæle eller for at interagere med dit spil på bestemte måder.
Integreringsniveau
10 minutter og under 10 linjer med kode. Steamworks SDK-integrering er nødvendig.

Introduktion

Præstationer kan bruges som en måde til at opfordre og belønne spillerinteraktioner for at nå milepæle i dit spil. De bruges ofte til at markere et bestemt antal drab, kørte distancer, åbnede kister eller andre almindelige handlinger i dit spil. Og de kan også bruges til at hjælpe spillere med at opdage forskellige måder at spille dit spil på. Når disse præstationer bliver låst op, dukker de op i hjørnet af spillerens vindue og bliver markeret indenfor en præstationsside for den pågældende spiller.

Teknisk oversigt

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_achievement_examplescreenshot.jpg

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 pointer til et array af præstationer samt længden af arrayet. Formateringen af arrayet dækkes i den primære spilkode senere.
Returværdier:
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:
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:
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:
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-objektet, en opremsning af vores spilspecifikke præstationer og en global pointer 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


Du kan bruge Steam-klientkonsollen til at indstille eller rydde statistikker eller præstationer uden at tilføje kode til dit spil. Kør med steam.exe -console og derefter:
  • achievement_clear <appid> <achievement name>
  • reset_all_stats <appid>