Games × Web

ゲームの最適化に学ぶ、Webの「計算の配り方」 遠くは、手を抜く。

遠くの敵の動きが、少しだけカクつく。
あれは手抜きではなく、設計だ。 限られた計算を、気づかれる所に配っている。

2026年10月 読了 約12分 動くデモつき

遠くにいるモンスターだけ、
動きがほんの少し粗い。

『モンスターハンターワイルズ』の体験版を触っていて、そう感じた。目の前の相手は滑らかに動くのに、遠くの群れは、よく見るとコマ送りのように見える。そういえば『星のカービィ ディスカバリー』でも、離れた敵の動きが似た感じだった。

この記事が答える問い

限られた計算を、
どこに配ればいいのか?

調べてみると、これは不具合ではなく、意図して入れられた仕組みだった。ゲームは1コマを描くのに使える時間が決まっていて、その中に収まるように、プレイヤーが気づきにくい所から計算を削っている。

結論を先に置く。ゲームの最適化の根っこには、二つの考え方がある。予算を先に決めることと、知覚されない所から削ることだ。この二つは、Webでもそのまま使える。効くのは体感速度だけではない。サーバー代にも効く。

先に断っておくと、筆者はゲーム開発者ではない。Webのシステムを作っている側から、公開されている資料とプレイした体感をもとに整理した。

Chapter 01

ゲームは、
16.7msの予算で動いている

ざっくり言うと——1コマに使える時間は決まっている。機種が変わると予算の大きさは変わるが、配り方の仕組みは共通にできる。

1000 ÷ 60 ≒ 16.7 ms

60fpsで動かすなら、1コマの計算と描画を16.7ミリ秒に収める(30fpsなら33.3ミリ秒)

ゲームは、1秒間に何十枚もの絵を描き直している。60fpsなら、1枚あたり16.7ミリ秒。この中に、敵の動き、物理、音、描画のすべてを収めなければならない。はみ出せばコマが落ちて、画面がカクつく。ゲームの処理には、最初から予算がある。

最近は、同じゲームが複数の機種で出ることが多い。PS5のCPUはAMDのx86-64、Nintendo Switch 2のCPUはArmのCortex-A78Cで、命令の体系からして違う。それでも同時に出せるのは、違いの多くをゲームエンジンが吸収しているからだ。

カプコンは統合報告書で、内製の RE ENGINE を「マルチプラットフォームに対応できる」エンジンと説明している。いまはすべてのタイトルをこのエンジンで開発しているという。Switch 2の発売日に『ストリートファイター6』と『祇(くにつがみ)』の2本を出せたのも、このエンジンで効率よく作れた成果だとしている。

命令やOSの違いを吸収したあとに残るのが、性能の差だ。据え置き機と携帯機では、同じ16.7ミリ秒の中でできる計算の量がまるで違う。そこで、予算そのものを機種ごとに変えられるようにしておく。

Unreal Engine には「Animation Budget Allocator」という仕組みがある。キャラクターのアニメーションに使ってよい時間を、ミリ秒で決めておく。収まらないときは、重要度の低いキャラクターから更新の頻度を落とす。この予算は、品質設定を通じて機種ごとに変えられる。

この章を一言で

予算の大きさは機種ごとに、配り方の仕組みは共通に。

Chapter 02

遠くは、
手を抜く

ざっくり言うと——遠くのものは粗く描き、動きの計算も間引く。間引いた分は、時間をずらして平らにならす。

予算に収めるための代表的な手は、三つある。

LOD(Level of Detail)

遠いものほど、ポリゴンの少ない粗いモデルに差し替える。画面の上で数ピクセルにしかならないなら、細部は見えない。

更新の間引き

遠いキャラクターの動きは、毎コマではなく2〜4コマに1回だけ計算する。冒頭の「コマ送りに見える」の正体はこれだ。

カリング

画面の外にあるもの、何かの陰に隠れたものは、そもそも描かない。

『星のカービィ ディスカバリー』は、レビューで「カービィから少し離れた敵は、低いフレームレートで動く」と書かれていた。もともと30fpsのゲームで、その半分になる場面もあったという。Switch 2 Edition では、この間引きがほとんど目立たなくなった。間引かれるのは、ずっと遠くのものだけになったと報じられている。予算が増えた分を、遠くに配り直したと読める。

『モンスターハンターワイルズ』のPC版には、遠くのものの動きを間引く距離と頻度を決める設定項目がある、とプレイヤーの間で話されている。手の抜き方が、プレイヤーが選べる「つまみ」になっているわけだ。

言葉で読むより、動かしたほうが早い。下のデモで確かめてほしい。

Demo

中央の円がプレイヤーで、ドラッグすると動かせる。生き物はどれも、動く前に重い「骨格の計算」をする。「遠くほど間引く」に切り替えると、点線の輪の外側ほど計算の回数が減る。

計算の時間

0.0 ms

このフレームで計算した数

0

削った計算

0%

直近3秒の計算時間(点線は60fpsの1フレーム=16.7ms)

数字は、いま読んでいる端末で実際に計った値。重さは端末によって違う。このデモ自体も、画面から外れると止まる。

間引くと、計算の量は目に見えて減る。手元のMacで1000体を動かすと、1コマで計算する数がおよそ8割減った。それでも、プレイヤーの周りの動きは何も変わらない。

もう一つ試してほしいのが、「更新をずらす」を切った状態だ。間引いた生き物が全員同じコマで計算すると、4コマや8コマごとに計算時間の山ができる。平均は減っても、山のコマでは予算からはみ出すかもしれない。間引いた計算は、コマをまたいで均等にばらす。ここまでやって、ようやく画面が安定する。

どれを間引くかは、距離だけで決まるわけではない。Unreal Engine の「Significance Manager」は、物ごとの重要度を計算する仕組みだ。重要度の低い物は、さりげない効果音やパーティクルを省く、といった使い方が公式の説明に挙がっている。遠くても、プレイヤーが狙っている相手は削らない。近くても、背景の小物は削ってよい。

この章を一言で

気づかれない所から削り、削った分は時間をずらしてならす。

Chapter 03

Webに
持ち込む

ざっくり言うと——Webの「遠く」は、画面の外、裏のタブ、いま見ていない情報。ここを毎回きっちり計算しているなら、削れる。

Webの画面にも「遠く」はある。スクロールしないと見えない行、裏に回ったタブ、開いてはいるが誰も見ていないダッシュボードの一角。ゲームの三つの手は、それぞれ次のように持ち込める。

カリング → 見えない所は作らない

  • 長い一覧は仮想スクロールにして、画面に入っている行だけをDOMに置く
  • CSS の content-visibility: auto で、画面外の要素の描画を省く(2024年9月から主要ブラウザで使える)
  • 画像は loading="lazy" で、近づいてから読み込む

更新の間引き → 見ていないものは、ゆっくり更新する

  • 裏に回ったタブでは、定期的な問い合わせを止める(Page Visibility API)
  • 画面外のウィジェットは、更新の間隔を延ばす(IntersectionObserver)
  • データの鮮度に段階をつける。大事な数字は即時に、そうでないものはキャッシュを長めに

LOD → 遠くは粗く、近づいたら細かく

  • 画像は srcset で、表示する大きさに合った解像度を出し分ける
  • まずぼかした小さな画像を出し、あとから本物に差し替える

更新をずらす → 重い処理を細切れにする

  • 長いJavaScriptの処理は途中で手を離し、その間に入力を受け付けられるようにする。scheduler.yield() は Chrome・Edge・Firefox で使えるが、Safari は未対応なので setTimeout で代わりにする
  • サーバー側の定期処理も、全員を同じ時刻に起こさず、時刻をばらす

たとえば、ダッシュボードの一部を定期的に更新するなら、こう書ける。見えているときは10秒ごと、画面外なら60秒ごと、裏のタブでは止める。

// 見えているかどうかで、更新の間隔を変える
let onScreen = true;
let timer;

function schedule() {
  clearTimeout(timer);
  if (document.hidden) return;              // 裏のタブ:止める
  const ms = onScreen ? 10_000 : 60_000;    // 画面外:間引く
  timer = setTimeout(async () => {
    await refresh();
    schedule();
  }, ms);
}

new IntersectionObserver(([e]) => {
  onScreen = e.isIntersecting;
  schedule();
}).observe(widget);
document.addEventListener('visibilitychange', schedule);
schedule();

ブラウザ自身も、同じ発想で動いている。裏に回ったタブでは requestAnimationFrame をほぼ止め、タイマーも間引く。ただ、ブラウザが間引くのは「いつ動くか」までで、サーバーへの問い合わせを要るかどうかは、アプリにしか判断できない。そこはアプリ側で決める。

この章を一言で

「全部を、毎回」をやめる。

Chapter 04

予算を、
先に決める

ざっくり言うと——削る技術は、予算があって初めて働く。画面の速さには時間の予算を、サーバーには回数の予算を置く。

ゲームの最適化で一番大事なのは、個々の技よりも、先に予算が決まっていることだと思う。16.7ミリ秒という上限があるから、「どこを削っても気づかれないか」を考える文化が育った。予算がなければ、削る理由がない。

Webの画面の速さにも、予算の物差しがある。たとえば操作への反応の速さを表す INP は、実際のページ表示の75%で200ミリ秒以内に反応できていれば「良い」とされる。ゲームの16.7ミリ秒と同じように、この数字を先に置き、超えたら直す。手元の速いPCではなく、遅い端末と遅い回線で測る。

サーバー側にも予算が置ける。こちらの単位は時間ではなく、回数だ。従量課金のサービスでは、請求はおおむね呼び出しの回数に比例する。

試算:30秒ごとに更新するダッシュボード

1日8時間開きっぱなし

960 回

1人・1日あたり

実際に見ているのが3分の1なら

320 回

裏のタブで止めた場合

1000人で減る回数

64万 回

1日あたり

見ている時間の割合は、説明のための仮定

遠くの敵の動きを間引くのと、裏のタブで問い合わせを止めるのは、同じ判断だ。誰も見ていない所の計算は、利用者に何も届けていない。それを削ると、画面は軽くなり、請求も小さくなる。

負荷が高いときに品質を落とす、という手もゲームから借りられる。ゲームは重い場面で、気づかれない程度に解像度を下げて、コマ落ちを防ぐ。Webでも、混み合ったときにおすすめ欄のような「なくても困らない機能」を一時的に止めて、本体の画面を守れる。それには、どの機能が削れるのかを、平時のうちに決めておく必要がある。

この章を一言で

予算が先、削り方は後。

Appendix

ゲームの手法と、
Webの対応表

ゲーム やっていること Webでは
フレーム予算1コマ16.7msに収めるINPなどの目標値、サーバーの呼び出し回数の上限
LOD遠いものは粗いモデルにするsrcset、ぼかした仮の画像
更新の間引き遠いキャラは数コマに1回だけ動かす画面外・裏のタブの更新を減らす、鮮度に段階をつける
カリング見えないものは描かない仮想スクロール、content-visibility、loading="lazy"
更新をずらす間引いた計算をコマにばらす長い処理を細切れにする、定期処理の時刻をばらす
重要度大事なものに計算を厚く配る画面の主役から先に読み込み、更新する
動的解像度重い場面だけ解像度を下げる混雑時に、なくても困らない機能を止める

並べてみると、ゲームの最適化は「計算を速くする技術」というより、「計算を配る技術」だと分かる。何を速くするかの前に、何をやらないかを決めている。遠くの敵がコマ送りに見えたら、それは誰かが予算と相談して、そこなら気づかれないと判断した跡だ。

Fact-check

仕組みの説明はエンジンの公式ドキュメントとメーカーの公式資料を、各作品の挙動は技術レビューを選んだ:カプコン 統合報告書2025(RE ENGINE)/Epic Games Animation Budget Allocator・Significance Manager/Switch 2 の仕様は Nintendo Life(Digital Foundry の分析)/『星のカービィ ディスカバリー』の間引きは GamingBolt のレビュー/『モンスターハンターワイルズ』の設定項目は Steam コミュニティの議論(プレイヤーによる説明)/MDN content-visibility・scheduler.yield()・Page Visibility API(裏のタブでの間引きを含む)/web.dev INP。冒頭の体感は筆者がプレイした印象。デモの数値は読者の端末で計った値で、ゲームの実際の処理とは別物。手法の対応づけは筆者の整理。