MonoGame で制作したゲーム「リトルセイバー」を Web (Blazor + WebGL) で動かしてみた話

Web で動くゲームを作ってみようと思った話

image

現在 Steam や DLsite, Microsoft Store, Google Play で配信中のアクションゲーム「リトルセイバー」ですが、動作フレームワークとして「MonoGame」を使用しています。

当ゲームは有料での販売であるため各々で体験版を配布しているのですが、よりお手軽にゲームを体験できるようにするために開発中のデモ版も含めて以前まで Web で公開していました。

その時は「Silverlight + XNA」をフレームワークとして作り公開していましたが、残念ながら 2021 年にサポートが終了し、唯一動く IE もサポートが終了してしまったため、現在 Web で試せる環境はなくなってしまいました。まあ一応「β版開発室」にはずっとページは残っているのですが。

image

Unity など他のゲーム開発プラットフォームでは WebGL がサポートされていたりしますが、MonoGame では現時点で Web 版はサポートされていません。しかし実験的に開発はしているらしく、コミュニティを見てみたら一応 Web 上で動くライブラリを公開しているようでした。

内部的な動作ベースは WebAssembly となっているため、これまでの Flash や Silverlight とは異なり多くのブラウザでサポートされています。今後 (現在もですが) Web ブラウザで手軽に遊べるゲームを作るなら WebAssembly + WebGL ベースで作るのが良いかと思います。

WebGL で動かせるライブラリが見つかったので、今後 Web でゲームのお試し版を公開する可能性があるかもしれないことも考え、試しに現在のゲームを Web 版で動かせるかどうか試してみました。

実際に動かしてみた動画

実際にどうやったのかなどは後で書きますが、結果としては「動かすこと」はできました。どんな感じに動いているかは動画としてアップしましたので見てみてください。

ニコニコ動画

Youtube

ちなみにこのゲームを動かしているサイトは開発目的で作ったものなので非公開となっており一般の方が遊ぶことはできません。理由としては使用しているライブラリがまだ未完であるのと、遊べるレベルまで最適化するのが非常に面倒だったためです。

使用しているライブラリなど

今回使用しているライブラリは MonoGame の開発者でもある nkast さんが個人的に作ったライブラリとなっています。

該当するコミュニティのページは以下となっています。

またビルド・実行可能なサンプルプログラムも公開されています。

フレームワークとしては「Blazor WebAssembly」+「WebGL」+「.NET 6」+「C#」で動く形となっています。

動くプログラムを確認するならサンプルプログラムを触ってみるのが手っ取り早いですが、ライブラリは nuget でも公開されているので新規プロジェクトとして制作することもできます。

プログラムの移行に関していろいろやったこと

WebGL 版も MonoGame ベースのフレームワークとして作られているので大部分のコードについては変更することなく移行することができました。ただ本ゲームは 2D ベースなので 3D ベースのゲームプログラムの場合はどのぐらい影響があるかは分かりません。また、大部分は変更はありませんでしたが Web ブラウザで動くプログラムになってしまうので、いくつかは変更が必要になるものはありました。ここではゲーム固有部分ではなく誰にでも影響がありそうな修正箇所についていくつか書いてみたいと思います。

Effect (.fx) コンテンツは使用できない

デフォルトで実装されているエフェクトは分かりませんがコンテンツパイプラインから読み込む形の Effect は使用できないみたいです。`Content.Load` で読み込もうとした時点でエラーになります。2D でもカスタマイズした描画を実行している場合は注意が必要です。3D は特に影響が大きいのではないでしょうか。

リトルセイバーでは被弾時の白点滅にエフェクトを使っていたのですが今回省きました。

Song クラスは使用できない

BGM 再生などに使用されることがある `Song` クラスは使用できません。こちらも `Content.Load` でエラーになります。BGM を流したい場合は効果音と同じ `SoundEffect` クラスを使用する必要があります。`Song` クラスはストリーム再生できるメリットがあるのですが汎用性が低いこともあり、リトルセイバーでは `SoundEffect` で BGM を再生していたため手間はあまりありませんでした。

GamePad は使用できない

`GamePad` クラスで何らかのアクセスをしようとしただけでエラーになります。キーボードやマウスは使用可能です。Web ブラウザではゲームパッドの対応が元々難しかったりするので仕方ないですね。

Load 用コンテンツ (.xnb) を実行ファイルに埋め込み

WebGL 版のライブラリでは `wwwroot` フォルダ内にあるファイルからコンテンツを読み込むように作成されていますが、テスト段階でなぜか読み込めない場合がありました。キャッシュの問題なのかなんなのか不明だったため、コンテンツファイルはすべて「埋め込みリソース」として実行ファイルに埋め込むようにしました。これが原因か分かりませんが、先述のサンプルプログラムでも一部そのようにカスタマイズして読み込んでいるコードがあったりします。

コンテンツファイルを埋め込むと初回ダウンロード時にすべてのファイルをダウンロードする必要があるので読み込みに時間が掛かります。ただし、全て読み込んでからゲームを動かすのでゲーム実行中の読み込みスピードは上がります。逆に `wwwroot` に置くパターンは初回のゲーム読み込みは早いですが、コンテンツが必要なタイミングでインターネットからダウンロードするので途中の読み込みは遅くなります。

テクスチャーで Dxt3 は使用できない

おそらく WebGL 版にデコーダーがないので `Content.Load` でエラーになります。`SpriteFont` もデフォルトでは `Compressed` (Dxt3) になっているので、普通に作ってるとエラーになります。テクチャーフォーマットは `Color` にしておけば大丈夫です。

SpriteFont.Characters は使えない

使っている人がいるかどうかは分かりませんが `SpriteFont` に何の文字が含まれているかをチェックするためなどの目的で `Characters` プロパティは使用できません。

暗号化やハッシュのライブラリは使用できない

これは MonoGame 側の話ではなく Blazor 側の話です。一応クラスはあるのですが使おうとするとサポートされていないという Exception が発生します。代わりに `BouncyCastle.NetCore` などのサードパーティライブラリを使用する必要があります。ただ今後の .NET のバージョンで暗号化やハッシュがサポートされる可能性はあります。

ローカルファイルへの書き込みはできない

Web ブラウザで動作するものなのでローカルへのファイル書き込みは出来きず代案が必要となります。...とはいうものの実は一時的にできたりはしますが、結局は永続化はできないので対処は必要です。ローカルストレージやインターネット上の Web ストレージなどを使用する必要があります。

SoundEffect の音量が正しく設定できない

これはおそらく不具合だと思うのですが、`SoundEffect.Volume` プロパティを設定しても必ず 100% の音量で再生されてしまいます。対処法としては元のサウンドファイルの音量を下げるしかないのですが、さすがに面倒なので放置しています。ただなぜか再生中には音量は変更できるようです。

パフォーマンス関連

おそらくこれが一番影響がでかいのではないかと思います。ネイティブで動くコードと WebAssembly で動くコードではパフォーマンスに大きな差があるため、ネイティブでギリギリまで調整して動いているプログラムは Web で動かそうとするとほぼ動かすことが困難になるのではないかと思います。ちなみにどれぐらいパフォーマンスが落ちるかについては先述の動画で確認できると思います。

ただ WebAssembly はまだ発展途上なので今後少しづつ改善はされていくと思います。それでもネイティブに勝てることはないと思うので Web 版が正式リリースの対象に含まれているゲームの場合は定期的にパフォーマンスの確認をしていった方がよいと思います。

まとめ

今回 WebGL 版としてゲームを動かせるか試してみたのですが、目的としてはローカルで動くゲームをまずは手軽に Web 上で遊んで確認できるような環境を作りたかったというのがあります。

リトルセイバーについてはすでに本リリースしているので今のところそういう環境が必要という場面にはなっていないですが、今後そういったこともあるかもしれないので興味的なものも含めて試してみました。

もしかしたら WebGL でゲームを公開してみたいという方もいるかもしれないので、この調査結果が大なり小なりでも参考になればいいかなと思いました。

【A Hat in Time】 2章2幕 フクロウ急行殺鳥事件 アイテム全取得プレイ動画

2章2幕「フクロウ急行殺鳥事件」にある全アイテムの入手場面を動画にしました。
アイテムは主に「糸」「記念品」「リフトトークン」ですが一部バッジなども含まれています。
カットはありません。ステージ開始から全力で取りに行きます。

入手に必要なアイテムは「薬品の帽子」です。

【UnityTips】オブジェクト同士が衝突しているかどうかを判定する (2D)

ゲームなどでよくある弾がキャラクターにあたる、キャラクター同士があたる、などに必要な衝突判定を実装する方法について説明します。

以下 Unity の各 Tips まとめ

【Hollow Knight】神の家 第二 芸術者の神殿 クリア【ノーダメージ】

芸術者の神殿に登場する10匹のボスを全てノーダメージでクリアしてみました。
この辺りから敵の動きにランダム要素があるボスが出てくるためかなり苦戦するようになってきました。(とくにマーム)
オブロブルズは相変わらず闘技場のころから苦手です。
もちろんどこかで被弾すれば最初からやり直しです。つらいです。

【ノーダメ突破率 (おおよそ)】  1.136%
ゼロ : 85%
スイショウゴエイムシ : 90%
ソウルの師 : 95%
オブロブルズ : 25%
カマキリの王 : 95%
マーム : 15%
ノスク : 80%
スイツキマーム : 98%
壊れた器 : 80%
絵師シオ : 70%

【A Hat in Time】 3章3幕 破滅のトイレ アイテム全取得プレイ動画

3章3幕「破滅のトイレ」にある全アイテムの入手場面を動画にしました。
アイテムは主に「糸」「記念品」「リフトトークン」ですが一部バッジなども含まれています。
カットはありません。ステージ開始から全力で取りに行きます。

入手に必要なアイテムは「フックショット」です。
3章3幕でしか取れないアイテムを中心に取っています。