Документація Steamworks
Крок за кроком: досягнення
Коротко
Використовуйте досягнення, щоби нагородити гравців за успіх у вашій грі чи за виконання певних дій у ній.
Рівень інтеграції
10 хвилин і менше ніж 10 рядків коду. Інтеграція Steamworks SDK обов’язкова.

Вступ

Досягнення можна використовувати як спосіб заохотити чи нагородити гравця за виконання певних цілей або особливу взаємодію у вашій грі. Їх часто використовують, щоби відмітити певну кількість убивств, пройдених кілометрів, відімкнених скринь чи інших типових дій у грі. Окрім того, ви можете показати гравцям інші способи взаємодії з грою. Гравці бачать спливне сповіщення про розблоковане досягнення в грі й потім можуть поглянути на нього на сторінці досягнень.

Технічний огляд

Нижче подається покроковий посібник з інтеграції простих досягнень Steam до вашого застосунку, що займе менш як 10 хвилин і менше ніж 10 додаткових рядків коду в основному коді гри. У розділі про SDK Steamworks подано чудовий приклад застосунку, що називається Spacewar. У ньому показано весь діапазон функцій Steam, тож рекомендуємо із ним ознайомитися. Тут інформація зі Spacewar та API статистик і досягнень відфільтрована до необхідної для розуміння принципу роботи цих функцій. Зверніть увагу, що статистики й досягнення сильно перетинаються, тож якщо ви інтегруєте обидві функції, врахуйте, що багато викликів можна об’єднати.

Крок 1: визначення досягнень для вашої гри

Досягнення визначаються для конкретного застосунку й налаштовуються на сторінці конфігурації досягнень в адмініструванні застосунку в Steamworks. Далі подано список досягнень зі Spacewar (застосунок-приклад Steamworks):

spacewar_achievement_examplescreenshot.jpg

Крок 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>