Resumo
Use conquistas para recompensar jogadores que alcançarem certos marcos ou interagirem com o jogo de formas específicas.Nível de integração
10 minutos e menos de 10 linhas de código. Requer integração com o SDK do Steamworks.Introdução
Conquistas podem ser usadas para encorajar e recompensar interações e marcos de jogadores no jogo. Elas costumam ser usadas para contar a quantidade de vítimas, quilômetros percorridos, tesouros abertos ou outras ações comuns no jogo, mas também servem para ajudar os jogadores a descobrir formas diferentes de jogar o jogo. Quando uma conquista é alcançada, uma notificação é exibida no canto da janela, e ela fica registrada na página de conquistas do jogador.
Visão geral técnica
A seguir, apresentamos um guia passo a passo rápido para integrar conquistas Steam bem básicas no seu aplicativo em menos de 10 minutos e com menos de 10 linhas de código integradas à sua base de código. O
SDK do Steamworks vem com um ótimo aplicativo de exemplo, denominado
Spacewar, que demonstra os diversos recursos do Steam e deve ser a sua referência para ver todos os recursos do Steam em funcionamento. Com a finalidade de ser o mais direto possível, este tutorial detalha apenas as informações encontradas no Spacewar e na API de estatísticas e conquistas necessárias para estatísticas. Lembre-se de que há muita coisa em comum entre estatísticas e conquistas. Logo, se estiver integrando ambas, esteja ciente de que muitas chamadas podem ser consolidadas.
1º passo — Definição das conquistas do jogo
Conquistas são separadas por aplicativo e configuradas na página de
configuração de conquistas no backend de administração de aplicativo do Steamworks. Confira abaixo a lista de conquistas de Spacewar, o aplicativo de exemplo do Steamworks:

2º passo — Encapsulamento do funcionamento de conquistas
O código a seguir independe do jogo e pode ser adicionado ao seu jogo da forma que achar melhor. A classe é completamente funcional, mas pode ser estendida para atender a necessidades adicionais. O código foi extraído dos arquivos de exemplo do Spacewar
StatsAndAchievements.cpp/h
. Strings literais e comentários foram traduzidos para uma melhor compreensão.
Arquivo de cabeçalho
Primeiro definimos uma estrutura para armazenar os dados de conquista recebidos do Steam e um atalho para criar objetos do tipo. Esses dados são mapeados diretamente aos existentes na página de
configuração de conquistas.
#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;
};
Em seguida, definimos uma classe auxiliar que encapsulará todas as chamadas à API de estatísticas do Steam, assim como criará todos os
retornos de chamada do Steam.
class CSteamAchievements
{
private:
int64 m_iAppID; // Nosso AppID atual
Achievement_t *m_pAchievements; // Dados das conquistas
int m_iNumAchievements; // Quantidade de conquistas
bool m_bInitialized; // Chamamos a funcao RequestStats e recebemos o retorno de chamada?
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 );
};
Arquivo de código
Construtor
Parâmetros — O construtor recebe um ponteiro para um vetor de conquistas e o tamanho do mesmo. A formatação do vetor será abordada no código principal do jogo, mais adiante.
Retorna — Nada
O que faz — O construtor inicializa alguns membros e recupera o AppID em execução. Além disso, ele associa os métodos de retorno de chamada para tratar chamadas assíncronas feitas ao Steam. Por último, realiza uma chamada inicial à função
RequestStats()
para recuperar as estatísticas e conquistas do usuário atual.
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()
Parâmetros — Nenhum
Retorna — um bool que representa o sucesso da chamada. Se a chamada falhou, a causa comum é a não inicialização do Steam. Confirme que há uma instância do cliente Steam aberta ao realizar esta chamada e que a função
SteamAPI_Init foi chamada anteriormente.
O que faz — Este método basicamente encapsula uma chamada à função
ISteamUserStats::RequestCurrentStats, que é uma chamada assíncrona ao Steam requisitando as estatísticas e conquistas do usuário atual. Esta chamada deve ser realizada antes de definir qualquer estatística ou conquista. A chamada inicial a este método é realizada no construtor. É possível chamá-la posteriormente, quando quiser, caso deseje verificar se há estatísticas ou conquistas atualizadas.
bool CSteamAchievements::RequestStats()
{
// Steam carregado? Caso contrario, nao podemos recuperar estatisticas.
if ( NULL == SteamUserStats() || NULL == SteamUser() )
{
return false;
}
// Usuario com sessao inciada? Caso contrario, nao podemos recuperar estatisticas.
if ( !SteamUser()->BLoggedOn() )
{
return false;
}
// Recuperar estatisticas do usuario.
return SteamUserStats()->RequestCurrentStats();
}
SetAchievement()
Parâmetros — A string que identifica a conquista a ser definida (ex.: "CONQ_VENCER_UMA_PARTIDA")
Retorna — um bool que representa o sucesso da chamada. Se a chamada falhou, então o Steam não está inicializado ou o processamento do retorno da primeira chamada à função
RequestStats
ainda não foi realizado. Não é possível definir conquistas até o recebimento do retorno de chamada.
O que faz — Este método define uma dada conquista como alcançada e envia os resultados ao Steam. É possível definir uma dada conquista várias vezes, então não se preocupe em definir conquistas já alcançadas. Esta é uma chamada assíncrona que disparará dois retornos de chamada:
OnUserStatsStored()
e
OnAchievementStored()
.
bool CSteamAchievements::SetAchievement(const char* ID)
{
// Ja recebemos o retorno de chamada do Steam?
if (m_bInitialized)
{
SteamUserStats()->SetAchievement(ID);
return SteamUserStats()->StoreStats();
}
// Caso contrario, ainda nao podemos definir conquistas
return false;
}
OnUserStatsReceived()
Parâmetros — Nenhum
Retorna — Nada
O que faz — Este método é um retorno de chamada que é chamado toda vez que você tentar recuperar estatísticas. Estatísticas e conquistas são recuperadas por meio da função
RequestStats()
. O método atualiza a variável-membro m_pAchievements para apontar aos dados de estatísticas e conquistas mais recentes do Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback )
{
// Podemos receber retornos de chamada de estatisticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString("Recebeu estatisticas e conquistas do Steam\\n");
m_bInitialized = true;
// Carregar conquistas
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 - falhou, %d\\n", pCallback->m_eResult );
OutputDebugString( buffer );
}
}
}
OnUserStatsStored()
Parâmetros — Nenhum
Retorna — Nada
O que faz — Este método é um retorno de chamada disparado sempre que o jogo tentar armazenar estatísticas no Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback )
{
// Podemos receber retornos de chamada de estatisticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString( "Estatisticas do Steam armazenadas\\n" );
}
else
{
char buffer[128];
_snprintf( buffer, 128, "StatsStored - falha, %d\\n", pCallback->m_eResult );
OutputDebugString( buffer );
}
}
}
OnAchievementStored()
Parâmetros — Nenhum
Retorna — Nada
O que faz — Este método é um retorno de chamada, disparado sempre que conquistas forem armazenadas com sucesso no Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback )
{
// Podemos receber retornos de chamada de estatisticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
OutputDebugString( "Conquista do Steam armazenada\\n" );
}
}
3º passo — Integração com o jogo
Segue uma listagem completa de trechos de códigos que precisam ser integrados ao jogo nos locais apropriados.
Defines e globais
A seguir, está uma lista de
include
s necessários para a construção com conquistas, uma enumeração com as conquistas específicas do jogo e um ponteiro global ao nosso objeto auxiliar. Observe que as conquistas devem ter o mesmo nome que o "Nome na API" na página de administração do Steamworks.
...
#include "steam_api.h"
// Definicao das conquistas
enum EAchievements
{
ACH_WIN_ONE_GAME = 0,
ACH_WIN_100_GAMES = 1,
ACH_TRAVEL_FAR_ACCUM = 2,
ACH_TRAVEL_FAR_SINGLE = 3,
};
// Vetor de conquistas, para armazenar dados e o estado das conquistas
Achievement_t g_Achievements[] =
{
_ACH_ID( ACH_WIN_ONE_GAME, "Vencedor" ),
_ACH_ID( ACH_WIN_100_GAMES, "Campeao" ),
_ACH_ID( ACH_TRAVEL_FAR_ACCUM, "Interestelar" ),
_ACH_ID( ACH_TRAVEL_FAR_SINGLE, "Orbitador" ),
};
// Acesso global ao objeto auxiliar de conquistas
CSteamAchievements*g_SteamAchievements = NULL;
...
Inicialização
Esta chamada à função
SteamAPI_Init inicializará a base do Steam e deve ser chamada antes de qualquer outra função da API. Se for bem-sucedida, então criamos o objeto auxiliar passando o vetor de conquistas e o tamanho do vetor.
...
// Inicializacao do Steam
bool bRet = SteamAPI_Init();
// Criacao do objeto SteamAchievements se o Steam foi inicializado com sucesso
if (bRet)
{
g_SteamAchievements = new CSteamAchievements(g_Achievements, 4);
}
...
Processamento de retornos de chamada
Para garantir o processamento de todos os retornos de chamada do Steam, precisamos sempre verificar se há novas mensagens. Para tal, adicionamos a chamada a seguir ao ciclo de vida do jogo.
...
SteamAPI_RunCallbacks();
...
Disparo de conquistas
Disparar uma conquista é tão simples quanto chamar uma função com o identificador da mesma.
...
if (g_SteamAchievements)
g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES");
...
Encerramento
O seu código já deve ter uma chamada à função
SteamAPI_Shutdown. Ela encerra o Steam e deve ser chamada antes do encerramento do aplicativo. Por último, excluímos o objeto auxiliar criado.
...
// Encerramento do Steam
SteamAPI_Shutdown();
// Exclusao do objeto SteamAchievements
if (g_SteamAchievements)
delete g_SteamAchievements;
...
4º passo — Testes e solução de problemas
Para alterar ou remover o valor de uma estatística ou conquista sem adicionar código ao jogo, use o console do cliente Steam. Inicie o cliente Steam por meio do comando "steam.exe -console" e então execute os seguintes comandos no console:
- achievement_clear <AppID> <nome da conquista> (para definir a conquista como não alcançada)
- reset_all_stats <AppID> (para zerar todas as estatísticas)