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


この記事は、Akashic Engine Advent Calendar 2019の2日目の記事です。

TL; DR

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

作ったもの

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

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

何ができるのか

実行中のゲームスクリーンショット比較によるビジュアルテスト

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


....
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となって出力されています(大分見づらい感じになってしまいましたが。。)

期待する表示内容の取得

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

使い方

ビジュアルテスト対象のゲームで準備すること

akashic-contents-reftest-helperのインストール

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

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

sandbox.config.jsにakashic-contents-reftest-runnerが実行するシナリオを記述

game.jsonと同階層のディレクトリに以下のようなsandbox.config.jsというファイルを作成します。


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の詳細についてはこちらを参照してください)。
ビジュアルテスト(というより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の場合xyでクリックする位置を指定する必要があります。

エントリポイントでakashic-contents-reftest-helperの呼び出し

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


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

乱数シード値の設定について

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

akashic-contents-reftest-runnerの使い方

インストール

akashic-contents-reftest-runner を以下のコマンドで取得します。


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

期待する表示内容の取得

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


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

ビジュアルテスト実行

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


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リポジトリもしくはhelperリポジトリにIssueやPullRequestを投げていただけますと助かります。。
次回の記事(12/06(金)に公開予定)では実装方法について説明していきます。

明日は @blackspotbear さんです。