吉里吉里のゲームをSTEAMに対応する方法 プラグイン編

Steamの話

steamといえば、言わずと知れた世界で一番有名かつ大きなゲームの販売サイトです。
そこが去年の後半に成年向けゲームの販売を許可しだしたというので話題になりました。
また、日本ではあまり元気がないように思えますノベル系ADVゲームですが、最近は海外勢のものをよく見かけます。逆に人気があるのでしょうか。

2019/04/12 実際の案件を元に内容を更新

吉里吉里にsteamを組み込む

吉里吉里はよく出来たゲームエンジンで、これを使って作られたゲームタイトルはとてもたくさんあると思います。
これをsteamで売りたい、でも組み込みはどうすんだろう、という人向けの話です。
吉里吉里にsteamのUIや実績機能や組み込むのに一番簡単な方法は、krkrsteam,dllというプラグインを用いるのが一番簡単です。
これはこちらで公開されています。

準備

githubからソースコードをダウンロードして適当なところに展開します。
readmeにあるようにsteamのsdkであるsteamworkもダウンロードして適当なところに展開します。ついでに環境変数のSTEAMWORKS_SDK にsteamworkのパスも追記します。
readmeに指示されるままpremake4を実行してvisualstdioのプロジェクトファイルを生成します。

修正

さて、できあがったプロジェクトファイルをVisualStudioでコンパイルすればできあがり、となるはずなのですが、残念ながらエラーになります。

このkrkrsteam.dllが作られたときからsteamworkが更新されて、一部プラグインとは合わないようになっているからです。
なので今度はそこを修正します。

修正するのはgetCloudQuota() というクラウドストレージ機能のクオータ処理のところです。
int32 total;
int32 available;
この二つはクラウドストレージのトータルサイズと使用可能容量を示す変数ですが、現在のsdkではuint64型である必要があります。なので、これをuint64に修正します。

さらにこのtotalとavailableは、tjsの getCloudQuota() メソッドの返値として用いられようになっています。ここも修正します。
ここでは吉里吉里のゲームのクラウドストレージで4GB以上使うことはないだろう1、という見積もりでキャストして処理してしまうとします。
なので、実運用する場合はsteamのアプリ管理で、ユーザーごとのバイトクォータが4GBを超えないように設定するのがいいでしょう。(多分)
obj.SetValue(L"total", (uint32)total);
obj.SetValue(L"available", (uint32)available);
これでコンパイルが通るはずです。

吉里吉里に組み込む

できあがったkrkrstream.dllをゲームフォルダにコピーします。
併せてsteamwork sdkのフォルダからsteam_api.dllもコピーします。
krkrsteam.dllをPlugins.link で読み込みます。

AppIdの仮設定

ですが、このままではうまく動作しません。appIdが割り振られていないからです。
appIdというのはsteamで販売されるゲームに一意に割り振られている番号です。これは開発者登録をするともらえます。

開発者登録は有料です。登録料は$100で$1000以上売れたら返ってきます。個人的には、ゴミアプリをいっぱいアップロードしないようにという措置だと思っています。

じゃあ、ここでお金を払わないといけないのかというと、そこはテスト用のIdが用意されています。今回はそれを使って話を進めます。
krkrsteam.dllと同じフォルダにsteam_appid.txtというテキストファイルを作り、中に
480
とだけ記します。
さてゲームを起動してみましょう!
Steam must be running to play this game (SteamAPI_Init() failed)
と、起動に失敗してしまいました。
直訳すると「このゲームをプレイするにはsteamが実行している必要があります」です。
ということなので、steamを起動してから再度実行します。
今度は画面の端にsteamの通知が表示され、shift+tabを押すことでsteamUIが表示されたと思います。

描画部分の修正

SteamUIは表示されるようになりましたが、多くのゲームでは表示がおかしいと思います。
というのも、steamUIはゲーム画面の更新時に同期して描画されます。ですが吉里吉里は負荷軽減のため、必要に応じて描画を行う方式をとっているため、安定してsteamUIが描画されません。
具体的には画面上で何か動いているものがないとsteamUIの描画が反映されないことになります。

そこで常に画面更新をするように、ゲーム起動の比較的早い段階、kagならなばinitialize.tjsの冒頭にでも、次のように書いておきます。
function UIUpdate()
{
 Window.mainWindow.primaryLayer.update();
}
続いて、メインウィンドウの初期化後あたり、kagならば、KAGWindowコンストラクタの末尾あたりに次のように追記します。
System.addContinuousHandler(UIUpdate);
これで隙あらば、画面描画を行うようになります。
必要ならばYesNoDialog.tjsにも同様の処理を入れておきます。
これでsteamUIが画面に表示されます。

実績のテスト

実績の解除は次のように記します。
Steam.setAchievement(id or API Name);
idは数字か実績名(API Name)が入ります。
実績の内容(文言)はsteam側で設定しますので、ここをカスタマイズするためには開発者登録が必要になります。
とりあえず0を入れておくと、実績「intersteller」が発動します。
API Nameはsteamworksの実績設定画面に表示されているアルファベット名です。
これを文字列で渡すことでも実績解除が出来ます。

Steamの自動起動を組み込む

現状、steamを起動していないときのエラーがダダ漏れです。
ゲームを単体起動しようとするとSteamが勝手に起動する仕組みが入っていないからです。
この仕組みは、SteamAPI_RestartAppIfNecessaryをプラグインの初期化部分に差し込むことで解決します。
このAPIは、Steamが起動していない場合Steamを起動してログイン後に引数で渡されたAppIdのゲームをSteamが起動します。
Steam起動後にゲームを起動するため、このAPIがtrueだった場合は速やかにゲームを終了する必要があります。
TVPGetApplicationWindowHandleでゲームウィンドウのHWNDを拾って終了してください。

SteamAPI_RestartAppIfNecessaryを組み込む際に注意が二つあります。
一つ目はSteamに開発中のゲームが登録されていないとエラーになってしまうこと。
これは当然といえば当然。
二つ目はsteam_appid.txtが存在するときは、SteamAPI_RestartAppIfNecessaryが必ずfalseを返す=Steamが起動しないことです。
Steamによればこれは開発中にSteamを介さずに起動できるようにするため、だそうです。

課題

吉里吉里Zだと、エンジンの描画範囲の修正をしたほうがいいです。
じゃないとウィンドウから全画面時に切り替えると描画範囲が正しくSteamUIに反映されず、戻れなくなったりします。
これはkrkrzのBasicDrawDevice.cppを修正することで解決します。

その他にも、実績関係だったりsteamの配信サーバーへのアップロードなどステップがありますが、長くなるので今回はここまで。



  1. セーブデータくらいですし。 ↩︎