无所属单位

主页 文献与帮助
Steamworks 文献库
统计与成就

原文内容有作更新

此页原文在翻译版发布之后作了更新。
点击此处查看此页更新后的英文版本。

概览

Steam 统计和成就为您的游戏提供了一种简便的方式,持续并动态追踪用户的成就与统计数据。 用户的数据将与 Steam 帐户相关联,而每项成就与统计数据皆可依固定的格式显示于用户的 Steam 社区个人资料之中。

有什么好处

除了向您的游戏玩家提供高价值的奖励之外,成就也很适合用于鼓励和奖励团队合作或玩家互动、加深游戏目标的层次,并且玩家会因在游戏上花更多时间得到奖励。

统计数据则记录了许多精细的信息,像是游戏时间、使用的能量增强物品的数量等等。 您可将它们仅用于记录内部游戏数据,以便从多台电脑收集多重会话的游戏统计数据,为用户授予成就。

实现概览

定义游戏的统计与成就

成就专属于各个应用程序,必须使用 Steamworks 合作伙伴站点的应用管理页面进行设置。
您可直接从 此处 前往统计配置页面,或从 此处 前往成就配置页面。

可储存的统计类型有三种:
  • INT - 32 位(带正负号的)整数(例如游戏次数)
  • FLOAT - 32 位的浮点值(例如驾驶里程数)
  • AVGRATE - 移动平均数。 详见:AVGRATE 统计类型

Steamworks 合作伙伴站点的接口可供您定义和更新游戏统计和成就,具体用途为:
  • 定义初始统计与成就
  • 增加额外统计与成就
  • 更新成就名称、描述,和图标
  • 更新统计的参数与限制(最大 / 最小值、移动平均窗口大小等等)
统计具有下列属性:
  • ID - 每项统计自动产生的数字 ID
  • 类型 - 此项统计的类型 - INT 、 FLOAT ,或 AVGRATE
  • API 名称 - 用来访问此项成就的 API 字符串
  • 设置人 - 设定谁能修改这项统计,默认为客户端。 更多信息请见 游戏服务器统计数据 文档。
  • 仅限增量 – 设置后,此统计数值便只能随着时间增加
  • 最大变更 - 设置后,每次调用一个 SetStat 到下一个 Setstat 时,此项统计的数值能改变的最大值
  • 最小值 - 设置后,此项成就能接受的最小数值,默认最小值是基本数值类型的最小值( INT_MIN 或 -FLT_MAX )。
  • 最大值 - 设置后,此项成就能接受的最大数值,默认最大值是基本数值类型的最大值( INT_MAX 或 FLT_MAX )。
  • 默认值 - 设置后,新用户初始设置的默认统计数值。 如果没有设置,默认值则为零。
  • 合计 – 设置后, Steam 将记录此项统计的全球统计。 更多信息,请见下方的全球统计。
  • 显示名称 - 显示于 Steam 社区的统计名称。 可本地化。
AVGRATE 具有以下额外的属性:
  • 窗口 - 用来计算平均值的”滑动窗口”的大小
AVGRATE 统计由 Steam 自动平均得出。 请参见下文的的 AVGRATE 一节,了解更多信息。

成就具有下列属性:
  • ID - 每项统计自动产生的数字 ID
  • API 名称 - 用来访问此项成就的 API 字符串
  • 进度状态 - 指定一项统计数据,作为此项成就在社区中的显示进度指示器。 当统计数值达到解锁值时,成就将自动解锁
  • 显示名称 - 出现于客户端弹出通知和社区的显示名称。 可本地化
  • 描述 - 社区中关于此项成就的描述。 可本地化
  • 设置人 - 设定谁能解锁此项成就。 默认是客户。 请参见 游戏服务器统计数据 ,了解更多信息。
  • 隐藏 - 如勾选,”隐藏”成就在达成之前则完全不会显示于用户的社区页面中
  • 已达成图标 - 达成成就后显示的图标
  • 未达成图标 - 未达成成就时显示的图标

以下是 Steamworks API 示例应用程序(SpaceWar) 的成就列表:
achievements_spacewar.png

使用方法

在游戏中访问统计和成就:

AVGRATE 统计类型

此统计类型提供了独特且非常有用的功能,但需要一些详细说明。

假如您想追踪某项平均统计数据,例如”每小时平均得分”。 一种办法是设置两项统计数据,一个 INT 的”总得分”,和 FLOAT 的”总游玩时数”,然后将得分除以游玩时数,得到每小时平均得分。

这个方法这的缺点是,一但玩家的游戏时数达到某个程度,计算出的平均值将变动得非常缓慢。 实际上,玩家玩得越多,平均值的反应就越不明显。 如果玩家花了 100 个小时在游戏上,得出的平均将会”延迟”大概 50 个小时。 即便他们提高了技术水平,也不会如期看到每小时平均得分的增加。

AVGRATE 统计类型则能让您设置一个可影响平均值的”滑动窗口”。 举例来说,您可使用几小时前的游戏时间数据,让统计数值更精确地反应玩家目前的技术水平。

现在来设置一个 AVGRATE 统计来实现”每小时平均得分”,而且只有过去 20 个小时的游戏才能影响数值。 方法是:
  • 由于平均值以”每小时”计算,与此统计相关的时间参数的单位都是”小时”,例如统计本身的窗口属性,和传给 UpdateAvgRateStat 的 dSessionLength 参数
  • 创建一个名为” AvgPointsPerHour ”的 AVGRATE 统计,并将窗口属性设置为 20.0 (谨记:以”小时”为单位)
  • 在游戏中适当的时候,调用 ISteamUserStats::UpdateAvgRateStat 并传入下列参数:
    • pchName – ” AvgPointsPerHour ”
    • flCountThisSession - 自从上次调用 UpdateAvgRateStat 后,玩家所获得的分数
    • dSessionLength - 自从上次调用 UpdateAvgRateStat 后,玩家的游玩时数必须使用与窗口属性相同的单位,在此例中,以”小时”为单位。
  • 例如,玩家在过去 0.225 小时( 13.5 分钟)获得了 77 分,就会变成 SteamUserStats()-> UpdateAvgRateStat( " AvgPointsPerHour", 77, 0.225 )
在上述实例中, Steam 会将这回合的平均每小时 342.2 分( 77 除以 0.225 )与先前的数值一起计算,结果便是玩家过去 20 个小时游戏时间的平均得分。 如果这是第一次为此玩家更新数据,那么目前的值就是 342.2。

此实例用”小时”作为单位,但您也可使用任何其他的时间单位。请记住,您必须始终使用该单位作为”dSessionLength”的基础,以及窗口属性。

取得其他玩家的统计数据

您可使用 ISteamUserStats::RequestUserStats 取得其他玩家的统计数据,然后使用 ISteamUserStats::GetUserStatISteamUserStats::GetUserAchievement ,和ISteamUserStats::GetUserAchievementAndUnlockTime 取得用户的其他数据。 由于其他玩家会不断上传新的统计数据,您取得的数据并不会自动更新。 再次调用 ISteamUserStats::RequestUserStats 即可刷新数据。

为避免使用过多的内存,系统会维持一个近来最少使用( LRU )缓存,并会偶尔加载其他用户的统计。 这种状况发生时将自动发送一个 ISteamUserStats::UserStatsUnloaded_t 回调。 发出回调后,再次调用ISteamUserStats::RequestUserStats 之前,都无法使用该用户的统计数据。

离线模式

Steam 会将统计与成就数据保存于本机缓存之中,以便在离线模式中也能正常使用 API 。 这段时间无法上传的统计皆会被保存至下次联机至网络。 如在多台机器上都有过修改,Steam 会自动合并成就,并选择进度最多的一组统计数据。 由于 Steam 已经在本机缓存上保存了统计数据,游戏便没有必要再将数据缓存于磁盘上。 这两种缓存常常互相冲突,当他们这样做时,用户就会认为他们的进度已经复原了。

游戏服务器统计数据

ISteamUserStats 性质相近的便是游戏服务器的 ISteamGameServerStats 了,可以使用与客户端相同的方式取得用户的统计数据。 但只有在”设定人”是 GS(游戏服务器)或Official GS (官方游戏服务器)的情况下,才可以用它来设置统计和成就。 游戏服务器和官方游戏服务器的差别在于,官方服务器是您自己主持并控制的。 使用官方服务器设定统计数据能够杜绝作弊行为,因为如此一来玩家便无法变更自己的游戏服务器,或假扮游戏服务器。 如要设置官方服务器,可从此处输入服务器的 IP 范围。

游戏服务器可设定的统计与成就不能由客户端更改。 游戏服务器只能为正在服务器上游玩的玩家设定统计与成就。 当用户离开了服务器,会有一小段宽限期可设置最终的统计,之后所有上传都会被拒绝。 这是为了保持数据的一致性,以及避免恶意的服务器随时随意更改任何人的统计。 鉴于这项限制,尽量不要等到回合结束时才设置统计。 要持续地设置统计,当用户退出游戏时便能顺利储存。

游戏服务器变更统计时,客户端便会自动获得更新。 但是,像客户端一样,加载其他用户的数据后不会自动更新,有可能不正确。

重设统计数据

开发过程中,常常会有为了测试希望清除某个帐户所有统计与成就的状况。 要清除帐户所有统计,调用 ISteamUserStats::ResetAllStats,而将 bAchievementsToo 设置为 true[/ b] 即可一同清除成就。 调用后,记得要重新逐一查看统计与成就,并重设内存中游戏状态。 无法统一清除所有用户的统计与成就。 这是因为就算统一清除了数据,运行中的游戏可能也不会注意到这次清除,而写入内存中的值。 所幸有一个简单的办法,可以在您的游戏中建立一个统一清除系统。 具体方法是:
  • 定义一项成就,名称可以用” Versoin ”
  • 在游戏中放入一个硬编码统计版本编号
  • 加载统计后,对比” Version ”和硬编码的版本编号
  • 如不相符,调用 ISteamUserStats::ResetAllStats 然后将”Version”设为硬编码号码
这样一来,无论何时您想要统一清除,只需改变硬编码的统计版本号码就可以了。 其他人拿到新生成版本时,便会统一清除数据。只需确保统一清除代码不会在最终生成版本中出现。

统计一致性

最好的做法是考虑相关的统计数据会如何变得不一致。 举例来说,您有三项统计”GamesWon”、”GamesLost”,和“GamesPlayed”。 即使考虑得再周到,统计数据之间也会确实出现无法同步的情况。此种情况下,胜场和败场数加起来有可能不到总游戏场数。 假如您将“GamesLost”去掉,而使用“GamesPlayed”-“GamesWon”来计算败场的话,数据不一致时便会导致”GamesLost”变成负数。 这种状况下,最好的办法是不要使用“GamesPlays”而使用 “GamesWon”+ “GamesLost”进行计算。

全球统计数据

可在管理页面中标出统计的合计, Steam 便会保存所有用户的该项统计的总值。 这可以用来记录经济体中所有的金钱、总击杀数、最受欢迎的武器或地图,或哪个队伍的表现更佳。 另一方面,此方法不能用在像是“MostKills”的统计中,因为将多个用户的最高击杀数加起来没有任何意义。 由于统计是掌握在玩家手上,这些数据便有遭到操纵的可能。 因此,使用合计时,设置最小值、最大值、仅限增量(如有需要)与最大变更便至关重要。 最大变更对合计统计来说意义特殊。 每当上传了新的值,合计值的改变不会超过最大变更值。 如此便能限制一个作弊者影响全球总数的快慢。

要访问全球统计,先调用 ISteamUserStats::RequestGlobalStats 然后再调用 ISteamUserStats::GetGlobalStat 取得每项统计。 也可向 ISteamUserStats::RequestGlobalStats 要求一段特定时间内的历史记录,这些历史记录了这项统计每天的变更量。 使用 ISteamUserStats::GetGlobalStatHistory 便可访问历史纪录。

您也可从客户端要求全球成就完成百分比。 首先调用 ISteamUserStats::RequestGlobalAchievementPercentages。 然后调用 ISteamUserStats::GetMostAchievedAchievementInfoISteamUserStats::GetNextMostAchievedAchievementInfo 以最多完成到最少完成的顺序逐一查看每项成就。 您也可调用 ISteamUserStats::GetAchievementAchievedPercent 取得某项成就的完成百分比。

Steam 社区

游戏发行之后,有关个人与全球的成就进度便会显示于 Steam 社区中。每个玩家都将从他们的社区资料中获得一个链接,此链接会显示他们已经取得的成就,以及他们还未解锁的内容。
备注 :在您的应用在社区曝光以前,您的成就不会显示出来。

每项成就都会与在 Steamworks 控制面板中设置的图标、名称和描述一起列出。 如果成就的名称和内容已被本地化为用户所选语言,便会以该语言显示。

此页面和游戏的 Steam 主页面皆会有一个链接连至全球成就统计数据的页面,显示每一项成就的 Steam 玩家全球达标率,按最常见到最罕见的顺序排列。 这对于玩家来说很有趣,对开发者来说也是项很有用的资源;特别挑战够不够难? 或是难度太高了? (这些信息也可在 Steamworks 合作伙伴站点上找到)。

更多问题?

请前往 统计与成就讨论区 提问。