Steamworks 文献库
逐步指导:排行榜

简介

本文是一份快速指南,逐步指导您在 10 分钟内,将非常基础的 Steam 排行榜集成到您的应用程序中。 Steamworks SDK 中有一个名为 Spacewar 的绝佳应用程序实例,完整展示了所有 Steam 功能。若要全面了解 Steam 所有使用中的功能,请首先访问此处。 本教程将 Spacewar 中的信息和 ISteamUserStats 中的排行榜 API 简化至 Steam 排行榜所必需的信息,尽量简单明了。

第一步:定义游戏排行榜

排行榜专属于各个应用程序,在 Steamworks 合作伙伴站点中的排行榜配置页面进行设置。

要定义排行榜,必须填写以下字段:
  • 名称 - 内部开发可用的合理名称。
  • 社区名称 - 如要在社区中心显示此排行榜,在此设置公开显示的名称。 如未输入名称,则该排行榜将不会显示。
  • 排序方式 - 设置排行榜的排列顺序。 按名次的排行榜请使用升序。 高分榜请使用降序。
  • 显示类型 - 决定排行榜显示的数据类型。 可选择数字、秒或毫秒。
  • 写入 - 如设置为“可信任”,客户端便无法设置排行榜分数,只能通过 SetLeaderboardScore WebAPI 设置。 默认为 false。
  • 读取 - 如选择为“好友”,游戏便只能读取该用户好友的排行榜分数,但 WebAPI 可始终读取所有分数。 默认为 false。

spacewar_leaderboards

第二步:封装排行榜工作

以下代码独立于游戏之外,您可将其放入游戏中合适的地方。 此类代码已可正常运行,但也可轻易扩展,满足更多需求。 所有代码均直接来自于 Spacewar 的示例文件 Leaderboards.cpp/h

头文件

定义一个帮助程序类,可用于包装所有 Steam 排行榜 API 调用及创建所有 Steam 调用结果处理程序。
class CSteamLeaderboards { private: SteamLeaderboard_t m_CurrentLeaderboard; // 排行榜句柄 public: int m_nLeaderboardEntries; // 有多少个条目? LeaderboardEntry_t m_leaderboardEntries[10]; // 条目 CSteamLeaderboards(); ~CSteamLeaderboards(){}; void FindLeaderboard( const char *pchLeaderboardName ); bool UploadScore( int score ); bool DownloadScores(); void OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure); CCallResult m_callResultFindLeaderboard; void OnUploadScore( LeaderboardScoreUploaded_t *pResult, bool bIOFailure); CCallResult m_callResultUploadScore; void OnDownloadScore( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure); CCallResult m_callResultDownloadScore; };

代码文件

构造函数

参数 - 无
返回 – 不适用
作用 - 此构造函数只初始化成员变量。
CSteamLeaderboards::CSteamLeaderboards() : m_CurrentLeaderboard( NULL ), m_nLeaderboardEntries( 0 ) { }

FindLeaderboard()

参数 - 您要设置的排行榜字符串标识符(如 "Feet Traveled")
返回 - 无
作用 - 包装对 ISteamUserStats::FindLeaderboard 的调用。该调用为对 Steam 的异步调用,请求给定排行榜的句柄。 必须执行该调用后才能检索或设置排行榜条目。 该方法也可设置要使用的调用返回方法。
void CSteamLeaderboards::FindLeaderboard( const char *pchLeaderboardName ) { m_CurrentLeaderboard = NULL; SteamAPICall_t hSteamAPICall = SteamUserStats()->FindLeaderboard(pchLeaderboardName); m_callResultFindLeaderboard.Set(hSteamAPICall, this, &CSteamLeaderboards::OnFindLeaderboard); }

OnFindLeaderboard()

参数 – 不适用
返回 - 无
作用 - 尝试在 Steam 上寻找排行榜时调用的回调方法。 如找到请求的排行榜,则设置该排行榜句柄为当前排行榜。
void CSteamLeaderboards::OnFindLeaderboard( LeaderboardFindResult_t *pCallback, bool bIOFailure ) { // 检查调用期间是否遇到错误 if ( !pCallback->m_bLeaderboardFound || bIOFailure ) { OutputDebugString( "Leaderboard could not be found\n" ); return; } m_CurrentLeaderboard = pCallback->m_hSteamLeaderboard; }

UploadScore()

参数 - 一个 int32,表示将存入当前排行榜的值。
返回 - 如果尚未选择排行榜,返回 False ,否则返回 True。
作用 - 包装对 ISteamUserStats::UploadLeaderboardScore 的调用。该调用为对 Steam 的异步调用,将当前用户分数上传至当前已选排行榜。 该方法也可设置要使用的调用返回方法。 在使用 FindLeaderboard() 选择排行榜后方可执行此调用。
bool CSteamLeaderboards::UploadScore( int score ) { if (!m_CurrentLeaderboard) return false; SteamAPICall_t hSteamAPICall = SteamUserStats()->UploadLeaderboardScore( m_CurrentLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, score, NULL, 0 ); m_callResultUploadScore.Set(hSteamAPICall, this, &CSteamLeaderboards::OnUploadScore); return true; }

OnUploadScore()

参数 – 不适用
返回 - 无
作用 - 尝试将分数上传至 Steam 排行榜时调用的回调方法。
void CSteamLeaderboards::OnUploadScore(LeaderboardScoreUploaded_t *pCallback, bool bIOFailure) { if ( !pCallback->m_bSuccess || bIOFailure ) { OutputDebugString( "Score could not be uploaded to Steam\n" ); } }

DownloadScores()

参数 – 不适用
返回 - 如果尚未选择排行榜,返回 False ,否则返回 True。
作用 - 包装对 ISteamUserStats::DownloadLeaderboardEntries 的调用。该调用为指向 Steam 的异步调用,从当前已选排行榜下载条目。 下方示例中我们下载十个条目:当前用户之前四条、当前用户及当前用户之后五条。 此调用可进行修改,以从排行榜任意位置返回任意数量的条目。 该方法也可设置要使用的调用返回方法。 在使用 FindLeaderboard() 选择排行榜后方可执行此调用。
bool CSteamLeaderboards::DownloadScores() { if (!m_CurrentLeaderboard) return false; // 加载当前用户的特定排行榜数据 SteamAPICall_t hSteamAPICall = SteamUserStats()->DownloadLeaderboardEntries( m_CurrentLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -4, 5); m_callResultDownloadScore.Set(hSteamAPICall, this, &CSteamLeaderboards::OnDownloadScore); return true; }

OnDownloadScore()

参数 – 不适用
返回 - 无
作用 - 尝试从 Steam 排行榜下载条目时调用的回调方法。 成功下载数据后,将数据复制至条目数组中。 已下载条目数量存储于 m_nLeaderboardEntries
void CSteamLeaderboards::OnDownloadScore(LeaderboardScoresDownloaded_t *pCallback, bool bIOFailure) { if (!bIOFailure) { m_nLeaderboardEntries = min(pCallback->m_cEntryCount, 10); for (int index = 0; index < m_nLeaderboardEntries; index++) { SteamUserStats()->GetDownloadedLeaderboardEntry( pCallback->m_hSteamLeaderboardEntries,index,&m_leaderboardEntries[index],NULL,0); } } }

第三步:与游戏集成

以下是您需要整合至游戏中适当位置的代码段的完整列表。

定义和全局

以下是建立排行榜所需的 include 列表及指向帮助程序对象的全局指针。
... #include "steam_api.h" #include "SteamLeaderboards.h" // 排行榜对象的全局访问 CSteamLeaderboards* g_SteamLeaderboards = NULL; ...

初始化

调用 SteamAPI_Init 可以全面初始化 Steam,且必须早于任何其他调用。 如果调用成功,即可创建帮助程序对象。
... // 初始化 Steam bool bRet = SteamAPI_Init(); // 如果 Steam 初始化成功,则创建 SteamLeaderboards 对象 if (bRet) { g_SteamLeaderboards = new CSteamLeaderboards(); } ...

回调处理

为了确保处理到所有 Steam 回调,我们需要经常抽取新的信息。 将此调用加入游戏循环即可达到效果。
... SteamAPI_RunCallbacks(); ...

关闭

SteamAPI_Shutdown 的调用可能已存在于您的代码中。 此调用将关闭 Steam,且必须在应用程序退出之前调用。 最后我们需要删除先前建立的帮助程序对象。
... // 关闭 Steam SteamAPI_Shutdown(); // 删除 SteamLeaderboards 对象 if (g_SteamLeaderboards) delete g_SteamLeaderboards; ...

第四步:测试和故障排除

此示例代码可向调试控制台输出调试信息,帮您了解成功和失败的调用信息。 以下是一些典型的失败信息和修复方法:

This application has failed to start because steam_api.dll was not found. Re-installing the application may fix this problem.(由于未找到 steam_api.dll,此应用程序启动失败。重新安装应用程序也许能修复问题。)
请确定 steam_api.dll 与可执行文件处于同一个目录。

[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a localsteamclient.dll ([S_API FAIL] SteamAPI_Init()失败;未找到运行的 Steam 实例或本地 steamclient.dll)
很可能是因为您的 Steam 客户端没有在运行。 启动 Steam 并登录。

[S_API FAIL] SteamAPI_Init() failed; no appID found.([S_API FAIL] SteamAPI_Init() 失败;未找到 appID。)
很可能是因为您的 steam_appid.txt 文件不在正确的位置上。 将其放入您的源文件夹,并确保其中包含您的 appID 号码。