Steamworks-dokumentaatio
Vaiheittaiset ohjeet: saavutukset
In Brief
Use achievements to reward players for hitting certain milestones or for interacting with your game in particular ways.
Level of integration
10 minutes and under 10 lines of code. Steamworks SDK integration necessary.

Johdanto

Achievements can be used as a way to encourage and reward player interactions and milestones within your game. They are often used for marking number of kills, miles driven, chests opened or other common actions within your game. And they can also be used to help your players discover different ways of playing your game. When unlocked, these achievements will pop up in the corner of the players' window and will be marked within an achievement page for that player.

Technical Overview

The following is a quick step by step guide to integrating very basic Steam Achievements into your application in under 10 minutes and under 10 lines of code integrated into your code base. The Steamworks SDK comes with a great example application called Spacewar that shows off the full breadth of Steam features and should be your first stop to see all Steam features in action. This tutorial distills the information found in Spacewar and the Stats and Achievements API down to just the necessary information required for Steam Stats to keep things as straightforward as possible. Please note that there is a considerable amount of overlap between stats and achievements, as such if you are integrating both be aware that a lot of calls can be consolidated.

Step 1 - Defining your game's Achievements

Achievements are application specific and are setup on the Achievement Configuration page in the Steamworks App Admin backend. The following is the list of achievements from the Steamworks sample app Spacewar:
spacewar_achievements.png

Step 2 - Encapsulating Achievements work

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.

Otsikkotiedosto

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; // Nykyinen AppID Achievement_t *m_pAchievements; // Saavutustiedot int m_iNumAchievements; // Saavutusten lukumäärä bool m_bInitialized; // Olemmeko kutsuneet pyyntöjen tilastoja ja saaneet takaisinkutsun? 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 ); };

Kooditiedosto

Konstruktori

Muuttujat - The constructor takes a pointer to an array of achievements along with the length of the array. The formating of that array will be covered in the main game code later.
Palautukset - N/A
Toiminta - 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() - tilastopyynnöt

Muuttujat - None
Palautukset – Muuttuja, joka osoittaa, onnistuiko kutsu vai ei. 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.
Toiminta - 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() { // Onko Steam ladattu? Jos ei, emme saa haettua tilastoja. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Onko käyttäjä kirjautuneena sisään? Jos ei, emme saa haettua tilastoja. if ( !SteamUser()->BLoggedOn() ) { return false; } // Pyynnön käyttäjätilastot. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement() - saavutusten määrittely

Muuttujat - The string identifier of the Achievement that you want to set (ie. "ACH_WIN_ONE_GAME")
Palautukset - 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.
Toiminta - 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) { // Olemmeko saaneet takaisinkutsua vielä Steamiltä? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Jos emme ole, emme voi määrittää vielä saavutuksia. return false; }

OnUserStatsReceived() - vastaanotetut tilastot

Muuttujat – —
Palautukset – Ei mitään
Toiminta - 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 ) { // Voimme saada muiden pelitilastojen takaisinkutsuja. Ohita ne! if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // Lataa saavutukset. 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() - tallennetuista käyttäjätilastoita

Muuttujat – —
Palautukset – Ei mitään
Toiminta - This method is a callback that is called anytime you attempt to store stats on Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // Voimme saada muilta saapuvilta pelitilastoilta takaisinkutsuja. Hylkää ne. 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() - tallennetuista saavutuksista

Muuttujat - N/A
Palautukset - Nothing
Toiminta - This method is a callback that is called anytime Achievements are successfully stored on Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // Voimme muiden pelien saapuvien tilastojen takaisinkutsuja. Hylkää ne! if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

Step 3 - Integrating into your game

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

Määrittäjät ja globaalit muuttujat

The following is the list of includes that are needed to build with Achievements, an enum of our game specific achievements and a global pointer to our helper object. Please note that the achievements match those of the Admin page on Steamworks.
... #include "steam_api.h" // Määrittelee saavutuksemme. enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Saavutusten jono, joka säilyttää tiedot saavutuksista ja niiden tilasta 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" ), }; // Saavutusobjektien yleiskäyttöoikeus CSteamAchievements* g_SteamAchievements = NULL; ...

Alustus

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.
... // Alusta Steam. bool bRet = SteamAPI_Init(); // Luo saavutusten SteamAchievements-objekti, jos Steamin alustus onnistui. if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Vastakutsujen käsittely

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();

Saavutusten käynnistäminen

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"); ...

Sammutus

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

Step 4 - Testing and Troubleshooting


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 <appid> <achievement name>
  • reset_all_stats <appid>