Steamworks ドキュメンテーション
データと実績

概要

Steamデータと実績は、ゲームに永続的でさまざまな実績を提供するための簡単な方法を提供し、ユーザーの統計と実績情報を追跡します。 Steamアカウントと関連付けられたユーザーのデータや、個々のユーザーの実績およびデータは、フォーマットを整えられ、各ユーザーのSteamのコミュニティプロフィールに表示されます。

利点

プレイヤーに価値のある報酬を付与する以外にも、実績はチームワークとプレイヤー同士の対話を奨励し、ゲームの目的に深みを与え、ゲーム内で時間を過ごすことに対して満足感を提供できます。

データはプレイ時間、パワーアップ使用回数など、きめ細かい情報を追跡します。 または、ゲームの内部データの追跡に使用することもできます。そうすることで、たとえば複数台のコンピュータを使用する1人のユーザの、すべてのセッションにわたるゲームプレイのデータを収集して、実績を付与するというようなことも可能です。

実装概要

ゲームデータと実績の定義付け

実績はアプリケーション固有で、Steamworksパートナーサイトの「アプリ管理」ページで設定します。

ゲームでは3種類のデータを保存可能です:
  • INT-32ビットの符号付き整数(例:プレイしたゲーム数)
  • FLOAT-32ビットの浮動小数点値(例:運転した距離)
  • AVGRATE-移動平均。 次のドキュメントを参照:AVGRATEデータタイプ

SteamworksのパートナーWebサイトは、ゲームのデータと実績を定義しアップデートするためのインターフェイスを提供します。 これを利用することで、次が可能になります:
  • 初期統計データと実績の定義
  • 統計データと実績の追加
  • 実績の名称、説明、アイコンのアップデート
  • データパラメーターと制約(最大/最小値、画面サイズの移動平均など)のアップデート
データには、次のプロパティが用意されています:
  • ID-各データ用に自動的に生成される数値ID
  • Type-このデータの種類-INT、FLOAT、またはAVGRATE
  • API Name-APIを使って、このデータにアクセスするために使用される文字列
  • Set By-データを修正できる人を設定。 初期値は、クライアントに設定されています。 詳細は、 ゲームサーバー統計データ を参照してください。
  • Increment Only-データは時間と共に増加することのみが可能になります。
  • Max Change-1つのSetStatコールから次のコールまでに変化できるデータ値の限度を設定します。
  • Min Value-このデータが受け入れる最小値を表します。 初期値では、最小値は基となる数値タイプの最小値になります(INT_MIN、またはFLT_MAX)。
  • Max Value-このデータが受け入れる最大値を表します。 初期値では、最大値は基本的な数値タイプの最大値になります(INT_MAX、またはFLT_MAX)。
  • Default Value-新規ユーザー用に最初に設定される初期値。 設定されない場合、初期値はゼロになります。
  • Aggregated-Steamはこのデータのグローバル合計を保持します。 詳細は、以下の「グローバルデータ」を参照してください。
  • Display Name-Steamコミュニティに表示されるデータの名称です。 ローカライズ可。
AVGRATE データには、以下の追加プロパティがあります:
  • Window-データの平均化に使用される「スライディングウィンドウ」のサイズ。
AVGRATEはSteamにより自動的に平均化されるデータです。 詳細は、以下のAVGRATEセクションを参照してください。

実績には、次のようなプロパティが用意されています:
  • ID-各実績用に自動的に生成される数値ID
  • API Name-APIで実績にアクセスするために使用される文字列
  • Progress Stat-この実績に対してコミュニティ上でプログレスバーとして使用されるデータを指定します。 データがアンロック値に達すると、実績も自動的にアンロックされます。
  • Display Name-クライアントの通知ポップアップやコミュニティで表示される実績の名前です。 ローカライズ可。
  • Description-コミュニティで表示される実績の説明です。 ローカライズ可。
  • Set By-実績をアンロックできる人を設定。 初期値は、クライアントに設定されています。 詳細は、 ゲームサーバー統計データを参照してください。
  • Hidden-trueの場合、「非表示」の実績は、達成するまでユーザーのコミュニティ ページ上に全く表示されません。
  • Achieved Icon-達成時に表示されるアイコン
  • Unachieved Icon-未達成時に表示されるアイコン

以下、Steamworks API Example Application (SpaceWar)からの実績のリストです:
achievements_spacewar.png

使用方法

ゲーム内からのデータと実績へのアクセス:

  • Steamworks APIの初期化後、ISteamUserStatsに含まれるStatsおよびAchievements APIの使用を開始できます。
  • ゲームセッションの開始時にISteamUserStats::RequestCurrentStatsを呼び出し、Steamのバックエンドからユーザーの統計データおよび実績を取得します。 データの準備が出来次第、ISteamUserStats::UserStatsReceived_tコールバックを受信します。
  • ISteamUserStats::GetStatおよびISteamUserStats::GetAchievementを使用して、データの反復処理を行い、ゲームの状態を初期化します。
  • ゲーム内で実績を表示するには、ISteamUserStats::GetAchievementDisplayAttributeを使用して、名前(「name」)や説明(「desc」)を含む、ユーザーが読める実績のプロパティデータを取得します。 これらのプロパティは、SteamworksパートナーのWebサイト上でローカライズでき、返送されるデータはユーザーがゲームを実行している言語によって異なります。 さらに、ISteamUserStats::GetAchievementIconを使用して実績のアイコンを取得や、ISteamUserStats::GetAchievementAndUnlockTimeでアンロックされた各実績の時間を取得することができます。
  • データの変更時、特にユーザーに表示されるデータの変更があった場合には、ISteamUserStats::SetStatまたは ISteamUserStats::UpdateAvgRateStatを呼び出してください。 この呼び出しは、Steamの内部メモリ状態だけを変更するもので、低コストです。 これにより、ゲームがクラッシュしたとしてもSteamはセッション間で変更を維持することができます。
  • ゲーム内の適切なポイント(チェックポイント、レベル遷移時など)にISteamUserStats::StoreStatsを呼び出し、変更をアップロードします。 完了すると、ISteamUserStats::UserStatsStored_tコールバックを受信します。
  • プログレスバーを持つ実績には、重大点においての進行状況のポップアップ表示のためにISteamUserStats::IndicateAchievementProgressを使用してください。 たとえば、20勝する必要がある場合に10回勝った時点で半分達成したことをユーザーに示す呼び出しに使うことができます。
  • 1つ以上の実績がアンロックされた際には、それぞれのアンロックされた実績に対してISteamUserStats::SetAchievementを呼び出し、次に実績をただちにアップロードするために ISteamUserStats::StoreStatsを呼び出します。 ゲームはISteamUserStats::UserStatsStored_tコールバックに加え、アンロックされた個々の実績に対して1つのISteamUserStats::UserAchievementStored_tコールバックを取得します。 その際、Steamのゲームオーバーレイはユーザーに通知パネルを表示します。

AVGRATEデータタイプ

このタイプの統計データはユニークで非常に便利な機能をいくつか提供しますが、多少説明が必要です。以下、詳細な説明です。

「1時間ごとに獲得するポイント」など、データの平均を追跡したい場合を考えてみましょう。 1つの方法としては、INT "TotalPoints"とFLOAT "TotalPlayTimeHours"の2つのデータを用意し、得点を時間で除算し、1時間当たりの得点を算出する方法があります。

この実装の欠点は、プレイ時間が多くなればなるほど、算出される平均値の変化が極端に遅くなることです。 実際、プレイヤーがゲームをすればするほど、平均値への変化が少なくなります。 たとえば、100時間プレイしたユーザーに対して、算出される平均値は50時間分ほど弱まることとなります。 プレイヤーの技術が向上しても、1時間当たりの得点は期待するほど変化しないという結果を招きます。

AVGRATEデータタイプを利用すれば、平均値に対して「スライディングウィンドウ」効果を実装できます。 たとえば、ゲームプレイにおける最も最近の数時間だけを使用することで、データはより正確にプレイヤーの現在のスキルレベルを反映したものになります。

それでは、AVGRATEデータを設定して、過去20時間のゲームプレイだけが値に反映される「1時間当たりのポイント」を実装してみましょう。 そのための手順はこうです:
  • 平均値は「1時間ごと」になるため、このデータに関連する時間のパラメーターはすべて「時間」単位になる点にご注意ください。 これは、データ自体のウィンドウプロパティと以下のUpdateAvgRateStatに渡される「dSessionLength」パラメーターにも適用されます。
  • 「AvgPointsPerHour」という名前のAVGRATE統計データと、20.0(単位は「時間」)というウィンドウプロパティを作成します。
  • ゲーム中の適切な時点で、以下のパラメーターと共にISteamUserStats::UpdateAvgRateStatを呼び出します:
    • pchName- 「AvgPointsPerHour」
    • flCountThisSession-UpdateAvgRateStatへの最終呼び出し以降に、プレイヤーが獲得したポイント数
    • dSessionLength-UpdateAvgRateStatへの最終呼び出し以降のゲーム時間。 この単位は、統計データのウィンドウプロパティ上の単位と同じものになります。 この場合、その単位は「時間(hour)」です。
  • たとえば、プレイヤーが0.225時間(13.5分)続いた最終ラウンドで、77点獲得したとします。その場合は、SteamUserStats()->UpdateAvgRateStat( "AvgPointsPerHour", 77, 0.225 )となります。
上記の例では、Steamは現在のラウンドの平均得点を、1時間当た り342.2点(77を0.225で割った数)と算出し、過去の値と合わせます。
結果は、プレイヤーの過去20時間のゲーム時間の総平均を反映します。 これが現在のユーザーについての初めてデータの更新であった場合、現在の値は342.2となります。

この例は「時間(hours)」を時間単位として用いていますが、別の時間単位を設定することができます。 ただし、「dSessionLength」とウィンドウプロパティのベースとして、その単位を一貫して用いる必要があることを覚えておいてください。

他のユーザーのデータの取得

ISteamUserStats::RequestUserStatsを使用して、別のユーザーの統計データを取得することができます。 次にISteamUserStats::GetUserStatISteamUserStats::GetUserAchievement、および ISteamUserStats::GetUserAchievementAndUnlockTimeを使用して、そのユーザーのデータを取得します。 他のユーザーが新たなデータをアップロードしたとしても、このデータは自動的にアップデートされません。データを更新するには、ISteamUserStats::RequestUserStatsを再度呼び出してください。

メモリを使いすぎないよう、使用頻度が少ないLRU(Least Recently Used)キャッシュが1つ保持され、他のユーザーの統計データは時折アンロードされます。 アンロードされると、ISteamUserStats::UserStatsUnloaded_tコールバックが自動的に送信されます。 このコールバックが送信されると、ISteamUserStats::RequestUserStatsが再び呼び出されるまで、指定されたユーザーの統計データは入手できません。

オフラインモード

SteamはオフラインモードでもAPIが正常に機能できるように、データと実績データのローカルキャッシュを保存します。 コミットできないデータは、次にユーザーがオンラインになった時のために保存されます。 1つ以上のマシンから変更があった場合、Steamは自動的に実績を統合し、より進行している方のデータセットを選択します。 Steamがデータのローカルキャッシュを保存しているため、ゲームがディスク上にあるデータのローカルキャッシュを保存する必要はありません。 そういったキャッシュがあるとしばしば競合を起こし、ユーザーから見るとゲームの進行状況が巻き戻されたように見え、フラストレーションの原因となることがあります。

ゲームサーバー統計データ

ISteamUserStatsと並行してゲームサーバー用のISteamGameServerStatsがあります。 これらによってクライアントと同じ方法(上記を参照)でユーザーの統計データを取得できます。 データの設定および実績の付与を実行することもできますが、それは 「Set by」がGS(ゲームサーバー)または公式GSに設定された場合のみ可能です。 ゲームサーバーと公式ゲームサーバーの違いは、公式ゲームサーバーはあなたがホスト・管理するサーバーであるという点です。 公式ゲームサーバーを使用して統計を設定すると、ユーザー運営のゲームサーバーでの修正(ハッキング)や、なりすましによるゲームサーバーへのチートに対策を施すことができ、セキュリティが強化されます。 公式ゲームサーバーを定義するには、こちらでサーバーのIP範囲を入力してください。

ゲームサーバーが設定可能なデータと実績は、クライアント側では設定できません。 ゲームサーバーは、現在サーバー上でプレイ中のユーザーに対してのみ統計データと実績を設定可能です。 ユーザーがサーバーを離れた際には最終的なデータを設定するための短い猶予期間が設けられますが、その後はいかなる新規のアップロードも拒否されます。 これにより統計データの一貫性が確保でき、悪意のあるゲームサーバーにユーザーの統計データを随時設定させないようにします。 制限があるので、ラウンドの終わりまで統計データの設定を待たないほうがいいでしょう。 継続的に設定し、ユーザーが終了すると同時に保存するようにしましょう。

クライアントはゲームサーバーが統計データを変更した際には自動的にアップデートを取得します。 しかし、クライアントと同様、サーバーが他のユーザー用にロードした統計データは自動的に更新されず、データが古い場合があります。

統計データの再設定

開発中には、テストのために1つのアカウント、またはすべてのアカウントの統計データや実績を完全に消去しなければならない場合があります。 アカウントの統計データをすべて消去するには、ISteamUserStats::ResetAllStatsを呼び出してください。その際に、bAchievementsTootrueに設定すると、実績も同様に消去します。 呼び出しが完了したら、統計データと実績に対して反復処理を行い、メモリ内のゲーム状態を必ずリセットしてください。 ユーザー全員の統計データと実績を一括消去する方法はありません。 その理由の1つは、一括消去を実行しても、実行中のゲームはその消去に気付かず、メモリ内の値を書き戻すかもしれないためです。 幸いにも、ゲーム内に一括消去システムを構築する簡単な方法があります。 そのためには以下を実行してください:
  • 「バージョン」などのような名前で統計データを定義します。
  • ゲーム内にハードコードされた統計データのバージョン番号を設置します。
  • データがロードされたら、「バージョン」の統計データと、ハードコードされたバージョン番号を比較します。
  • それらが一致しない場合には、ISteamUserStats::ResetAllStatsを呼び出し、続いてハードコードされた番号に「バージョン」の統計データを設定します。
こうすれば、一括消去を実行しなければならない際に、ハードコードされたデータのバージョン番号を変更するだけでよくなります。 この一括消去は、ユーザーが新しいビルドを取得した際に実行されます。

統計データの一貫性

関連データの整合性が失われた状態を想定することは重要です。 たとえば、「GamesWon」「GamesLost」「GamesPlayed」という3つのデータがあるとします。 そして、最善を尽くしたにもかかわらず、統計データの同期に失敗したとしましょう。 この例の場合、ゲームの勝敗数の合計がプレイされたゲームの総数に合致しなくなってしまったとします。 解決策として「GamesLost」の統計データを除去し、代わりに「GamesPlayed」から「GamesWon」を引いて計算した場合、不整合によって「GamesLost」の数がマイナスになってしまうかもしれません。 この場合の最善策は、「GamesPlayed」のデータを破棄し、「GamesWon」 +「GamesLost」で算出することでしょう。

グローバル統計データ

統計データは管理ページで「総計済み」とマークすることで、そのデータのユーザー全員の総数を保管するようSteamに指示することができます。 これは、エコノミーでの総通貨量、総合キル数、お気に入りの武器、お気に入りのマップ、実績が良かったチームなどのデータの取得に使用することができます。 逆に言えば、「MostKills」のような統計には使用しないでください。複数のユーザーの数値を加算しても意味がありません。 統計データがユーザーの手にある場合、このデータは改変されてしまうことがあります。 したがって、集計データを利用する際には、最小値、最大値、増加限定(適切な場合)、また最大変化値にしっかりした制限を設定することが不可欠になります。 集計データにとって最大変化値は、特別な意味を持ちます。 新しい値がアップロードされる際、最大変化値以上にグローバル値が変化することはありません。 これにより、チートプレイヤーがグローバル総計に与える影響の頻度を制限することができます。

グローバル総計にアクセスするには、ISteamUserStats::RequestGlobalStatsを呼び出してから、各グローバル統計データのISteamUserStats::GetGlobalStatを呼び出します。 過去の指定日数分の、ISteamUserStats::RequestGlobalStatsを要求することもできます。 この履歴は、毎日のデータの変化量です。 その履歴には、ISteamUserStats::GetGlobalStatHistoryでアクセスすることができます。

さらに、クライアントからグローバルの実績の達成率をリクエストすることもできます。 そうするには、はじめに ISteamUserStats::RequestGlobalAchievementPercentagesを呼び出します。 次に、ISteamUserStats::GetMostAchievedAchievementInfoISteamUserStats::GetNextMostAchievedAchievementInfoを呼び出して、達成率の高いものから低いものの順に実績を反復処理します。 特定の実績の達成率は、ISteamUserStats::GetAchievementAchievedPercentを呼び出すことで取得できます。

Steamコミュニティ

ゲームがリリースされると、個々の実績およびグローバル実績の進行状況に関する情報が、Steamコミュニティ内に表示されます。 各プレイヤーのコミュニティプロフィールに、獲得した実績と未解除の実績を表示するページへのリンクが加わります。
注意:アプリがコミュニティにある程度認知されるまで、ゲームの実績は表示されません。

それぞれの実績は、Steamworksコントロールパネルで設定された通りに、該当するアイコン、名前、説明と共にリストアップされます。 実績の名前と説明は、ユーザーが現在選択している言語にローカライズ済みの場合には、その言語で表示されます。

またこのページとSteam内のゲーム用メインページに、ゲームのグローバル実績統計データセットへのリンクが表示されます。 そこには、解除率の高い実績から低い実績の順で、そのゲームの実績を獲得したSteamプレイヤーの割合が表示されます。 これは、プレイヤーが見て楽しめる内容であるのはもちろんですが、 開発者にとっては用意したチャレンジの難易度を確認する素晴らしいリソースになります。 (この情報は、売上と有効化レポートサイト上でもご覧いただけます。)

他にも質問がありますか?

ご質問は、統計データおよび実績に関する掲示板へ投稿してください。