世界を動かす技術を、日本語で。

Show HN: 「Terraria」と「Celeste」をWebAssemblyに移植する

概要

  • TerrariaCeleste をWebAssembly経由でブラウザ上に移植した技術的挑戦の記録
  • C#FNAエンジン を活用し、元のソースコードを最小限の修正でWASM化
  • アセット管理スレッド対応OpenGL描画 の課題と解決策
  • FNA ProxyOffscreenCanvas などの技術的工夫
  • Gitリポジトリ や実際に動作するデモも公開

Terraria/Celesteをブラウザで動かすまでの技術的道のり

  • Celeste をブラウザで動かしたという過去の投稿に触発され、独自に移植作業を開始

  • TerrariaCeleste はどちらもC#とFNAエンジンベースであり、同様の手法で移植可能と判断

  • ゲームの デコンパイルWASMターゲット への再コンパイルを目指す

  • ilspycmd によるデコンパイル時にReLogic.dllが欠如していたが、バイナリから抽出することで解決

  • 必要ライブラリ を揃えて再ビルドし、Linux上での起動に成功

    • 新規プロジェクトを WASMターゲット で作成し、emscriptenの設定を追加
    • FNAエンジン のC++部分はemscriptenのOpenGLエミュレーションでビルド
    • FNA-WASM-BUILDを利用し、GitHub Actionsで自動化
    • ネイティブライブラリは<NativeFileReference>でリンク
  • ビルド後、最小限のエントリポイントを[JSExport]で公開し、JSからFNAゲームループを制御

  • アセット管理 はOrigin Private File System APIとwindow.showDirectoryPicker()でユーザーから取得

  • FirefoxWebKit 系ブラウザのAPI対応差異も吸収

  • .NET 8.0 wasm ではスレッド未対応だったが、.NET 9.0 wasmでWasmEnableThreadsを有効化

  • メインスレッド問題 はOffscreenCanvas未対応のため、FNA ProxyでOpenGL呼び出しをDOMスレッドにプロキシ

  • FNA Proxy はfishスクリプトでラッパーC関数を自動生成し、C#のPInvokeバインディングも書き換え

  • AES暗号化 未対応はemscripten OpenSSLで自前実装し、<NativeFileReference>でリンク

  • AOT(Ahead-Of-Time)コンパイル でパフォーマンスを大幅向上

  • 実際に遊べるデモGitリポジトリ を公開

Celesteのブラウザ移植と追加課題

  • Celeste もFNAベースのため、TerrariaのWASM化手法をそのまま流用

  • SDL3 の安定化によりOffscreenCanvasが利用可能となり、FNA Proxy不要に

  • FMOD (Celesteの音声ライブラリ)はEmscriptenビルドはあるが、Web Worker内での動作に非対応

    • wrapスクリプトの再利用は不可能(FMODが非オープンソースのため)
  • Everest mod loader のブラウザ対応も視野

    • 追加パッチやEmscriptenのバグ回避も必要
    • 依存関係の違いによるさらなる調整

まとめ・リポジトリ・デモ

  • Terraria 移植版デモ:[Terraria in the browser here]

  • Celeste 移植版デモ:[Celeste in the browser here]

  • Terraria Gitリポジトリ :[Terraria git repository]

  • Celeste Gitリポジトリ :[Celeste git repository]

    • 各プロジェクトは 公式データ を所有していることが前提
    • オープンソースコミュニティ の協力と技術的工夫による成果
    • 今後も WASMゲームエンジン の進化に伴い、さらなる移植や最適化が期待される

Hackerたちの意見

ブラウザでFNA/XNAプロジェクトが動いてるのを見るのはクールだね。

C#のバイナリをデコンパイルして、WASMみたいな全然違うターゲット用に再コンパイルするのがすごいよね。

それに、最後まで行くためにやったことがすごいよね。

デモを試してたら「Firebaseの帯域幅制限を超えました」ってエラーが出た。静的サイトならこんなこと心配しなくていいはずだよ。CloudflareやGitHub PagesみたいなCDNにホスティングすればいいのに(無料だし!)。

すみません!GitHub Pagesのフォールバックに切り替えたよ。Cloudflare PagesはWASMファイル(100MB以上)が25MBの制限を超えちゃうから使えないんだ。(サービスワーカーで回避できるけど、あれはちょっと不安定だしね。)GitHub Pagesも、SharedArrayBufferを使うために必要なcoi/coepヘッダーを送る方法がないから適してない。サービスワーカーで回避できるけど、できればそれは避けたいな。

これめっちゃクールだね。ただ、ブログ自体はFirefoxで60FPS出てないよ。たぶん背景のエフェクトのせいかな?

そのエフェクトは気持ち悪くて気が散ったよ。インスペクターで削除できるキャンバス要素で描画されてるんだ。

これは本当にすごい!すぐに自分のChromebookで使いたいと思ってる。

あなたの(?)ウェブサイト、リソースをめっちゃ消費しててヤバい。i5-7500Tでまともなフレームレートで動かすのが大変だよ。ブラウザが完全に崩れちゃう。

スマホで全部スクロールしたけど、全然問題なかったよ。

サイトでJavaScriptを無効にすると、完璧に動くよ。パフォーマンスを壊すやつだと思うけどね。https://velzie.rip/static/background.js

Celeste Classic in WASM https://midzer.de/wasm/celeste/

なぜか倍速で動いてて、すごく難しい。

「ブラウザで動いてはいけないはずのものがブラウザで動いてる」っていう変なプロジェクトのジャンルが好きなんだ。お気に入りの一つは、[...] Minecraft 1.12の直接再コンパイルだよ。Minecraftって元々Javaアプレットだったから、ブラウザで動くべきものだと思うんだけどね。ポイントはわかるけど、面白いなと思うよ。

サーバー上でWebAssemblyをコンテナで動かして、オーケストレーションサービスで連携しつつ、サンドボックス環境間で通信するってアイデアがさらに面白い。なんか思い出すけど、最近記憶が曖昧になってきてるな。/s

最近、WASM+OpenGL+SDLを使ってウェブ向けの基本的なゲーム開発を始めたんだけど、驚くほど簡単だよ。コードをウェブで動かすのにかけた時間より、CMakeファイルいじってた時間の方が長かった。ウェブプラットフォームにはまだいくつかの制限や粗い部分があるけど、正直、WindowsやMacOS用にコンパイルするよりもWASM用の方がずっと楽だった。