harumaxy.com

HTML5(Web)ゲーム 開発技術選定

最近Web技術でゲームを作りたいと思って色々比較してたので考えを書いておく。

なぜWeb?

とりあえず作るだけなら何でも良い。
問題なのは、人に遊んでもらうための配布・更新の簡単さであり、実際に手にとって遊んでもらうというのはフィードバックやマネタイズのために超重要。

  • Webゲーム => 配布が簡単 (URL貼るだけ)
  • モバイル/PCゲーム => 配布が困難 & DLして遊んでもらう手間のハードル

自分はソフトウェアエンジニアなのでネイティブアプリをDLして実行することは日常的に行っているが、一般的な消費者は利用している端末(PC/モバイル)・OSもばらけているしソフトをインストールするというのはかなり一般目線で高度な行為であると言える。
(AppStore/GooglePlay や Steam はそれを簡単にするが、逆にストアへの公開はエンジニアにとって作業ハードルが高い)

配布が簡単かつクロスプラットフォーム、という点で現状Webが最適。

候補

速く作る、速く配布する、(Webにおいての)動作保証という部分を重要視し、ゲームエンジンは一旦除外。

  • ゲームエンジン
  • JSライブラリ
  • Wasmをコンパイルできる言語+ライブラリ

メジャーなゲームエンジン(Unity, Unreal, Godot)は、だいたいWebへのエクスポートに対応している。が、クロスプラットフォーム対応はなかなかの作業リソースが必要かつ、最初からそれを志向しているわけではない(PC・モバイル等のネイティブが優先)のためかどうしても二級市民のような扱いが多い気がする。
例えば、パフォーマンスが重いとか、ブラウザによっては動作しないなど。

Javascript(Typescript)製のゲームライブラリはWebが主戦場かつ一級市民であるので、おそらくWebゲーム開発においては最もベストな選択。代わりに、フル機能のエディタ(シーン編集という意味)を備えないので外部ツールやコーディング力が求められる。
ライブラリの中にも、レンダリング機能のみで他は別途補完が必要なもの、ゲーム実装に必要な物理・レンダリング・オーディオなど一通りパッケージングされたものがあり、どれを選ぶかにより機能の自由度と難易度が変わる。また、ブラウザで利用できるグラフィックAPI(WebGL, WebGPU)の性能を引き出すような実装をされているかどうかなどでパフォーマンスの差もある。

システムプログラミング向けの低級言語で実装されたゲームライブラリをWasmにコンパイルしてWebで動かす手法もある。一般的にはJSエンジンより実行速度は速い傾向にある。実装の難易度はJSと同じく、エンジンと言うよりライブラリのため同様の手間、加えてWasmコンパイルの追加の手間暇が必要。
難易度が高い反面、グラフィックAPIはJSライブラリと同じの物を利用するので描画性能は差が出なかったり、ブラウザAPIの利用に制限があったりなど、JSの上位互換と言えるほどの優位はない。また、コードを書く->実行してデバッグの間にコンパイル時間が入るので開発サイクルに時間がかかりがち。
対応言語は色々あるが、実用性はRust一択かも?(後述)

Javascript(Typescript)での開発

有力そうなライブラリを列挙してみる。

  • 3D
    • Babylon.js
  • 2D
    • Phaser
    • Excalibur.js
    • Kaboom.js (Kaplay.js)

いわゆるエンジンではなくライブラリによるゲーム開発は、UNIX哲学かわからないが 2D/3D 兼用ではなくどっちかに特化しているのが多い。

ライブラリ以外は、

  • ゲームに限らず、型付き言語でないと生産性が低いので Typescript 推奨
  • バンドラーは流行りの Vite とかで問題なし
  • 開発ツールは npm パッケージが使えりゃなんでも良い (Bun とか速そう)

みたいなとこだろう。

Wasmでの開発

言語は色々ある、というかLLVMバックエンドを使える言語なら大体Wasmコンパイルできる。
が、「一応できるくらい」と「得意」には差があると考える。
実質、Productionレベルで使えるWasmコンパイル言語は C/C++, Go, Rust くらい。

awesome-wasm-langs

有力そうなライブラリ

  • Rust
    • Bevy
    • Macroquad (raylib風)
  • C/C++
    • Raylib + Emscripten
  • Go
    • Ebitengine (触ってない)

C/C++ ならRaylibがWasmコンパイルに対応している。Raylib自体は様々な言語のバインディングを提供しているが、Emscriptenに依存する関係上で余計な言語を混ぜるとコンパイル設定が複雑になりそうなのでやるなら pure C ゲームコードも実装したほうが良さそう。(どの言語でも結局raylib使うならあんまりコードに差はでなさそう)
テンプレートがあるのでこれに習うのが良さげ。ぶっちゃけRaylib風のライブラリがほしいなら Rust + Macroquad でいい気がする。(ただしRaylibは割とStarが多く、サンプルが充実してる)

Rust はゲーム抜きにしても Wasm のコンパイル元として人気がある。

  • Macroquqd: シンプルで使い易い必要十分の機能(Raylib的)
  • Bevy: 難易度が高いが大規模開発で管理しやすく高パフォーマンス(ECSアーキテクチャを採用・wgpuに対応)

という対比か。
プログラミング言語としての難易度とは裏腹に、C系と違ってツールチェーンがデフォルトで充実していて開発生産性やとっつきやすさが段違い。Wasmビルドもビルドコマンドに--target wasm32-unknown-unknownで対応できる。楽すぎ。

raylib はビルドで諦めたが、 bevy と macroquad は両方ともWasmビルドしてブラウザで動かすとこまでやった。簡単すぎてWasm初心者には間違いなく Rust をおすすめする。

Go は使ったこと無いけど Ebitengine とかいうのが Wasm 対応してるらしい。
開発者が日本人。

JS vs Rust(Wasm)

最終的に Rust で Wasm 開発するか、 JS で開発するかが の二択かが Web ゲーム開発では実用的な選択肢になると考えられる。

  • ビルド時間が速いほうが良いならJS
    • ゲームでは特に、コード書いて実行 のスパンが短いことは重要だと考える。
  • ネットワーキング要素があるならJS
    • Fetch, WebSocket, WebRTC といったブラウザAPIを使える
  • パフォーマンスが必要ならWasm
    • といってもV8も速いのでそこまで体感できないかも
  • ネイティブアプリも作るならRust
    • ただ、ElectronやTauri、React NativeなどでJSアプリをネイティブにする手法もある

静的型がほしいならRustでもTypescriptでもどっちでも良い
文法の違いでどっちかが劇的に生産性高いということもない。
wasm-bindgen で Wasm からブラウザAPIを利用することもできなくはないが、第一級かどうかという部分の差がデカい。(結局Wasm->JSへのFFIのような感じになってくるので)

結論

JS か Wasm か、でどっちかしかできないことというのは無いように思えるが、手間が少ない点ではJSが勝っているように思える。パフォーマンスもそこまで差がない。
(WasmはJSの2~3倍は出るが、JSでもenough感)

というわけで(というか当然かもだが)、Webの開発はJSでやるのがベスト。