In breve
Usa gli achievement come ricompensa per i giocatori che raggiungono certi traguardi o che interagiscono con il tuo gioco in modi particolari.Livello d'integrazione
10 minuti e meno di 10 linee di codice. È necessaria l'integrazione con l'SDK di Steamworks.Introduzione
Gli achievement possono essere usati per incoraggiare i giocatori a interagire con il tuo gioco e raggiunere determinati traguardi. Spesso vengono usati per segnalare il numero di uccisioni, di chilometri percorsi, di forzieri aperti o altre azioni comuni nel tuo gioco. Possono anche aiutare i giocatori a scoprire nuovi modi di giocare al tuo titolo. Quando sbloccati, gli achievement appariranno nell'angolo della finestra di gioco e veranno contrassegnati per il giocatore in una relativa pagina.
Panoramica tecnica
Quella che segue è una breve guida per aiutarti a integrare gli achievement 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 il più semplice possibile. Tieni presente che statistiche e achievement presentano un considerevole grado di sovrapposizione. Pertanto, se desideri integrare entrambi, potrai utilizzare molte delle stesse chiamate alle API.
Passaggio 1: definizione degli achievement del gioco
Gli achievement sono elementi specifici dell'applicazione e vengono impostati dalla pagina
Configurazione achievement nel back-end Amministratore applicazione di Steamworks. Di seguito è illustrato l'elenco degli achievement dall'applicazione demo Spacewar di Steamworks:

Passaggio 2: integrazione degli achievement
Il codice presentato di seguito non dipende dal gioco e può essere aggiunto al tuo titolo se lo ritieni opportuno. La classe è già 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 dati degli achievement ricevuti da Steam e una macro per la creazione di oggetti di questo tipo. Questi dati vengono associati direttamente al contenuto della pagina
Configurazione degli achievement.
#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;
};
A questo punto, definiamo una classe helper che raggruppi tutte le chiamate alle API delle statistiche di Steam e che crei anche tutte le
callback di Steam.
class CSteamAchievements
{
private:
int64 m_iAppID; // AppID attuale
Achievement_t *m_pAchievements; // Dati degli achievement
int m_iNumAchievements; // Numero di achievement
bool m_bInitialized; // Request stats è stato chiamato e ha ricevuto la nuova chiamata?
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 );
};
File di codice
Costruttore
Parametri: il costruttore porta un puntatore su un array di achievement per tutta la lunghezza dell'array. L'ordine di questo array verrà trattato in seguito, quando affronteremo il codice di 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.
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()
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 invia una chiamata a
ISteamUserStats::RequestCurrentStats, ossia una chiamata asincrona a Steam, per richiedere statistiche e achievement 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 CSteamAchievements::RequestStats()
{
// Steam è avviato? In caso contrario, non è possibile ottenere statistiche.
if ( NULL == SteamUserStats() || NULL == SteamUser() )
{
return false;
}
// L'utente ha effettuato l'accesso? In caso contrario, non è possibile ottenere statistiche.
if ( !SteamUser()->BLoggedOn() )
{
return false;
}
// Richiedi statistiche dell'utente.
return SteamUserStats()->RequestCurrentStats();
}
SetAchievement()
Parametri: l'identificatore di stringa dell'achievement che desideri impostare (ad esempio, "ACH_WIN_ONE_GAME").
Restituzioni: una variabile booleana indicante la riuscita o il fallimento della chiamata. Se la chiamata non è riuscita, Steam non è inizializzato oppure la callback non è ancora stata elaborata dalla chiamata iniziale a
RequestStats
. Non è possibile impostare gli achievement fino a quando non viene ricevuta questa callback.
Funzione: questo metodo imposta un dato achievement come completato e invia i risultati a Steam. È possibile impostare un dato achievement più volte in modo da non doversi preoccupare di impostare solo quelli ancora non configurati. Si tratta di una chiamata asincrona che attiverà due callback:
OnUserStatsStored()
e
OnAchievementStored()
.
bool CSteamAchievements::SetAchievement(const char* ID)
{
// Abbiamo ricevuto una nuova chiamata da Steam?
if (m_bInitialized)
{
SteamUserStats()->SetAchievement(ID);
return SteamUserStats()->StoreStats();
}
// Se non l'abbiamo ricevuta, non possiamo ancora impostare gli achievement
return false;
OnUserStatsReceived()
Parametri: non disponibile.
Restituzioni: nessuna.
Funzione: questo metodo consiste di una callback attivata a ogni tentativo di richiesta delle statistiche. Le statistiche e gli achievement vengono richiesti utilizzando
RequestStats()
. Il metodo aggiorna la variabile m_pAchievements del membro per restituire le ultime statistiche e i dati degli achievement ottenuti da Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback )
{
// potremmo ricevere callback per statistiche in arrivo di altri giochi, ignorale
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString("Statistiche e achievement ricevuti da Steam\n");
m_bInitialized = true;
// carica achievement
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()
Parametri: non disponibile.
Restituzioni: nessuna.
Funzione: questo metodo consiste di una callback attivata a ogni tentativo di archiviazione delle statistiche su Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback )
{
// Potremmo ricevere callback per le statistiche in arrivo di altri giochi, ignorale
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString( "Statistiche archiviate per Steam\n" );
}
else
{
char buffer[128];
_snprintf( buffer, 128, "StatsStored - failed, %d\n", pCallback->m_eResult );
OutputDebugString( buffer );
}
}
}
OnAchievementStored()
Parametri: non disponibile.
Restituzioni: nessuna.
Funzione: questo metodo consiste di una callback attivata ogni volta che gli achievement vengono correttamente archiviati su Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback )
{
// Potremmo ricevere callback per le statistiche in arrivo di altri giochi, ignorale
if ( m_iAppID == pCallback->m_nGameID )
{
OutputDebugString( "Achievement archiviato per Steam\n" );
}
}
Passaggio 3: integrazione nel gioco
Di seguito è riportato l'elenco completo dei frammenti di codice che è necessario integrare nelle posizioni corrette nel tuo gioco.
Definizioni e variabili globali
Di seguito elenchiamo le inclusioni da compilare con gli achievement, un enum dei nostri achievement specifici del gioco e un puntatore globale al nostro oggetto helper. Tieni presente che gli achievement corrisponderanno a quelli impostati nella pagina Amministratore su Steamworks.
...
#include "steam_api.h"
// Definizione dei nostri achievement
enum EAchievements
{
ACH_WIN_ONE_GAME = 0,
ACH_WIN_100_GAMES = 1,
ACH_TRAVEL_FAR_ACCUM = 2,
ACH_TRAVEL_FAR_SINGLE = 3,
};
// Matrice degli achievement con dati su di essi e sul loro stato
Achievement_t g_Achievements[] =
{
_ACH_ID( ACH_WIN_ONE_GAME, "Vincitore" ),
_ACH_ID( ACH_WIN_100_GAMES, "Campione" ),
_ACH_ID( ACH_TRAVEL_FAR_ACCUM, "Interstellare" ),
_ACH_ID( ACH_TRAVEL_FAR_SINGLE, "Orbiter" ),
};
// Accesso globale all'oggetto degli achievement
CSteamAchievements* g_SteamAchievements = NULL;
...
Inizializzazione
La chiamata a
SteamAPI_Init inizializza Steam e deve essere effettuata prima di qualsiasi altra operazione. Se questa chiamata va a buon fine, creiamo l'oggetto helper passando nell'array degli achievement per tutta la lunghezza dell'array.
...
// Inizializza Steam
bool bRet = SteamAPI_Init();
// Crea l'oggetto SteamAchievements in caso di inizializzazione corretta di Steam
if (bRet)
{
g_SteamAchievements = new CSteamAchievements(g_Achievements, 4);
}
...
Elaborazione delle callback
Per garantire l'elaborazione di tutte le callback a Steam, è necessario inserire regolarmente nuovi messaggi. È possibile eseguire questa operazione aggiungendo questa chiamata al ciclo di gioco.
...
SteamAPI_RunCallbacks();
...
Attivazione degli achievement
Per l'attivazione di un achievement è sufficiente una singola chiamata che passi attraverso l'identificativo dell'achievement.
...
if (g_SteamAchievements)
g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES");
...
Arresto
La chiamata a
SteamAPI_Shutdown probabilmente è già presente nel tuo codice. Consente l'arresto di Steam e deve essere eseguita prima di uscire dall'applicazione. Infine, eliminiamo l'oggetto helper creato.
...
// Arresta Steam
SteamAPI_Shutdown();
// Cancella l'oggetto SteamAchievements
if (g_SteamAchievements)
delete g_SteamAchievements;
...
Passaggio 4: test e risoluzione dei problemi
Per impostare o eliminare le statistiche o un achievement senza aggiungere codice al tuo gioco, puoi usare la console del client di Steam. Avviala con steam.exe -console, seguito da:
- achievement_clear <appid> <achievement name>
- reset_all_stats <appid>