本文へスキップ

大きな世界 ​

このページは、画面より広いマップを持つゲームや、シミュレーションが 1 フレームに収まらないことがあるゲーム向けです。コロニーシム、タイクーン、工場ゲーム、大きなタイルマップなどが当てはまります。

画面内だけを描く ​

camera.sees は、ワールド座標の矩形がビューと重なるかどうかを返します。x と y は矩形の左上です。

ts
draw.withCamera(game.camera, () => {
  for (const unit of units) {
    if (game.camera.sees(unit.x - 16, unit.y - 16, 32, 32)) draw.sprite(unit.frame, unit.x, unit.y);
  }
});

タイルマップは、カメラを渡すと自動で同じ処理をします。

ts
draw.withCamera(game.camera, () => map.draw(draw, { camera: game.camera }));

0.28 から、開発ツールがこの最適化の出番を教えてくれます。ビューの外のスプライトが 1,000 個以上あり、描いた数の半分以上を占めると、コンソールの警告、オーバーレイの perf パネルの行、game_profile の drawHint で知らせます。計測は 60 フレームに 1 回で、開発時だけ動きます。draw.record のレイヤーとレンダーターゲット内のスプライトは数えません。コピーごとのスケール、インターリーブデータ、フレームのリストを使う draw.sprites も同様です。

残りをまとめて描く ​

同じフレームをたくさん描くなら、Float32Array を使い回して draw.sprites を呼びます。

ts
draw.sprites(unitFrame, positions, count);

変わらない背景は一度だけ記録して、毎フレーム再生します。

ts
const ground = draw.record((d) => {
  for (const t of tiles) d.sprite(t.frame, t.x, t.y, { origin: [0, 0] });
});
// 毎フレーム:
draw.withCamera(game.camera, () => draw.replay(ground));

描画も見てください。

シミュレーションが重いときもフレームレートを保つ ​

ステップが 1 フレームより長くかかると、時計は追いつこうとして余分にステップを実行します。するとフレームはさらに遅れ、数 fps まで落ちることがあります。0.28 からは budgetMs を指定できます。ステップがこの時間を使うと、そのフレームは追いつくのをやめます。

ts
const game = new Game({ canvas, width: 640, height: 360, clock: { budgetMs: 8 } });

ゲームは実時間より遅くなりますが、フレームレートは安定します。ステップが必要なフレームでは最低 1 ステップ実行します。game.step() は影響を受けないので、シナリオとリプレイは変わりません。

0.28 より前は、clock: { maxFrame: 1 / 30 } のように maxFrame を小さくしてください。1 フレームのステップ数を制限できますが、その値より低いリフレッシュレートではゲームが遅くなります。

重い処理をメインスレッドから外す ​

いちばん重くなりやすいのは経路探索と大きな群衆です。探索を複数のステップに分け、同じ目的地へ向かうエージェントで 1 つのフローフィールドを共有してください。ワーカーについてはスレッドを使うを見てください。

セーブが収まるか確かめる ​

Storage は localStorage を使います。容量はふつう 1 オリジンあたり約 5 MB です。入りきらない書き込みは、最後に保存できたデータをそのまま残します。0.28 から、set は書き込みがストレージに届いたかどうかを返します。

ts
if (!save.set("world", world)) showSaveFailed();

0.28 より前の set は何も返さず、書き込みの失敗を知る方法がありませんでした。もっと大きなセーブは、IndexedDB に自分で保存してください。