# 大きな世界

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

## 画面内だけを描く {#draw-only-what-s-in-view}

`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` も同様です。

## 残りをまとめて描く {#batch-what-s-left}

同じフレームをたくさん描くなら、`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));
```

[描画](./drawing)も見てください。

## シミュレーションが重いときもフレームレートを保つ {#hold-the-frame-rate-when-the-simulation-overruns}

ステップが 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 フレームのステップ数を制限できますが、その値より低いリフレッシュレートではゲームが遅くなります。

## 重い処理をメインスレッドから外す {#move-heavy-work-off-the-main-thread}

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

## セーブが収まるか確かめる {#check-that-saves-fit}

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

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

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