DxLibのHTML5版作成ができあがるまでの記録

# はじめに

これは私がHTML5ゲームを作れるようになりたいと思うようになってから, DXライブラリで作ったゲームがブラウザ上で動くようになるまでのおよそ2ヶ月間の記録を, 忘れないうちに書き留めたものです.

その時に思っていたこと考えていたことも記事に含めているため, かなり駄文になってしまっています.
emscriptenを使った開発でつまづいたところを [ぶつかった壁一覧](#ぶつかった壁一覧) に並べているので, そこから該当部分に飛んでください. また, **太字に目を通せばある程度この記事の概要が掴める**ように書いたつもりです.

成果物へのリンクを最初に置いておきます.

- DxLibの3Dアクションサンプルプログラム <https://nokotan.github.io/DxLibToHTML5Test/DxLibSampleTest/DxLibHTML5Test.html>

また, これは3部完結のうちの第2部です.

- [DxPortLibを使ってHTML5で動くアプリがつくれるようになるまでの奮闘記](https://qiita.com/nokotan/items/89d5b4eeb7775404e0ee)
- DxLibのHTML5版作成ができあがるまでの記録 (これ)
- WebAssemblyStudioを改造して, ブラウザ上でDxLib開発するための環境構築をする (未執筆)

# ぶつかった壁一覧

- [wasm-ld: error: Section too large に立ち向かう](#wasm-ld-error-section-too-large-に立ち向かう)
- [C++側で入力イベントを捕捉する](#c側で入力イベントを捕捉する)
- [.wasm ファイルを生成してダイナミックリンク](#wasm-ファイルを生成してダイナミックリンク)

# DxLibのビルドが通って画面の表示ができるまで

本家DxLibの対応しているプラットフォームは, Windows, Android, iOS, コンシューマ機だと PlayStation Vita/PlayStation4, Nintendo Switch(執筆日現在)です. このうち, **Android版, iOS版の描画ライブラリにOpenGLES 2.0**が使われているという情報が[teratail](https://teratail.com/questions/194871)から入ってきました. また, ソースコードを読んでみると, **iOS版のサウンド再生にOpenAL**が使われていることがわかりました.

ただ, ユーザ入力(タッチ, マウス, キーボード)とフォント描画に各プラットフォームに固有の機能が使われていたので, フォント描画にfreetypeを, ユーザ入力に[emscripten/html5](https://emscripten.org/docs/api_reference/html5.h.html)が提供するDOM APIを使用することとしました.

## wasm-ld: error: Section too large に立ち向かう

DxLib HTML5版の開発に**CMakeを使っていた**のですが, 突然次のエラーが出てビルドできなくなってしまいました.

```bash:エラーの内容
wasm-ld: error: ***.o: Section too large
```

結論から言うと, この現象の解決には**emsdkの再インストールが有効**でした. 原因は, .aファイルを生成するときに使用するアーカイバに, OSにプリインストールされているアーカイバ(ar)が設定されてしまうことでした.

emsdkの再インストールをするまでは, emsdkツールキット内のアーカイバを使うように設定するため, CMakeFiles/(プロジェクト名).dir/link.txtを次のように修正していました. (が, cmakeコマンドを叩くたびに設定が戻ってしまって面倒だった)

```txt:link.txt(修正前)
ar (...ここから先は変えない)
```

```txt:link.txt(修正後)
(path to emsdk)/upstream/emscripten/emar (...ここから先は変えない)
```

# Android版, iOS版から書き換える必要がある部分を実装する

## C++側で入力イベントを捕捉する

JavaScript側でキー入力イベントを捕捉しようと思ったら, 次のようなコードでできると思います.

```js:JavaScript
function onKeyDown(e) {
  // キーが押された時の処理
}

document.querySelector("canvas").addEventListener("keydown", onKeyDown, true)
```

この処理は, C++側でemscripten/html5.hを使うと次のように書くことができます.

```c++:C++
// 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の描画はフォントを除いてできるようになりました.

![](https://pbs.twimg.com/media/EQTtxWzU0AEvED0?format=jpg&name=large)

### 参考資料

- emscripten: html5.h <https://emscripten.org/docs/api_reference/html5.h.html>

## freetypeを使ってフォント描画に対応する

SDL2_ttfの実装に, freetypeが使われていたので, それをDxLibに適応するように変更をかけていきました. 

フォントの描画が次のように荒れてしまっていたのですが

```bash:期待するフォント描画(左)/実際のフォント描画(右)
__****__    ____*_*_
_**__**_    __*_*___
**____**    *_*_____
**____**    *_*_____
**____**    *_*_____
**____**    *_*_____
_**__***    __*_*___
__***_**    ____*_*_
______**    ________
_____**_    ________
____**__    ________
_****___    __*_*_*__
```

DxLib側の内部関数 `FontCacheCharImageBltToHandle` での, ビットマップフォントのビット深度とアンチエイリアスに関わる仕様が, ソースコードを追っても全容を追うことができませんでした. なので, この部分は[DxLibの管理人さんに頼らせていただきました](https://dxlib.xsrv.jp/cgi/patiobbs/patio.cgi?mode=view&no=4825). (これ自体は emscripten とは関係がないので詳細は割愛します.)

# 3D描画に対応する

いよいよこの移植プロジェクトの正念場である3D描画への対応を開始したのですが, 幸いにも, DxLibが必要とするzlib, bulletといったライブラリがすべて emscripten に揃っていました. そのため, これらのライブラリの一部のヘッダーファイルをプロジェクトを追加する必要があったものの, あっさりとモデルが描画できてしまったので, emscripten そして WebGL すごいと感動したのでした.

![3DinDxLibForHTML5.png](https://pbs.twimg.com/media/EQ-kLLmU0AAYWlj?format=jpg&name=large)

# .wasm ファイルを生成してダイナミックリンク

スタティックリンクだとビルド時間が長くイテレーションを回しづらいと感じ始めたので, emscripten でもダイナミックリンクができるかどうかを模索していました.

Windowsであれば .dll, linuxであれば .soファイルを使ってダイナミックリンクができるように, emscripten でも .wasmを使ってダイナミックリンクを行うことができます.

## .wasm ファイルの生成

emcc を使うのであれば, [Emscriptenのstandaloneモードについて](https://qiita.com/chikoski/items/29b6582eee01c8cba5b8) が参考になると思います.

CMakeLists.txtを使うのであれば, 次のように設定します.

```CMake:CMakeLists.txt
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
```

または, target を使うのであれば, 次のように設定します.

```CMake:CMakeLists.txt
set_target_properties(DxLib
    PROPERTIES 
        SUFFIX ".wasm"
)
```

## .wasm ファイルの読み込み

emscripten によって生成される .html ファイルのうち, 変数Moduleの定義の部分を探して, Moduleに次の設定を追加すると, よしなにダイナミックロードをしてくれます.

```js:index.html
// 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](https://webassembly.studio)というブラウザ上でC++/Rustのソースコードを.wasmにコンパイルして実行できる開発環境を見つけてしまって, ブラウザ上でDxLibを使ったゲーム開発ができるのではないかと期待しました.

そして, 出来上がったのが [これ](https://nokotan.github.io/WebAssemblyStudio/index) なのです.

![DxLibOnWasmStudio](https://pbs.twimg.com/media/ER29bWbUUAY4Srp?format=jpg&name=large)

この WebAssemblyStudio まわりの環境整備 (WebAssemblyStudio本体とコンパイルサーバ)についての話を次回でお話しして, 一度このシリーズを締めたいと考えています. ここまで長文にお付き合いありがとうございました.