Akashic Engineで作成されたゲームのビジュアルテストをするためのツール【概要・使い方編】

この記事は、[Akashic Engine Advent Calendar 2019](https://qiita.com/advent-calendar/2019/akashic-engine)の2日目の記事です。

# TL; DR
(すごい今更で説明するまででもないかもですが、、)[Akashic Engine](https://akashic-games.github.io/) を利用することでブラウザで動作するゲームを作成することができ、Akashic Sandbox (`akashic-sandbox`) や akashic-cli の serve コマンドで動作確認することもできます。
ただ、Akashic Engineには現状では結合テストが用意されていないので、期待通りの表示になっているかを確認する結合テスト(**ビジュアルテスト**)を作成してみました。
これを用いることによってフレーム単位で期待通りの表示になっているかを確認することができるので、シーン遷移の確認だけでなくゲーム中のアニメーションが期待通りの動作をしているかも確認することができます。

# 作ったもの
以下の2つのライブラリを作成しました。

- [akashic-contents-reftest-runner](https://github.com/dera-/akashic-contents-reftest-runner)
- [akashic-contents-reftest-helper](https://github.com/dera-/akashic-contents-reftest-helper)

akashic-contents-reftest-runnerはコンソール上で動くツールで、これを実行することで指定したゲームのビジュアルテストが動作します。このツールはNode.jsを使用して動作しますので、予めNode.jsのインストールが必要です。
このツールでビジュアルテストを実行するために、対象のゲームにakashic-contents-reftest-helperをインストールしていくつか準備しておく必要がありますが、それについては使い方のセクションで説明します。

# 何ができるのか
## 実行中のゲームスクリーンショット比較によるビジュアルテスト
上記で紹介したライブラリを利用することによって、ゲームをヘッドレスで実行して実行時のスクリーンショットが期待する表示内容とどれだけdiffがあるかを測定することができます。
diffが閾値(このツールでは画像領域中の5%を閾値としています)より大きくなってしまった場合はテストが失敗するようになっています。
コンソール上では以下のようにdiffが出力されます。

```bash

....
validate game_scene.png
diff: 3.671875%
validate game_scene_one_click.png
diff: 3.6806640625%
validate game_scene_two_click.png
diff: 3.9697265625%
....
```

さらに、具体的にどの部分にdiffが出ているかは以下のような画像がゲームディレクトリの`reftest/diff`直下に出力されるので、そこで視覚的にもdiffを把握することができます。赤い部分が期待している内容とテスト実行時のdiffとなって出力されています(大分見づらい感じになってしまいましたが。。)
![game_scene.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412511/6a5e71a5-da67-6665-7f75-4f9ba2a41520.png)


## 期待する表示内容の取得
期待する表示内容はpng形式の画像としてゲームディレクトリの`reftest/expected`直下に予め保存しておく必要があります。その画像は手動で作成することも可能ですが、akashic-contents-reftest-runnerを用いてゲーム実行中のスクリーンショットを取得することもできます。その場合ゲームの表示やアニメーションを変えたケースに対応できないのですが、ゲーム内部処理のみ修正を加えて表示に変更がないことを確認したいケースに対しては有効です。

# 使い方
## ビジュアルテスト対象のゲームで準備すること
### akashic-contents-reftest-helperのインストール
ビジュアルテスト対象のゲームでは、game.jsonと同階層のディレクトリに移動して、以下のコマンドで `akashic-contents-reftest-helper` を取得します。

```bash
akashic install @dera-/akashic-contents-reftest-helper
```

### sandbox.config.jsにakashic-contents-reftest-runnerが実行するシナリオを記述
game.jsonと同階層のディレクトリに以下のようなsandbox.config.jsというファイルを作成します。

```javascript

var scenarioTable = [
    {
      "age": 100,
      "commands": [
        {
          "name": "screenshot",
          "options": { "fileName": "game_scene.png" }
        }
      ]
    },
    {
      "age": 250,
      "commands": [
        {
          "name": "click",
          "options": { "x": 0, "y": 0 }
        }
      ]
    },
    {
      "age": 255,
      "commands": [
        {
          "name": "screenshot",
          "options": { "fileName": "game_scene_one_click.png" }
        }
      ]
    },
    {
      "age": 320,
      "commands": [
        {
          "name": "click",
          "options": { "x": 0, "y": 0 }
        }
      ]
    },
    {
      "age": 322,
      "commands": [
        {
          "name": "click",
          "options": { "x": 0, "y": 0 }
        }
      ]
    },
    {
      "age": 325,
      "commands": [
        {
          "name": "screenshot",
          "options": { "fileName": "game_scene_two_click.png" }
        },
        {
          "name": "finish"
        }
      ]
    }
  ];
var config = {
	autoSendEvents: "init",
	events: {
		init: [[32,0,"dummy",{"type":"scenario","sessionId":"dummy-id","scenarioTable":scenarioTable}]]
	}
};

module.exports = config;
```

このファイルは、Akashic Sandbox や akashic-cli の serve コマンドでゲームを実行しているときのみ利用することができるのですが、ビジュアルテストではakashic-cli の serve コマンドを動かしているので利用可能となっています(sandbox.config.jsの詳細については[こちら](https://akashic-games.github.io/guide/sandbox-config.html)を参照してください)。
ビジュアルテスト(というよりserveコマンド)では、`autoSendEvents`に書かれているイベントがゲーム起動時に実行されるので、そのイベントで`scenarioTable`を渡します。
`scenarioTable`には`akashic-contents-reftest-runner`がゲーム上で実行するシナリオを記述するのですが、具体的な記述方法については以下の表を参照してください。

| プロパティ名 | 型 |                        説明                                   |
| :-------: | :---: | :-------------------------------------------------------: |
| `age` | `number` | 何フレーム目でシナリオを実行するかを指定します |
| `commands[i].name` | `string` | 実行するシナリオを指定します。指定できるものはゲームスクリーンショットを取得する`screenshot`、ゲーム画面をクリックする`click`、ゲームを終了する`finish`のみです。`finish`を記述しない場合runnerが止まらないため最後に必ず指定する必要があります。 |
| `commands[i].options` | `Object` | `name`によって記述できるものが変わります。`screenshot`の場合`fileName`で出力ファイル名を指定、`click`の場合`x`と`y`でクリックする位置を指定する必要があります。|

### エントリポイントでakashic-contents-reftest-helperの呼び出し
`akashic-contents-reftest-helper`を有効にするために、ゲームのエントリポイント(`game.json`の`main`に指定されているファイル)に以下のように追記します。

```javascript

var reftestHelper = require("@dera-/akashic-contents-reftest-helper");
reftestHelper.init(scene); // このファイル中で定義しているsceneを引数として渡す
```

### 乱数シード値の設定について
それと、ゲームで乱数を使用している場合、ビジュアルテスト時は乱数シード値を一意に定めておく必要があります(でなければ実行結果が変わってしまうので)。
コードに直接一意の値を書き込むことも可能ですが、テストの度に一々コードを修正するのも手間ですので、[sandbox.config.js](https://akashic-games.github.io/guide/sandbox-config.html)を利用して[セッションパラメーターのrandomSeed](https://akashic-games.github.io/guide/shin-ichiba/shin-ichiba-spec#random-seed)を指定しておくことをお勧めします。

## akashic-contents-reftest-runnerの使い方
### インストール
`akashic-contents-reftest-runner` を以下のコマンドで取得します。

```bash

npm install -g @dera-/akashic-contents-reftest-runner
```

### 期待する表示内容の取得
以下のようなコマンドで、ビジュアルテストの実行に必要な期待する表示内容を取得することができます(akashic-contents-reftest-runnerの初導入時はビジュアルテスト実行よりも先にこちらを実行することをお勧めします)。

```bash

akashic-contents-reftest-runner --output . --mode expected
```

### ビジュアルテスト実行
以下のコマンドで、ビジュアルテストが実行できます。

```bash

akashic-contents-reftest-runner --output . --mode test
```

### akashic-contents-reftest-runnerで指定可能なオプション
`akashic-contents-reftest-runner (v1.0.2)`で指定可能なオプションについては以下の通りです。

|   オプション | 短縮名 |  効果 | デフォルト値 |
| :----------------: | :----: | :------------: | :----------: |
| `--output <output>` |  `-o`  |   対象のゲームのgame.jsonが`<output>` の値に応じたゲームが作成されます。詳細については後述します。   | `.` |
| `--mode <mode>`     |  `-m`  | `<mode>`で実行モードを指定します。`test`でビジュアルテストを実行して、`expected`で期待する表示内容を取得します。それ以外のモードは指定できません。 | `test` |

# まとめ
今回は、Akashic Engineでビジュアルテストをするためのツールとそれの使い方に関するお話をしました。
ゲーム開発者の方にこのツールを使っていただけると幸いです(ちょうどニコ生自作ゲームコンテストも開かれている時期ですし。。)
また、本ツールには不十分な点が多々存在すると思われますので、そういった場合は[runnerリポジトリ](https://github.com/dera-/akashic-contents-reftest-runner)もしくは[helperリポジトリ](https://github.com/dera-/akashic-contents-reftest-helper)にIssueやPullRequestを投げていただけますと助かります。。
次回の記事(12/06(金)に公開予定)では実装方法について説明していきます。

明日は @blackspotbear さんです。