Documentazione di Steamworks
Statistiche: tutorial

Introduzione

Quella che segue è una breve guida per aiutarti a integrare, passo dopo passo, le statistiche basilari di Steam nella tua applicazione, in meno di 10 minuti e integrando meno di 10 righe nel tuo codice di base. L'SDK di Steamworks include una fantastica applicazione demo denominata Spacewar, che mostra l'intera gamma di funzioni di Steam e consente di vederle in azione. In questo tutorial, le informazioni disponibili in Spacewar e quelle sull'API per le statistiche e gli achievement vengono sintetizzate e ridotte alle informazioni necessarie per rendere il procedimento più semplice possibile. Tieni presente che statistiche e achievement presentano un considerevole grado di sovrapposizione. Pertanto, se desideri integrare entrambi, sarà necessario integrare numerose chiamate alle API.

Passo 1: definizione delle statistiche del tuo gioco

Le statistiche sono elementi specifici dell'applicazione e vengono impostate dalla pagina Configurazione statistiche nel back-end dell'amministratore dell'app di Steamworks. Di seguito è illustrato l'elenco delle statistiche dall'applicazione demo Spacewar di Steamworks:
stats_spacewar.png

Passo 2: integrazione delle statistiche

Il codice presentato di seguito non dipende dal gioco e può essere aggiunto al tuo titolo se lo ritieni opportuno. La classe è completamente funzionale, ma può essere facilmente estesa per soddisfare qualsiasi ulteriore necessità. L'intero codice è estratto direttamente dai file di esempio di Spacewar StatsAndAchievements.cpp/h.

File di intestazione

In primo luogo, definiamo una struttura che conservi i nostri dati statistici ricevuti da Steam, definiamo i tipi di dati in un pratico enum e definiamo in ultimo una macro per la creazione di oggetti statistici. Questi dati vengono associati direttamente al contenuto della pagina Configurazione statistiche.
#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; };

A questo punto, definiamo una classe helper che raggruppi tutte le chiamate alle API delle statistiche di Steam e che crei anche tutte le richiamate di Steam.
class CSteamStats { private: int64 m_iAppID; // Il nostro AppID corrente Stat_t *m_pStats; // Dati statistiche int m_iNumStats; // Numero delle statistiche bool m_bInitialized; // Abbiamo effettuato la chiamata Request stats e ricevuto la richiamata? 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 ); };

File di codice

Costruttore

Parametri: il costruttore porta un puntatore su un array di statistiche per tutta la lunghezza dell'array. L'ordine di questo array verrà trattato in seguito, quando affronteremo il codice del gioco principale.
Restituisce: non disponibile.
Funzione: il costruttore inizializza un numero di membri e rileva l'AppID attualmente in esecuzione. Inoltre, si aggancia ai metodi di richiamata per gestire le chiamate asincrone a Steam. Infine, effettua una chiamata iniziale a RequestStats() per ottenere statistiche e achievement dell'utente attuale.
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()

Parametri: nessuno.
Restituisce: una variabile booleana indicante la riuscita o il fallimento della chiamata. Se la chiamata non è riuscita, molto probabilmente Steam non è inizializzato. Assicurati di avere un client di Steam aperto durante il tentativo di chiamata e di aver precedentemente effettuato la chiamata a SteamAPI_Init.
Funzione: questo metodo sostanzialmente invia una chiamata a ISteamUserStats::RequestCurrentStats, ossia una chiamata asincrona a Steam, per richiedere le statistiche dell'utente attuale. È necessario effettuare questa chiamata prima di poter impostare qualsiasi statistica o achievement. La chiamata iniziale a questo metodo viene effettuata attraverso il costruttore e può essere effettuata nuovamente ogni volta che si desidera controllare le statistiche o gli achievement aggiornati.
bool CSteamStats::RequestStats() { // Steam è caricato? In caso contrario, non è possibile ottenere statistiche. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // L'utente ha effettuato il log in? In caso contrario, non è possibile ottenere statistiche. if ( !SteamUser()->BLoggedOn() ) { return false; } // Request user stats. return SteamUserStats()->RequestCurrentStats(); }

StoreStats()

Parametri: nessuno.
Restituisce: una variabile booleana indicante la riuscita o il fallimento della chiamata. Se la chiamata non è riuscita, molto probabilmente Steam non è inizializzato. Assicurati di avere un client di Steam aperto durante il tentativo di chiamata e di aver precedentemente effettuato la chiamata a SteamAPI_Init.
Funzione: questo metodo sostanzialmente invia una chiamata a ISteamUserStats::StoreStats, ossia una chiamata asincrona a Steam che conservi le statistiche dell'utente attuale sul server. Questa chiamata deve essere effettuata ogni volta che si desiderano aggiornare le statistiche dell'utente.
bool CSteamStats::StoreStats() { if ( m_bInitialized ) { // carica statistiche 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 ); // Il risultato medio viene calcolato SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue ); break; default: break; } } return SteamUserStats()->StoreStats(); } }

OnUserStatsReceived()

Parametri: non disponibile.
Restituisce: niente.
Funzione: questo metodo consiste di una richiamata attivata a ogni tentativo di richiesta delle statistiche. Le statistiche vengono richieste utilizzando RequestStats(). Il metodo aggiorna la variabile m_Stats del membro per restituire gli ultimi dati statistici ottenuti da Steam.
void CSteamStats::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // potremmo ricevere richiamate entranti dalle statistiche di altri giochi, ignorale if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "Received stats and achievements from Steam\n" ); // Carica le statistiche 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 non riuscito, %d\n", pCallback->m_eResult ); OutputDebugString (buffer); } } }

OnUserStatsStored()

Parametri: non disponibile.
Restituisce: nessuna.
Funzione: questo metodo consiste in una richiamata attivata a ogni tentativo di archiviazione delle statistiche su Steam. Se una delle statistiche che abbiamo cercato di impostare ha infranto un vincolo, verrà ripristinata al valore precedente. In questo caso, carichiamo nuovamente i valori.
void CSteamStats::OnUserStatsStored( UserStatsStored_t *pCallback ) { // Potremmo ricevere richiamate per le statistiche in arrivo di altri giochi, ignorarle if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "StoreStats - success\n" ); } else if ( k_EResultInvalidParam == pCallback->m_eResult ) { // Una o più statistiche che abbiamo impostato hanno rotto un vincolo. Sono state ripristinate, // e dovremmo reiterare i valori ora per restare sincronizzati. OutputDebugString( "StoreStats - some failed to validate\n" ); //Fingi di richiamare qui in modo da ricaricare i valori 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 ); } } }

Passo 3: integrazione nel gioco

Di seguito è riportato l'elenco completo dei frammenti di codice che è necessario integrare nel gioco nelle posizioni appropriate.

Definizioni e variabili globali

Di seguito sono elencate le inclusioni che sono necessarie per lavorare con Stats, una matrice delle nostre statistiche specifiche del gioco e un puntatore globale al nostro oggetto helper. Tieni presente che le statistiche corrisponderanno a quelle impostate nella pagina Amministratore su Steamworks.
... #include "steam_api.h" #include "isteamuserstats.h" #include "SteamStats.h" // Matrice delle statistiche che conserverà i dati relativi alle statistiche e al loro stato 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"), }; // Accesso globale all'oggetto Stats CSteamStats* g_SteamStats = NULL; ...

Inizializzazione

La chiamata SteamAPI_Init inizializza Steam e deve essere effettuata prima di qualsiasi altra operazione. Se questa chiamata ha esito positivo, l'oggetto helper viene quindi creato passando la matrice delle statistiche e la sua dimensione.
... // Inizializzare Steam bool bRet = SteamAPI_Init(); // Crea l'oggetto SteamStats se Steam è stato inizializzato correttamente if (bRet) { g_SteamStats = new CSteamStats(g_Stats, 6); } ...

Elaborazione delle richiamate

Per garantire l'elaborazione di tutte le richiamate a Steam, è necessario inserire regolarmente nuovi messaggi. È possibile eseguire questa operazione aggiungendo questa chiamata al ciclo di gioco.
... SteamAPI_RunCallbacks(); ...

Salvataggio delle statistiche

Le statistiche vengono salvate effettuando una singola chiamata a StoreStats().
... if (g_SteamStats) g_SteamStats->StoreStats(); ...

Arresto

La chiamata SteamAPI_Shutdown probabilmente è già presente nel tuo codice. Questa consente l'arresto di Steam e deve essere eseguita prima di uscire dall'applicazione. Infine, eliminiamo l'oggetto helper creato.
... // Arresta Steam SteamAPI_Shutdown(); // Elimina l'oggetto SteamStats if (g_SteamStats) delete g_SteamStats; ...

Passo 4: test e risoluzione dei problemi

Questo esempio di codice fornisce informazioni di debug alla console dedicata che possono aiutare a capire quali chiamate hanno esito positivo e quali negativo. Di seguito sono riportati alcuni messaggi di errore e correzioni comuni:

Impossibile avviare l'applicazione in quanto il file steam_api.dll è mancante. Installare nuovamente l'applicazione per tentare di risolvere il problema.
Assicurarsi che steam_api.dll sia nella stessa cartella dell'eseguibile.

[S_API FAIL] SteamAPI_Init() non riuscita; impossibile individuare un'istanza di esecuzione di Steam o un file steamclient.dll locale.
Probabilmente il client di Steam non è in esecuzione. Avvia Steam ed effettua l'accesso.

[S_API FAIL] SteamAPI_Init() non riuscita; nessun AppID trovato.
Probabilmente il file steam_appid.txt non è nella posizione corretta. Assicurati che il file si trovi nella cartella di origine e che contenga il numero dell'AppID.