Коротко
Використовуйте досягнення, щоби нагородити гравців за успіх у вашій грі чи за виконання певних дій у ній.Рівень інтеграції
10 хвилин і менше ніж 10 рядків коду. Інтеграція Steamworks SDK обов’язкова.Вступ
Досягнення можна використовувати як спосіб заохотити чи нагородити гравця за виконання певних цілей або особливу взаємодію у вашій грі. Їх часто використовують, щоби відмітити певну кількість убивств, пройдених кілометрів, відімкнених скринь чи інших типових дій у грі. Окрім того, ви можете показати гравцям інші способи взаємодії з грою. Гравці бачать спливне сповіщення про розблоковане досягнення в грі й потім можуть поглянути на нього на сторінці досягнень.
Технічний огляд
Нижче подається покроковий посібник з інтеграції простих досягнень Steam до вашого застосунку, що займе менш як 10 хвилин і менше ніж 10 додаткових рядків коду в основному коді гри. У розділі про
SDK Steamworks подано чудовий приклад застосунку, що називається
Spacewar. У ньому показано весь діапазон функцій Steam, тож рекомендуємо із ним ознайомитися. Тут інформація зі Spacewar та API статистик і досягнень відфільтрована до необхідної для розуміння принципу роботи цих функцій. Зверніть увагу, що статистики й досягнення сильно перетинаються, тож якщо ви інтегруєте обидві функції, врахуйте, що багато викликів можна об’єднати.
Крок 1: визначення досягнень для вашої гри
Досягнення визначаються для конкретного застосунку й налаштовуються на сторінці
конфігурації досягнень в адмініструванні застосунку в Steamworks. Далі подано список досягнень зі Spacewar (застосунок-приклад Steamworks):

Крок 2: інкапсуляція досягнень
Код нижче працює для всіх ігор, і ви можете додати його у свою гру. Клас повністю функціональний, але його можна легко розширити, якщо потрібно. Увесь цей код взятий безпосередньо з файлу
StatsAndAchievements.cpp/h
зі Spacewar.
Заголовний файл
Спершу ми визначаємо структуру, що призначається для збереження отриманих від Steam даних, і задаємо макрос для створення об’єктів цього типу. Ці дані відсилаються безпосередньо до того, що знаходиться на сторінці
налаштування досягнень.
#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;
};
Далі ми визначаємо допоміжний клас, який охопить всі виклики до API статистик Steam, а також створення всіх
зворотних викликів Steam.
class CSteamAchievements
{
private:
int64 m_iAppID; // Наш поточний AppID
Achievement_t *m_pAchievements; // Дані досягнень
int m_iNumAchievements; // Кількість досягнень
bool m_bInitialized; // Чи ми викликали запит на досягнення й отримували зворотний виклик?
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 );
};
Програмний код
Конструктор
Параметри — конструктор приймає вказівник на масив досягнень і довжину масиву. Форматування цього масиву буде показано пізніше в основному ігровому коді.
Повернені значення — немає даних.
Що робить — конструктор ініціалізує кілька членів, а також приймає AppID запущеного застосунку. Окрім цього, він пов’язує методи зворотних викликів для обробки асинхронних викликів до Steam. Зрештою, він виконує початковий виклик
RequestStats()
для отримання статистик і досягнень поточного користувача.
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()
Параметри — немає.
Повернені значення — логічний тип даних (bool), що показує, чи вдався виклик. Якщо виклик не вдався, то, імовірно, Steam не ініціалізовано. Перед викликом слід переконатися, що клієнт Steam відкрито, а до цього була викликана функція
SteamAPI_Init.
Що робить — цей метод по суті здійснює виклик
ISteamUserStats::RequestCurrentStats, що є асинхронним запитом статистик і досягнень поточного користувача. Цей виклик слід виконати перед установленням статистик і досягнень. Початковий виклик цього метода відбувається в конструкторі. Його можна викликати заново будь-коли, якщо потрібно дізнатися оновлені статистики й досягнення.
bool CSteamAchievements::RequestStats()
{
// Чи завантажено Steam? Якщо ні, то статистики отримати неможливо.
if ( NULL == SteamUserStats() || NULL == SteamUser() )
{
return false;
}
// Чи увійшов користувач? Якщо ні, ми не можемо отримати статистики.
if ( !SteamUser()->BLoggedOn() )
{
return false;
}
// Запит користувацьких статистик.
return SteamUserStats()->RequestCurrentStats();
}
SetAchievement()
Параметри — текстовий ідентифікатор досягнення, яке необхідно встановити (наприклад, ACH_WIN_ONE_GAME).
Повернені значення — логічний тип даних (bool), що показує, чи вдався виклик. Якщо виклик не вдався, то або не ініціалізовано Steam, або ще не оброблено зворотний виклик початкового виклику
RequestStats
. Ви не зможете встановити жодне досягнення, доки не буде отримано цей зворотний виклик.
Що робить — цей метод установлює обране досягнення як виконане й надсилає результати в Steam. Досягнення можна встановити кілька разів, тож можна не турбуватися про те, щоби встановлювати лише ті досягнення, які ще не встановлені. Це асинхронний виклик, на який буде отримано два зворотних виклики:
OnUserStatsStored()
та
OnAchievementStored()
.
bool CSteamAchievements::SetAchievement(const char* ID)
{
// Ми ще не отримували зворотний виклик від Steam?
if (m_bInitialized)
{
SteamUserStats()->SetAchievement(ID);
return SteamUserStats()->StoreStats();
}
// Якщо ні, то встановити досягнення ще не можна
return false;
}
OnUserStatsReceived()
Параметри — немає даних.
Повернені значення — немає.
Що робить — цей метод є зворотним викликом, який викликається під час кожної спроби надіслати запит статистики. Статистики й досягнення запитуються за допомогою
RequestStats()
. Метод оновлює змінну-член m_pAchievements для показу останніх даних про статистики й досягнення, що повертаються зі Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback )
{
// ми можемо отримати зворотні виклики для статистики інших ігор, ігноруємо їх
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString("Received stats and achievements from Steam\n");
m_bInitialized = true;
// Завантаження досягнень
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()
Параметри — немає даних.
Повернені значення — немає.
Що робить — цей метод є зворотним викликом, який викликається під час кожної спроби зберегти статистику в Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback )
{
// ми можемо отримати зворотні виклики для статистики інших ігор, ігноруємо їх
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()
Параметри — немає даних.
Повернені значення — немає.
Що робить — цей метод є зворотним викликом, який викликається щоразу, коли досягнення успішно збережені в Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback )
{
// ми можемо отримати зворотні виклики для статистики інших ігор, ігноруємо їх
if ( m_iAppID == pCallback->m_nGameID )
{
OutputDebugString( "Stored Achievement for Steam\n" );
}
}
Крок 3: інтеграція у вашу гру
Далі подано повний перелік фрагментів коду, які потрібно інтегрувати у вашу гру у відповідних місцях.
Визначення і глобальні змінні
Нижче наведено перелік включень, які необхідні для об’єкта Achievements, перерахування з досягненнями цієї гри та глобальний вказівник на допоміжний об’єкт. Майте на увазі, що досягнення збігаються з тими, що вказані на сторінці адміністрування в Steamworks.
…
#include "steam_api.h"
// Визначення наших досягнень
enum EAchievements
{
ACH_WIN_ONE_GAME = 0,
ACH_WIN_100_GAMES = 1,
ACH_TRAVEL_FAR_ACCUM = 2,
ACH_TRAVEL_FAR_SINGLE = 3,
};
// Масив досягнень, який зберігає дані про досягнення та їхній стан
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" ),
};
// Глобальний доступ до об’єкту досягнень
CSteamAchievements* g_SteamAchievements = NULL;
…
Ініціалізація
Виклик
SteamAPI_Init ініціалізує Steam і повинен бути виконаний у найпершу чергу. Якщо виклик удався, то створюється допоміжний об’єкт, котрому передається масив досягнень і розмір масиву.
…
// Ініціалізація Steam
bool bRet = SteamAPI_Init();
// Створення об’єкту SteamAchievements, якщо Steam успішно ініціалізовано
if (bRet)
{
g_SteamAchievements = new CSteamAchievements(g_Achievements, 4);
}
…
Обробка зворотних викликів
Для обробки всіх зворотних викликів Steam потрібно регулярно перевіряти наявність нових повідомлень. Для цього в ігровий цикл додається наведений нижче виклик.
…
SteamAPI_RunCallbacks();
…
Активація досягнень
Активація досягнень проста — це один виклик, що передає ідентифікатор досягнення.
…
if (g_SteamAchievements)
g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES");
…
Вимкнення інтерфейсу SteamAPI
Виклик
SteamAPI_Shutdown, котрий закриває Steam, імовірно, вже є у вашому коді. Його слід надіслати до того, як закриється застосунок. Наприкінці видаляється створений раніше допоміжний об’єкт.
…
// Вимкнення Steam
SteamAPI_Shutdown();
// Видалення об’єкту SteamAchievements
if (g_SteamAchievements)
delete g_SteamAchievements;
…
Крок 4: тестування й вирішення проблем
Для встановлення чи очищення досягнення без додавання коду у вашу гру можна використовувати клієнтську консоль Steam. Запустіть steam.exe з параметром -console, а потім:
- achievement_clear <appid> <achievement name>
- reset_all_stats <appid>