はじめに
これは私がHTML5ゲームを作れるようになりたいと思うようになってから, DXライブラリで作ったゲームがブラウザ上で動くようになるまでのおよそ2ヶ月間の記録を, 忘れないうちに書き留めたものです.
その時に思っていたこと考えていたことも記事に含めているため, かなり駄文になってしまっています.
emscriptenを使った開発でつまづいたところを ぶつかった壁一覧 に並べているので, そこから該当部分に飛んでください. また, 太字に目を通せばある程度この記事の概要が掴めるように書いたつもりです.
成果物へのリンクを最初に置いておきます.
- DxLibの3Dアクションサンプルプログラム https://nokotan.github.io/DxLibToHTML5Test/DxLibSampleTest/DxLibHTML5Test.html
また, これは3部完結のうちの第2部です.
- DxPortLibを使ってHTML5で動くアプリがつくれるようになるまでの奮闘記
- DxLibのHTML5版作成ができあがるまでの記録 (これ)
- WebAssemblyStudioを改造して, ブラウザ上でDxLib開発するための環境構築をする (未執筆)
ぶつかった壁一覧
DxLibのビルドが通って画面の表示ができるまで
本家DxLibの対応しているプラットフォームは, Windows, Android, iOS, コンシューマ機だと PlayStation Vita/PlayStation4, Nintendo Switch(執筆日現在)です. このうち, Android版, iOS版の描画ライブラリにOpenGLES 2.0が使われているという情報がteratailから入ってきました. また, ソースコードを読んでみると, iOS版のサウンド再生にOpenALが使われていることがわかりました.
ただ, ユーザ入力(タッチ, マウス, キーボード)とフォント描画に各プラットフォームに固有の機能が使われていたので, フォント描画にfreetypeを, ユーザ入力にemscripten/html5が提供するDOM APIを使用することとしました.
wasm-ld: error: Section too large に立ち向かう
DxLib HTML5版の開発にCMakeを使っていたのですが, 突然次のエラーが出てビルドできなくなってしまいました.
wasm-ld: error: ***.o: Section too large
結論から言うと, この現象の解決にはemsdkの再インストールが有効でした. 原因は, .aファイルを生成するときに使用するアーカイバに, OSにプリインストールされているアーカイバ(ar)が設定されてしまうことでした.
emsdkの再インストールをするまでは, emsdkツールキット内のアーカイバを使うように設定するため, CMakeFiles/(プロジェクト名).dir/link.txtを次のように修正していました. (が, cmakeコマンドを叩くたびに設定が戻ってしまって面倒だった)
ar (...ここから先は変えない)
(path to emsdk)/upstream/emscripten/emar (...ここから先は変えない)
Android版, iOS版から書き換える必要がある部分を実装する
C++側で入力イベントを捕捉する
JavaScript側でキー入力イベントを捕捉しようと思ったら, 次のようなコードでできると思います.
function onKeyDown(e) {
// キーが押された時の処理
}
document.querySelector("canvas").addEventListener("keydown", onKeyDown, true)
この処理は, C++側でemscripten/html5.hを使うと次のように書くことができます.
// html5.h のインクルードが必要
# include <emscripten/html5.h>
// コールバック
EM_BOOL onKeyAction(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) {
// キーが押された時の処理
return false;
// return true; とすると, ランタイム側で e.preventDefault() を呼んでくれる
}
void onKeyboadInit() {
// ハンドラの登録
emscripten_set_keydown_callback("canvas", NULL, true, &onKeyAction);
}
ほかにも, emscripten/html5.hには, 次の関数が用意されています.
- キー入力
-
emscripten_set_keyup_callback
(キーが離された時)
-
- マウス入力
-
emscripten_set_mousedown_callback
(マウスのボタンが押された時) -
emscripten_set_mouseup_callback
(マウスのボタンが離された時) -
emscripten_set_mousemove_callback
(マウスが動いた時) -
emscripten_set_wheel_callback
(マウスのホイールが回された時)
-
- タッチ入力
-
emscripten_set_touchmove_callback
(タッチが動いた時)
-
なお, 上記に示した関数を使ってコールバック関数を登録する際, コールバック関数の第2引数にイベントの種類に応じた構造体を指定する必要があるので注意が必要です.
また, 入力に対するデフォルトの処理をさせないようにしたいときは, JavaScript であれば, e.preventDefault()
が使えます. 一方, C++ では, コールバックの戻り値に true
を指定することで, ランタイム側に e.preventDefault()
を呼んでもらうことができます.
この対応が終わったところで, 2Dの描画はフォントを除いてできるようになりました.
参考資料
- emscripten: html5.h https://emscripten.org/docs/api_reference/html5.h.html
freetypeを使ってフォント描画に対応する
SDL2_ttfの実装に, freetypeが使われていたので, それをDxLibに適応するように変更をかけていきました.
フォントの描画が次のように荒れてしまっていたのですが
__****__ ____*_*_
_**__**_ __*_*___
**____** *_*_____
**____** *_*_____
**____** *_*_____
**____** *_*_____
_**__*** __*_*___
__***_** ____*_*_
______** ________
_____**_ ________
____**__ ________
_****___ __*_*_*__
DxLib側の内部関数 FontCacheCharImageBltToHandle
での, ビットマップフォントのビット深度とアンチエイリアスに関わる仕様が, ソースコードを追っても全容を追うことができませんでした. なので, この部分はDxLibの管理人さんに頼らせていただきました. (これ自体は emscripten とは関係がないので詳細は割愛します.)
3D描画に対応する
いよいよこの移植プロジェクトの正念場である3D描画への対応を開始したのですが, 幸いにも, DxLibが必要とするzlib, bulletといったライブラリがすべて emscripten に揃っていました. そのため, これらのライブラリの一部のヘッダーファイルをプロジェクトを追加する必要があったものの, あっさりとモデルが描画できてしまったので, emscripten そして WebGL すごいと感動したのでした.
.wasm ファイルを生成してダイナミックリンク
スタティックリンクだとビルド時間が長くイテレーションを回しづらいと感じ始めたので, emscripten でもダイナミックリンクができるかどうかを模索していました.
Windowsであれば .dll, linuxであれば .soファイルを使ってダイナミックリンクができるように, emscripten でも .wasmを使ってダイナミックリンクを行うことができます.
.wasm ファイルの生成
emcc を使うのであれば, Emscriptenのstandaloneモードについて が参考になると思います.
CMakeLists.txtを使うのであれば, 次のように設定します.
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
または, target を使うのであれば, 次のように設定します.
set_target_properties(DxLib
PROPERTIES
SUFFIX ".wasm"
)
.wasm ファイルの読み込み
emscripten によって生成される .html ファイルのうち, 変数Moduleの定義の部分を探して, Moduleに次の設定を追加すると, よしなにダイナミックロードをしてくれます.
// index.html と DxLib.wasm が同じフォルダ階層にあるものとする
var Module = {
dynamicLibraries: ["DxLib.wasm"]
}
参考記事
- emscripten: Linking https://github.com/emscripten-core/emscripten/wiki/Linking
最後に何が待っているのか
いよいよ本家DxLibの移植もほぼほぼ終わり, MMDも表示できるようにするという当初の目的を達成することができました.
ただ, 本家DxLibを使った開発では, CMakeを使ったことがある人はかなり限られている気がします. 欲をいえば, Visual Studioを使ってhtml5アプリの開発ができたらいいなと思うところです. しかし, Visual Studioのプロジェクトテンプレートおよびプロジェクトファイルについての調査が追いついておらず, Visual Studioへの対応はまだ達成できていません.
その一方, WebAssemblyStudioというブラウザ上でC++/Rustのソースコードを.wasmにコンパイルして実行できる開発環境を見つけてしまって, ブラウザ上でDxLibを使ったゲーム開発ができるのではないかと期待しました.
そして, 出来上がったのが これ なのです.
この WebAssemblyStudio まわりの環境整備 (WebAssemblyStudio本体とコンパイルサーバ)についての話を次回でお話しして, 一度このシリーズを締めたいと考えています. ここまで長文にお付き合いありがとうございました.