本文へスキップ

ゲームを公開する ​

Keiko は手助けなしでもどんなゲームでもプレイできます。仮想時計を進め、本物の入力イベントを送るだけです。ゲームを読み、調整値にアクセスするには、開発ビルドで window.__KEIKO__ を追加します。フックはすべて任意です。

ts
if (import.meta.env.DEV) {
  window.__KEIKO__ = {
    state: () => ({ player: { x: player.x, y: player.y }, score }),
    scene: () => currentScene,
    params: () => ({ player: { speed: tuning.speed, gravity: tuning.gravity } }),
    setParam: (group, key, value) => {
      if (group === "player") tuning[key] = value;
    },
    command: (name, ...args) => {
      if (name === "warp") return loadLevel(args[0]);
    },
  };
}

型が欲しければ、そのファイルに import type {} from "keiko"; を追加します。ビルドからは消えます。

ステート ​

state() はテストが game.state として読む値を返します。小さく、JSON にしやすく保ってください。数値、文字列、真偽値、配列、プレーンなオブジェクトです。プレイヤーには違いがわからない値は丸めておく(Math.round(x))と、浮動小数点の誤差でテストが壊れません。

ts
state: () => ({
  player: { x: Math.round(player.x), y: Math.round(player.y), onGround: player.onGround },
  enemies: enemies.map((e) => ({ kind: e.kind, hp: e.hp })),
  score,
}),

シーン ​

scene() はアクティブなシーンや画面の名前を返し、game.scene として読めます。ready や「ゲームオーバーになった」といったアサーションに便利です。

ts
const game = await play({ ready: (g) => g.scene === "level-1" });

パラメータ ​

パラメータはあなたが調整する数値です。速度、重力、タイミングなど。公開しておくと、テストから設定したり(game.set、play({ params }))、スイープしたり(test.sweep)できます。

ts
params: () => ({ player: { ...tuning } }),
setParam: (group, key, value) => {
  if (group !== "player" || !(key in tuning)) throw new Error(`Unknown param ${group}.${key}`);
  tuning[key] = value;
},

未知のパラメータで例外を投げておけば、テスト内のタイプミスに気づけます。

コマンド ​

コマンドを使うと、テストは先回りできます。レベルにワープする、アイテムを渡す、状況をお膳立てする。テスト専用のショートカットは、ゲームの通常のコードパスではなくここにまとめましょう。

ts
command: (name, ...args) => {
  switch (name) {
    case "warp": return loadLevel(String(args[0]));
    case "give": return inventory.add(String(args[0]));
    default: throw new Error(`Unknown command ${name}`);
  }
},

独自の固定ステップを持つエンジン ​

デフォルトでは、テストの 1 ステップは仮想時計の 1 フレームで、あなたの requestAnimationFrame ループが実行されます。エンジンに直接呼べる決定的な固定ステップの update があるなら、それを公開すると、ステップは直接そこへ送られます。

ts
step: (n) => {
  for (let i = 0; i < n; i++) world.update(1 / 60);
},
stepSeconds: 1 / 60,

タイマーはステップごとに引き続き進みます。

入力 ​

入力はデフォルトで DOM イベントとして届きます。ゲームが別の方法で入力を読む場合(仮想コントローラー、ネットワーク経由など)は、直接受け取れます。

ts
press: (code) => controller.down(code),
release: (code) => controller.up(code),
pointerDown: (x, y) => cursor.press(x, y),
pointerMove: (x, y) => cursor.move(x, y),
pointerUp: () => cursor.release(),

debuff のゲーム ​

debuff のゲームに __KEIKO__ は要りません。Keiko は debuff の開発ツールを通してゲームを動かすので、ステートはシーンの inspect()、パラメータとコマンドは debuff のものになります。debuff のレシピを見てください。