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 mã nền tảng 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:

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()
và
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>