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. The following is the list of achievements from the Steamworks sample app Spacewar:

spacewar_achievement_examplescreenshot.jpg

Trin 2 – Indkapsling af præstationsarbejde

The following code is game independent and can be added to your game as you see fit. The class is fully functional as is but can be easily extended to meet any further needs. All of this code was taken directly from the Spacewar example files StatsAndAchievements.cpp/h.

Headerfil

We first define a structure to hold our achievement data received from Steam and provide a macro for creating objects of that type. This data maps directly to what is found on the Achievement Configuration page.
#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; };

Next we define a helper class that will wrap all of the Steam Stats API calls as well as creating all of the Steam callbacks.
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. The formating of that array will be covered in the main game code later.
Returværdier: - N/A
Hvad det gør: - The constructor initializes a number of members along with grabbing the AppID we are currently running as. In addition it hooks up the call back methods to handle asynchronous calls made to Steam. Finally it makes an initial call to RequestStats() to get stats and achievements for the current user.
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: - None
Returværdier: - a bool representing if the call succeeded or not. If the call failed then most likely Steam is not initialized. Make sure you have a Steam client open when you try to make this call and that SteamAPI_Init has been called before it.
Hvad det gør: - This method basically wraps a call to ISteamUserStats::RequestCurrentStats which is an asynchronous call to steam requesting the stats and achievements of the current user. This call needs to be made before you can set any stats or achievements. The initial call to this method is made in the constructor. You can call it again any time after that if you want to check on updated stats or achievements.
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: - The string identifier of the Achievement that you want to set (ie. "ACH_WIN_ONE_GAME")
Returværdier: - a bool representing if the call succeeded or not. If the call failed then either Steam is not initialized or you still haven't processed the callback from the initial call to RequestStats. You can't set any achievements until that callback has been received.
Hvad det gør: - This method sets a given achievement to achieved and sends the results to Steam. You can set a given achievement multiple times so you don't need to worry about only setting achievements that aren't already set. This is an asynchronous call which will trigger two callbacks: OnUserStatsStored() and 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: - N/A
Returværdier: - Nothing
Hvad det gør: - This method is a callback that is called anytime you attempt to request stats. Stats and achievements are requested by using RequestStats(). The method updates the member variable m_pAchievements to reflect the latest stats and achievement data returned from 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 { 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: - N/A
Returværdier: - Nothing
Hvad det gør: - This method is a callback that is called anytime you attempt to store stats on 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: - N/A
Returværdier: - Nothing
Hvad det gør: - This method is a callback that is called anytime Achievements are successfully stored on 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

The following is a complete listing of code snippets that you would need to integrate into your game in the appropriate locations.

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. Please note that the achievements match those of the Admin page on 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

The call to SteamAPI_Init initializes all of Steam and must be called before anything else. If that call succeeds then we create the helper object by passing in the array of achievements along with the size of the array.
... // 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

To ensure that we process all Steam callbacks we need to regularly pump for new messages. This is achieved by adding this call to the game loop.
... SteamAPI_RunCallbacks(); ...

Udløsning af præstationer

Triggering an achievement is as simple as a single call passing along the achievement identifier.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Nedlukning

The call to SteamAPI_Shutdown is probably something you already have in your code. It shuts down Steam and must be called before your application exits. Finally we delete the helper object we created.
... // Luk Steam ned SteamAPI_Shutdown(); // Slet SteamAchievements-objektet if (g_SteamAchievements) delete g_SteamAchievements; ...

Trin 4 – Testning og fejlfinding


To set or clear stats or an achievement without adding code to your game, you can use the Steam client console. Run with steam.exe -console, then:
  • achievement_clear
  • reset_all_stats