Dokumentacja Steamworks
Osiągnięcia: Krok po kroku

Wprowadzenie

Poniżej znajdują się instrukcje dotyczące integracji najprostszych osiągnięć z aplikacją, które mogą zostać wprowadzone w mniej niż 10 minut i do których nie trzeba więcej niż 10 linii kodu zawartych w głównym kodzie gry. W sekcji 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. W tym przewodniku informacje prezentowane w Spacewar są filtrowane, aby wyświetlić statystyki i osiągnięcia potrzebne do zrozumienia interfejsu.
Instruktaż prezentuje najważniejsze informacje na temat Spacewar i interfejsu statystyk i osiągnięć, niezbędne do zrozumienia funkcjonowania Statystyk Steam. Zauważ, że statystyki i osiągnięcia pokrywają się w wielu miejscach. Jeśli więc zintegrujesz obie funkcje, wiele wywołań będzie mogło zostać skonsolidowanych.

1. Definicja osiągnięć

Osiągnięcia są zdefiniowane dla konkretnej aplikacji i są instalowane na stronie osiągnięć https://partner.steamgames.com/apps/achievements/ w części przeznaczonej dla administratora Steamworks. Oto osiągnięcia ze Spacewar:
spacewar_achievements.png

2. Kapsułowanie osiągnięć

Poniższy kod działa we wszystkich grach i można go dodać do każdej z nich. Klasa jest już w pełni funkcjonalna, ale w razie potrzeby można ją łatwo rozszerzyć. Cały kod jest pobierany bezpośrednio z pliku Spacewar StatsAndAchievements.cpp/h.

Plik nagłówka

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

Ponadto definiowana jest klasa pomocnicza, która będzie zawierać wszystkie wywołania interfejsu API statystyk.
class CSteamAchievements { private: int64 m_iAppID; // Nasz obecny identyfikator aplikacji Achievement_t * m_pAchievements; // Dane o osiągnięciach int m_iNumAchievements; // Liczba osiągnięć bool m_bInitialized; // Czy zostało wysłane żądanie o statystyki 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); };

Kod programu

Konstruktor

Parametry — konstruktor pobiera wskaźnik do tablicy osiągnięć i długość tablicy. Format tej tablicy zostanie pokazany w głównym kodzie gry.
Zwróć wartości — nie dotyczy.
To, co robi projektant — inicjuje kilku członków i akceptuje identyfikator AppID bieżącej aplikacji. Oprócz tego łączy on metody wywołań zwrotnych w celu obsługi wywołań asynchronicznych na Steam. Na koniec wywołuje wywołanie funkcji RequestStats(), aby uzyskać statystyki i osiągnięcia bieżącego użytkownika.
CSteamAchievements :: CSteamAchievements (Achievement_t * Osiągnięcia, int NumAchievements): m_iAppID (0), m_bInitialized (false), m_CallbackUserStatsReceived (this, & CSteamAchievements :: OnUserStatsReceived), m_CallbackUserStatsStored (this, & CSteamAchievements :: OnUserStatsStored), m_CallbackStored (to i CSteamAchievements :: OnAchievementStored) { m_iAppID = SteamUtils()>GetAppID (); m_pAchievements = Osiągnięcia; m_iNumAchievements = NumAvievements; RequestStats (); }

RequestStats()

Parametry — Brak
Zwrócone wartości — bool, wskazuje, czy połączenie powiodło się. Jeśli połączenie nie powiedzie się, najprawdopodobniej Steam nie zostanie zainicjowany. Przed wywołaniem musisz upewnić się, że klient jest otwarty i że wcześniej wywołano funkcję SteamAPI_Init.
To co robi polega na tym, że ta metoda jest zasadniczo wywołuje funkcję ISteamUserStats::RequestCurrentStats, która jest asynchronicznym zapytaniem o statystyki i osiągnięcia bieżącego użytkownika. To wywołanie musi zostać wykonane przed zainstalowaniem statystyk i osiągnięć. Początkowe wywołanie tej metody występuje w konstruktorze. Możesz wywołać ją ponownie w dowolnym momencie, jeśli chcesz poznać zaktualizowane statystyki i osiągnięcia.
bool CSteamAchievements::RequestStats() { // Załadowano Steam? Jeśli nie, statystyki się nie powiodą. if (NULL == SteamUserStats() || NULL == SteamUser()) { return false; } // Użytkownik zalogowany? Jeśli nie, statystyki nie będą dostępne. if ( !SteamUser()->BLoggedOn() ) { return false; } // Poproś o statystyki. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

Parametry — identyfikator ciągu osiągnięcia, które ma zostać ustawione (na przykład "ACH_WIN_ONE_GAME")
Zwrócone wartości — bool, wskazuje, czy połączenie powiodło się. Jeśli połączenie nie powiedzie się, nie zostanie ono zainicjowane przez Steam, lub początkowe wywołania zwrotne RequestStats nie zostały jeszcze przetworzone. Bez otrzymania wywołania zwrotnego nie będzie możliwe ustalenie osiągnięć.
Jak działa — ta metoda ustala, że to osiągnięcie zostało zakończone, i przesyła wynik do Steam. Osiągnięcie można ustawić kilka razy, więc nie musisz się martwić instalowaniem tylko tych osiągnięć, które nie zostały jeszcze ustanowione. Jest to wywołanie asynchroniczne, w odpowiedzi na które będą odbierane dwa wywołania zwrotne: OnUserStatsStored() i OnAchievementStored().
bool CSteamAchievements::SetAchievement(const char* ID) { // odebrał wywołanie zwrotne ze Steam? if (m_bInitialized) { SteamUserStats()-> SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Jeśli nie, osiągnięcie nie będzie działać return false; }

OnUserStatsReceived()

Parametry - nie dotyczy.
Zwrócone wartości — Nic.
Jak działa — Ta metoda to odpowiedź zwrotna, która jest wysyłana, gdy gra próbuje zażądać statystyk na Steam. Statystyki i osiągnięcia są wymagane przy użyciuRequestStats(). Ta metoda aktualizuje zmienną członka m_pAchievements w celu odzwierciedlenia najnowszych danych statystyk i osiągnięć zwróconych przez Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // możemy otrzymywać odpwiedzi 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; // ładowanie osiągnięć 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 — Brak.
Odpowiedzi zwrotne — Nic.
Zastosowanie — Ta metoda to odpowiedź zwrotna, która jest wysyłana za każdym razem, gdy gra próbuje zapisać statystyki na Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // możemy orzymać wywołania zwrotne ze statystykami z innych gier, ignoruj 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 — Brak.
Odpowiedzi zwrotne — Nic.
Zastosowanie — Ta metoda to wywołanie zwrotne, które jest wysyłane, gdy osiągnięcia są zapisywane na platformie Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // możemy otrzymać wywołania zwrotne ze statystykami z innych gier, ignorouj je if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

3. Integracja w grze

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

Definicje i zmienne globalne

Poniżej znajduje się lista połączeń wymaganych dla obiektu Osiągnięcia, lista osiągnięć związanych z tą grą oraz globalny wskaźnik do obiektu pomocniczego. Osiągnięcia są zbieżne z tymi wymienionymi na stronie Steamworks.
... #include "steam_api.h" // definiujemy 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, }; // szereg osiągnięć zawierających dane o osiągnięciach i 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 Osiągnięcia CSteamAchievements* g_SteamAchievements = NULL; ...

Inicjalizacja

Wywołanie SteamAPI_Init inicjuje Steam i musi zostać wykonane jako pierwsze. Jeśli wywołanie się powiedzie, tworzony jest obiekt pomocniczy, do którego przenoszona jest tablica osiągnięć i rozmiar tablicy.
... // inicjalizacja Steam bool bRet = SteamAPI_Init(); // twórz obiekt SteamAchievements, jeśli inicjalizacja Steam powiedzie się if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Obsługa odpowiedzi zwrotnych

Aby obsłużyć wszystkie wywołania zwrotne Steam, musisz regularnie sprawdzać, czy nie pojawiły się nowe wiadomości. Aby to zrobić, należy dodać to połączenie do pętli gry.
... SteamAPI_RunCallbacks(); ...

Aktywacja osiągnięć

Osiągnięcie aktywacji jest proste. Potrzebne jest tylko jedno połączenie, które przekazuje identyfikator osiągnięcia.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Zamykanie interfejsu SteamAPI

Wywołanie SteamAPI_Shutdown, które zamyka Steam, najprawdopodobniej znajduje się już w twoim kodzie. Musi zostać wysłane przed zamknięciem aplikacji. Na końcu poprzednio utworzony obiekt pomocniczy zostaje usunięty.
... // Wyłącz Steam SteamAPI_Shutdown(); // Usuń SteamAchievements if (g_SteamAchievements) delete g_SteamAchievements; ...

4. Testowanie i rozwiązywanie problemów

Ten przykładowy kod wyświetla informacje debugowania w konsoli debugowania, które są potrzebne do zrozumienia, które połączenia są wykonywane, a które nie. Poniżej przedstawiono typowe komunikaty o błędach i rozwiązania:

This application has failed to start because steam_api.dll was not found. Re-installing the application may fix this problem. (Aplikacja nie została uruchomiona, ponieważ nie znaleziono pliku d3dx_??. Dll. Ponowna instalacja aplikacji może rozwiązać problem).
Plik steam_api.dll powinien znajdować się w tym samym katalogu, co plik wykonywalny.

[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a local steamclient.dll (Wywołanie funkcji SteamAPI_Init() nie powiodło się, ponieważ nie można znaleźć działającego pliku Steam lub lokalnego pliku steamclient.dll).
Najprawdopodobniej klient Steam nie działa. Uruchom Steam i zaloguj się.

[S_API FAIL] SteamAPI_Init() failed; no appID found. (Wywołanie funkcji SteamAPI_Init () nie powiodło się, nie znaleziono AppID)
Najprawdopodobniej brakuje pliku steam_appid.txt we właściwym folderze. Umieść go we właściwym miejscu i upewnij się, że ma numer aplikacji.