Tài liệu Steamworks
Từng bước một: Thành tựu
Tóm gọn
Dùng tính năng thành tựu để tưởng thưởng người chơi khi họ đạt đến cột mốc quan trọng hoặc tương tác với trò chơi theo cách nhất định.
Mức độ tích hợp
10 phút và dưới 10 dòng mã. Cần phải tích hợp Steamworks SDK.

Giới thiệu

Thành tựu có thể dùng làm lời khích lệ lẫn phần thưởng cho sự tương tác của người chơi và cột mốc họ đạt được. Thường được dùng để đánh dấu số mạng giết, số dặm đã lái, số hòm đã mở hoặc các hành động phổ biến khác trong trò chơi. Đồng thời, thành tựu được dùng để dẫn dắt người chơi khám phá nhiều phương thức trải nghiệm trò chơi khác nhau. Khi mở khóa, thành tựu sẽ hiện ở góc cửa sổ người chơi và được đánh dấu trên trang thành tựu của người chơi đó.

Tổng quan về mặt kỹ thuật

Sau đây là hướng dẫn nhanh từng bước một để tích hợp thành tựu Steam vô cùng cơ bản vào ứng dụng của bạn trong vòng 10 phút và dưới 10 dòng mã tích hợp vào code base của bạn. Steamworks SDK có một ứng dụng ví dụ tuyệt vời mang tên Spacewar, phô diễn toàn bộ tính năng Steam và nên là chặng dừng đầu tiên để ngắm nghía cách mọi tính năng Steam vận hành. Hướng dẫn này cô đọng thông tin tìm thấy trong Spacewar và API Thành tựu và Thông số, chỉ cung cấp những thông tin cần thiết cho Thông số Steam để đảm bảo tài liệu này súc tích nhất có thể. Xin lưu ý rằng có khá nhiều sự trùng lặp giữa thông số và thành tựu, vì thế nếu bạn tích hợp cả hai nên lưu ý rằng nhiều lệnh gọi có thể được hợp nhất.

Bước 1 - Xác định thành tựu cho trò chơi của bạn

Thành tựu đi theo từng ứng dụng và được cài đặt trên trang Thiết lập thành tựu trong backend của Steamworks App Admin. Sau đây là danh sách thành tựu từ ứng dụng ví dụ Spacewar trên Steamworks:

spacewar_achievement_examplescreenshot.jpg

Bước 2 - Tóm lược thành tựu

Mã sau tồn tại độc lập với trò chơi và có thể thêm vào trò chơi nếu thích hợp. Class này vốn đã đầy đủ chức năng, nhưng cũng có thể dễ dàng mở rộng để đáp ứng nhiều nhu cầu hơn. Tất cả mã này được lấy trực tiếp từ file ví dụ Spacewar StatsAndAchievements.cpp/h.

File Header

Đầu tiên, chúng tôi xác định cấu trúc để giữ dữ liệu thành tựu nhận từ Steam và cung cấp macro để tạo các object thuộc loại đó. Dữ liệu này phản ánh trực tiếp nội dung tìm thấy trên trang Thiết lập thành tựu.
#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; };

Tiếp theo, chúng tôi xác định class helper sẽ bọc mọi lệnh gọi API thông số Steam cũng như tạo tất cả lệnh gọi lại trên Steam.
class CSteamAchievements { private: int64 m_iAppID; // AppID hiện tại Achievement_t *m_pAchievements; // Dữ liệu thành tựu int m_iNumAchievements; // Số thành tựu bool m_bInitialized; // Đã gọi RequestStats và nhận lệnh gọi lại? 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 mã

Constructor

Tham số - Constructor đưa giá trị con trỏ qua một mảng thành tựu cùng với độ dài của mảng. Định dạng của mảng đó sẽ được bàn đến sau trong phần mã trò chơi chính.
Trả về - N/A
Chức năng Constructor khởi chạy một số thành viên cùng lúc lấy AppID mà chúng ta hiện đang dùng. Thêm vào đó, nó cũng kết nối phương thức gọi lại để xử lý các cuộc gọi không đồng bộ đến Steam. Cuối cùng, lệnh gọi đầu tiên được thực hiện tới RequestStats() để lấy số liệu thống kê và thành tựu của người dùng hiện tại.
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()

Tham số - Không
Trả về - dữ liệu bool cho biết lệnh gọi có thành công hay không. Nếu lệnh gọi thất bại, rất có thể Steam chưa được khởi chạy. Hãy nhớ mở phần mềm Steam khi thực hiện lệnh gọi và gọi SteamAPI_Init trước.
Chức năng - Về cơ bản, phương thức này bọc lệnh gọi tới ISteamUserStats::RequestCurrentStats, đây là cuộc gọi không đồng bộ tới Steam yêu cầu cung cấp thông số và thành tựu của người dùng hiện tại. Lệnh gọi này cần được thực hiện trước khi đặt bất kỳ thông số hoặc thành tựu nào. Lệnh gọi đầu tới phương thức này được thực hiện trong constructor. Bạn có thể gọi lại bất kỳ lúc nào sau đó nếu muốn kiểm tra thông số cập nhật hoặc thành tựu.
bool CSteamStats::RequestStats() { // Đã tải Steam chưa? Nếu chưa, thì không thể lấy thông số. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Người dùng đã đăng nhập chưa? Nếu chưa, thì không thể lấy thông số. if ( !SteamUser()->BLoggedOn() ) { return false; } // Yêu cầu thông số người dùng. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

Tham số - Số nhận dạng chuỗi của thành tựu bạn muốn tìm (vd: "ACH_WIN_ONE_GAME")
Trả về - dữ liệu bool cho biết lệnh gọi có thành công hay không. Nếu không thành công, hoặc do Steam chưa khởi chạy hoặc bạn chưa xử lý lệnh gọi lại từ cuộc gọi ban đầu đến RequestStats. Bạn không thể thiết lập thành tựu đến khi lệnh gọi được nhận.
Chức năng - Phương pháp này đặt thành tựu chỉ định sang trạng thái đã đạt được, và gửi kết quả đến Steam. Có thể thiết lập cùng một thành tựu nhiều lần, vì vậy bạn không phải lo lắng về việc chỉ áp dụng với những thành tựu chưa thiết lập. Đây là cuộc gọi không đồng bộ và sẽ kích hoạt hai lệnh gọi lại: OnUserStatsStored()OnAchievementStored().
bool CSteamAchievements::SetAchievement(const char* ID) { // Đã nhận lệnh gọi lại từ Steam chưa? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // Nếu chưa, thì chưa thể thiết lập thành tựu return false; }

OnUserStatsReceived()

Tham số - N/A
Trả về - Trống
Chức năng - Phương pháp này là một lệnh gọi lại, được kích hoạt bất cứ khi nào cố gắng yêu cầu thông số. Thông số và thành tựu được yêu cầu bằng RequestStats(). Phương thức này cập nhật biến thành viên m_pAchievements để phản ánh dữ liệu thông số và thành tựu mới nhất được trả về từ Steam.
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // có thể nhận lệnh gọi lại về thông số khác của trò chơi, hãy phớt lờ if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // nạp thành tựu 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()

Tham số - N/A
Trả về - Trống
Chức năng - Phương pháp này là một lệnh gọi lại, được kích hoạt bất cứ khi nào cố gắng lưu trữ thông số trên Steam.
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // có thể nhận lệnh gọi lại về thông số khác của trò chơi, hãy phớt lờ 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()

Tham số - N/A
Trả về - Trống
Chức năng - Phương pháp này là một lệnh gọi lại, được kích hoạt bất cứ khi nào thành tựu được lưu trữ thành công trên Steam.
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // có thể nhận lệnh gọi lại về thông số khác của trò chơi, hãy phớt lờ if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

Bước 3 - Tích hợp vào trò chơi

Đây là danh sách đầy đủ các đoạn mã bạn cần để tích hợp vào trò chơi tại những vị trí thích hợp.

Các Define và Global

Dưới đây là danh sách thành phần bao gồm cần thiết để xây dựng với thành tựu, một enum các thành tựu cụ thể cho trò chơi và global pointer tới object helper. Xin lưu ý rằng thành tựu trùng với thông tin trên trang quản trị Steamworks.
... #include "steam_api.h" // Xác định thành tựu enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Mảng thành tựu sẽ giữ dữ liệu về nội dung và trạng thái của chúng 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" ), }; // Truy cập chung đến đối tượng thành tựu CSteamAchievements* g_SteamAchievements = NULL; ...

Khởi chạy

Lệnh gọi tới SteamAPI_Init khởi chạy toàn bộ Steam và phải được thực hiện trước hết. Nếu lệnh đó thành công, hãy tạo object helper để vượt qua mảng thành tựu cùng với kích thước mảng.
... // Khởi chạy Steam bool bRet = SteamAPI_Init(); // Tạo đối tượng SteamAchievements nếu Steam khởi chạy thành công if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

Xử lý lệnh gọi lại

Để đảm bảo xử lý tất cả lệnh gọi lại trên Steam, ta cần thường xuyên bơm tin nhắn mới. Thực hiện bằng cách thêm lệnh gọi vào vòng lặp trò chơi.
... SteamAPI_RunCallbacks(); ...

Kích hoạt thành tựu

Kích hoạt thành tựu chỉ đơn giản như thực hiện cuộc gọi và chuyển mã nhận dạng thành tựu.
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

Tắt

Lệnh gọi tới SteamAPI_Shutdown có lẽ đã tồn tại trong mã của bạn. Nó tắt Steam và phải được gọi trước khi thoát ứng dụng. Cuối cùng, xóa object helper đã tạo.
... // Tắt Steam SteamAPI_Shutdown(); // Xóa object SteamAchievements if (g_SteamAchievements) delete g_SteamAchievements; ...

Bước 4 - Thử nghiệm và xử lý sự cố


Để thiết lập hoặc xóa số liệu thống kê hoặc thành tựu mà không cần thêm mã vào trò chơi, bạn có thể sử dụng console phần mềm Steam. Chạy với steam.exe -console, sau đó:
  • achievement_clear <appid> <achievement name>
  • reset_all_stats <appid>