Dokumentacja Steamworks
Krok po kroku: osiągnięcia

Wprowadzenie

Poniżej znajduje się szybki poradnik krok po kroku dotyczący integracji najprostszych osiągnięć Steam z aplikacją, które mogą zostać wprowadzone w mniej niż 10 minut i które wymagają mniej niż 10 linii kodu w głównym kodzie gry. W narzędziach Steamworks SDK znajdziesz doskonały przykład tego typu rozwiązania — aplikację Spacewar. Na jej przykładzie możesz zapoznać się z całą gamą funkcji Steam. Ten artykuł prezentuje tylko najbardziej podstawowe informacje w oparciu o Spacewar oraz API osiągnięć i statystyk, by kwestia statystyk Steam była jak najbardziej przejrzysta. Zauważ, że statystyki i osiągnięcia pokrywają się w wielu miejscach, więc jeśli zintegrujesz obie funkcje, to wiele wywołań będzie mogło zostać skonsolidowanych.

Krok 1 – definiowanie osiągnięć twojej gry

Osiągnięcia są definiowane dla konkretnej aplikacji oraz konfigurowane na stronie osiągnięć w back-endzie administratora aplikacji Steamworks. Poniżej znajduje się lista osiągnięć z przykładowej aplikacji Steamworks o nazwie Spacewar:
spacewar_achievements.png

Krok 2 – enkapsulacja osiągnięć

Poniższy kod działa we wszystkich grach i możesz go zaimplementować wedle uznania. Ta klasa jest w pełni funkcjonalna sama w sobie, ale w razie potrzeby można ją łatwo rozszerzyć. Cały kod pochodzi bezpośrednio ze Spacewar z plików StatsAndAchievements.cpp/h.

Plik nagłówkowy

Zaczynamy od zdefiniowania struktury, która będzie przechowywać nasze dane na temat osiągnięć otrzymane od Steam. Następnie zapewniamy makra do tworzenia obiektów tego typu. Dane te odnoszą się bezpośrednio do tego, co znajduje się na stronie konfiguracji osiągnięć.
#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; };

Następnie definiujemy klasę pomocniczą, która będzie opakowywać wszystkie wywołania API statystyk Steam oraz tworzyć wszystkie wywołania zwrotne Steam.
class CSteamAchievements { private: int64 m_iAppID; // Nasze obecne AppID Achievement_t *m_pAchievements; // Dane o osiągnięciach int m_iNumAchievements; // Liczba osiągnięć bool m_bInitialized; // Czy wywołaliśmy żądanie statystyk i czy otrzymaliśmy wywołanie zwrotne? 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 ); };

Plik kodu

Konstruktor

Parametry – argumentami konstruktora są wskaźnik do tablicy osiągnięć oraz długość tablicy. Format tej tablicy zostanie pokazany w głównym kodzie gry później.
Zwraca – nie dotyczy.
Zastosowanie – konstruktor inicjalizuje kilka pól i równocześnie pobiera AppID bieżącej aplikacji. Oprócz tego łączy on metody wywołań zwrotnych w celu obsługi wywołań asynchronicznych przekazywanych do Steam. Na koniec początkowo wywołuje funkcję RequestStats(), aby uzyskać statystyki i osiągnięcia bieżącego użytkownika.
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()

Parametry – brak.
Zwraca – wartość bool wskazującą, czy wywołanie powiodło się. Jeśli wywołanie nie powiodło się, najprawdopodobniej Steam nie jest zainicjalizowany. Musisz upewnić się, że klient Steam jest otwarty w momencie wykonywania tego wywołania i że wcześniej wywołano funkcję SteamAPI_Init.
Zastosowanie – ta metoda zasadniczo wywołuje funkcję ISteamUserStats::RequestCurrentStats, która jest asynchronicznym wywołaniem do Steam z żądaniem o przesłanie statystyk i osiągnięć bieżącego użytkownika. To wywołanie musi zostać wykonane przed ustawieniem wartości jakichkolwiek statystyk i osiągnięć. Początkowe wywołanie tej metody ma miejsce w konstruktorze. Możesz potem wywołać je ponownie w dowolnym momencie, jeśli chcesz sprawdzić zaktualizowane statystyki i osiągnięcia.
bool CSteamAchievements::RequestStats() { // Czy Steam został wczytany? Jeśli nie, nie możemy uzyskać statystyk. if (NULL == SteamUserStats() || NULL == SteamUser()) { return false; } // Czy użytkownik jest zalogowany? Jeśli nie, nie możemy uzyskać statystyk. if ( !SteamUser()->BLoggedOn() ) { return false; } // Zażądaj statystyk użytkownika. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

Parametry – identyfikator typu string osiągnięcia, którego wartość chcesz ustawić (np. „ACH_WIN_ONE_GAME”).
Zwraca – wartość bool wskazującą, czy wywołanie powiodło się. Jeśli wywołanie nie powiodło się, to albo Steam nie został zainicjalizowany, albo wywołanie zwrotne z początkowego wywołania RequestStats nie zostało jeszcze przetworzone. Bez otrzymania wywołania zwrotnego nie będzie możliwe ustawienie statusu osiągnięć.
Zastosowanie – ta metoda ustawia status danego osiągnięcia na zdobyte i przesyła wynik do Steam. Status osiągnięcia można ustawiać wiele razy, więc nie musisz martwić się innymi osiągnięciami, które nie zostały jeszcze ustawione jako zdobyte. Jest to wywołanie asynchroniczne, które aktywuje dwa wywołania zwrotne: OnUserStatsStored() i OnAchievementStored().
bool CSteamAchievements::SetAchievement(const char* ID) { // Czy otrzymaliśmy wywołanie zwrotne ze Steam? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Jeśli nie, to nie można zmieniać jeszcze statusów osiągnięć return false; }

OnUserStatsReceived()

Parametry – nie dotyczy.
Zwraca – nic.
Zastosowanie – ta metoda to wywołanie zwrotne wykonywane za każdym razem, gdy próbujesz zażądać statystyk. Żądanie o przesłanie statystyk i osiągnięć jest wysyłane przy użyciu RequestStats(). Ta metoda aktualizuje pole m_pAchievements w celu odzwierciedlenia najnowszych danych statystyk i osiągnięć zwróconych przez Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // możliwe, że otrzymamy wywołania zwrotne o nadejściu statystyk innych gier, zignoruj je if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // wczytaj osiągnięcia 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()

Parametry – nie dotyczy.
Zwraca – nic.
Zastosowanie – ta metoda to wywołanie zwrotne wykonywane za każdym razem, gdy próbujesz zapisać statystyki na Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // możliwe, że otrzymamy wywołania zwrotne o nadejściu statystyk innych gier, zignoruj je 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()

Parametry – nie dotyczy.
Zwraca – nic.
Zastosowanie – ta metoda to wywołanie zwrotne wykonywane za każdym razem, gdy osiągnięcia zostają pomyślnie zapisane na Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // możliwe, że otrzymamy wywołania zwrotne o nadejściu statystyk innych gier, zignoruj je if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

Krok 3 – integracja z twoją grą

Poniżej znajduje się pełna lista fragmentów kodu, które należy zintegrować z twoją grą w odpowiednich miejscach.

Definicje i zmienne globalne

Poniżej znajduje się lista dyrektyw #include wymaganych dla obiektu Achievements, typ wyliczeniowy naszych osiągnięć dla danej gry oraz globalny wskaźnik do naszego obiektu pomocniczego. Pamiętaj, że osiągnięcia są zbieżne z tymi wymienionymi na stronie administratora Steamworks.
... #include "steam_api.h" // Definiujemy nasze osiągnięcia enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Tablica osiągnięć, która będzie przechowywać dane o osiągnięciach oraz o ich statusie 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" ), }; // Globalny dostęp do obiektu Achievements CSteamAchievements* g_SteamAchievements = NULL; ...

Inicjalizacja

Wywołanie SteamAPI_Init całościowo inicjalizuje Steam i musi zostać wykonane jako pierwsze. Jeśli będzie ono pomyślne, to następnie tworzymy obiekt pomocniczy, przekazując do niego tablicę osiągnięć wraz z jej rozmiarem.
... // Zainicjalizuj Steam bool bRet = SteamAPI_Init(); // Stwórz obiekt SteamAchievements, jeśli Steam został pomyślnie zainicjalizowany if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Obsługa wywołań zwrotnych

Aby upewnić się, że obsłużymy wszystkie wywołania zwrotne Steam, musimy regularnie sprawdzać, czy nie pojawiły się nowe komunikaty. Aby to zrobić, należy dodać to wywołanie do pętli gry.
... SteamAPI_RunCallbacks(); ...

Aktywacja osiągnięć

Aktywacja osiągnięcia jest prosta. Potrzebne jest tylko jedno wywołanie, które przekazuje identyfikator osiągnięcia.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Zamykanie

Wywołanie SteamAPI_Shutdown najprawdopodobniej znajduje się już w twoim kodzie. Zamyka ono Steam i musi zostać ono wywołane przed zamknięciem twojej aplikacji. Na końcu usuwamy poprzednio utworzony obiekt pomocniczy.
... // Zamknij Steam SteamAPI_Shutdown(); // Usuń obiekt SteamAchievements if (g_SteamAchievements) delete g_SteamAchievements; ...

Krok 4 – testowanie i rozwiązywanie problemów


Aby ustawić lub wyczyścić statystyki lub osiągnięcie bez dodawania kodu do twojej gry, możesz użyć konsoli klienta Steam. Uruchom steam.exe z parametrem -console, a następnie:
  • achievement_clear <appid> <achievement name>
  • reset_all_stats <appid>