Dokumentacja Steamworks
Krok po kroku: statystyki

Wprowadzenie

Poniżej znajduje się szybki poradnik krok po kroku dotyczący integracji najprostszych statystyk 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 statystyk twojej gry

Statystyki są definiowane dla konkretnej aplikacji oraz konfigurowane na stronie statystyk w back-endzie administratora aplikacji Steamworks. Poniżej znajduje się lista statystyk z przykładowej aplikacji Steamworks o nazwie Spacewar:
stats_spacewar.png

Krok 2 – enkapsulacja statystyk

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 statystyk otrzymane od Steam. Następnie zapewniamy makra do tworzenia obiektów statystyk. Dane te odnoszą się bezpośrednio do tego, co znajduje się na stronie konfiguracji statystyk.
#define _STAT_ID( id,type,name ) { id, type, name, 0, 0, 0, 0 } enum EStatTypes { STAT_INT = 0, STAT_FLOAT = 1, STAT_AVGRATE = 2, }; struct Stat_t { int m_ID; EStatTypes m_eStatType; const char *m_pchStatName; int m_iValue; float m_flValue; float m_flAvgNumerator; float m_flAvgDenominator; };

Następnie definiujemy klasę pomocniczą, która będzie opakowywać wszystkie wywołania API statystyk Steam oraz tworzyć wszystkie wywołania zwrotne Steam.
class CSteamStats { private: int64 m_iAppID; // Nasz obecny identyfikator aplikacji Stat_t *m_pStats; // Dane o statystykach int m_iNumStats; // Liczba statystyk bool m_bInitialized; // Czy wywołaliśmy żądanie statystyk i czy otrzymaliśmy wywołanie zwrotne? public: CSteamStats(Stat_t *Stats, int NumStats); ~CSteamStats(); bool RequestStats(); bool StoreStats(); STEAM_CALLBACK( CSteamStats, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived ); STEAM_CALLBACK( CSteamStats, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored ); };

Plik kodu

Konstruktor

Parametry – argumentami konstruktora są wskaźnik do tablicy statystyk 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.
CSteamStats::CSteamStats(Stat_t *Stats, int NumStats) : m_iAppID( 0 ), m_bInitialized( false ), m_CallbackUserStatsReceived( this, &CSteamStats::OnUserStatsReceived ), m_CallbackUserStatsStored( this, &CSteamStats::OnUserStatsStored ) { m_iAppID = SteamUtils()->GetAppID(); m_pStats = Stats; m_iNumStats = NumStats; 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 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 CSteamStats::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(); }

StoreStats()

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::StoreStats, która jest asynchronicznym wywołaniem do Steam, które przechowuje statystyki bieżącego użytkownika na serwerze. To wywołanie musi zostać wykonane za każdym razem, gdy chcesz zaktualizować statystyki użytkownika.
bool CSteamStats::StoreStats() { if ( m_bInitialized ) { // wczytaj statystyki for ( int iStat = 0; iStat < m_NumStats; ++iStat ) { Stat_t &stat = m_pStats[iStat]; switch (stat.m_eStatType) { case STAT_INT: SteamUserStats()->SetStat( stat.m_pchStatName, stat.m_iValue ); break; case STAT_FLOAT: SteamUserStats()->SetStat( stat.m_pchStatName, stat.m_flValue ); break; case STAT_AVGRATE: SteamUserStats()->UpdateAvgRateStat(stat.m_pchStatName, stat.m_flAvgNumerator, stat.m_flAvgDenominator ); // Jest dla nas obliczany uśredniony wynik SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue ); break; default: break; } } return SteamUserStats()->StoreStats(); } }

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 jest wysyłane przy użyciu RequestStats(). Ta metoda aktualizuje pole m_Stats w celu odzwierciedlenia najnowszych danych statystyk zwróconych przez Steam.
void CSteamStats::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" ); // załaduj statystyki for ( int iStat = 0; iStat < m_iNumStats; ++iStat ) { Stat_t &stat = m_Stats[iStat]; switch (stat.m_eStatType) { case STAT_INT: SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_iValue); break; case STAT_FLOAT: case STAT_AVGRATE: SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue); break; default: break; } } m_bInitialized = true; } 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. Jeśli podczas próby ustawienia wartości statystyk jedna z nich naruszy nałożone ograniczenia, to zostaną one przywrócone do swoich poprzednich wartości.
void CSteamStats::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( "StoreStats - success\n" ); } else if ( k_EResultInvalidParam == pCallback->m_eResult ) { // Przynajmniej jedna statystyka naruszyła ograniczenie. Zostały one wycofane, // a teraz należy ponownie iterować wartości, by były one zsynchronizowane. OutputDebugString( "StoreStats - some failed to validate\n" ); // Wykonaj sztuczne wywołanie zwrotne, by ponownie wczytać statystyki. UserStatsReceived_t callback; callback.m_eResult = k_EResultOK; callback.m_nGameID = m_iAppID; OnUserStatsReceived( &callback ); } else { char buffer[128]; _snprintf( buffer, 128, "StoreStats - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

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 Stats, tablica naszych statystyk dla danej gry oraz globalny wskaźnik do naszego obiektu pomocniczego. Pamiętaj, że statystyki są zbieżne z tymi wymienionymi na stronie administratora Steamworks.
... #include "steam_api.h" #include "isteamuserstats.h" #include "SteamStats.h" // Tablica statystyk, która będzie przechowywać dane o statystykach oraz o ich statusie Stat_t g_Stats[] = { _STAT_ID( 1, STAT_INT, "NumGames"), _STAT_ID( 2, STAT_INT, "NumWins"), _STAT_ID( 3, STAT_INT, "NumLosses"), _STAT_ID( 4, STAT_FLOAT, "FeetTraveled"), _STAT_ID( 5, STAT_AVGRATE, "AverageSpeed"), _STAT_ID( 7, STAT_FLOAT, "MaxFeetTraveled"), }; // Globalny dostęp do obiektu Stats CSteamStats* g_SteamStats = 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ę statystyk wraz z jej rozmiarem.
... // Zainicjalizuj Steam bool bRet = SteamAPI_Init(); // Stwórz obiekt SteamStats, jeśli Steam został pomyślnie zainicjalizowany if (bRet) { g_SteamStats = new CSteamStats(g_Stats, 6); } ...

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

Zapisywanie statystyk

Statystyki są zapisywane poprzez wykonanie pojedynczego wywołania do StoreStats().
... if (g_SteamStats) g_SteamStats->StoreStats(); ...

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 SteamStats if (g_SteamStats) delete g_SteamStats; ...

Krok 4 – testowanie i rozwiązywanie problemów

Ten przykładowy kod wyświetla informacje debugowania w konsoli debugowania, które pomogą ci zrozumieć, które wywołania są pomyślne, a które nie. Poniżej przedstawiono typowe komunikaty o błędach i możliwe sposoby ich 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 steam_api.dll. Ponowna instalacja aplikacji może rozwiązać problem. Upewnij się, że plik steam_api.dll znajduje 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źć uruchomionej instancji Steam lub lokalnego pliku steamclient.dll. Najprawdopodobniej klient Steam nie jest uruchomiony. 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 w twoim folderze źródłowym i upewnij się, że ma on numer AppID twojej aplikacji.