「60fpsで」と書くと、コードはどう変わるか
遊びをつくる執筆にClaudeを使用しています。

AIに「四角が左右に跳ね返るだけのゲームを作って」と頼んだあと、同じ依頼に「60fpsで動くようにしてください」の一文だけを足して、もう一度頼む場面を考えてみます。数字を一つ加えただけですが、出てくるコードは変わるのでしょうか。変わるとしたら、ゲームの動かし方の根本(ループの方式や速度の持ち方)が変わるのか、それとも「60」という文字がコメントや定数名に増えるだけなのでしょうか。 この違いは、遊ぶ側の体験に直結します。速度を「1フレームあたり何ピクセル」で書いたゲームは、画面の更新が速い環境(120Hzや144Hzのディスプレイなど)で速く動きます。「1秒あたり何ピクセル」と時刻の差で書いたゲームは、環境が違っても同じ速さを保ちやすくなります。「60fpsで」という指定が、前者と後者のどちらを生みやすいのかは、実際に生成させて読み比べないと分かりません。 この記事は、その答えを書くものではありません。読者が自分の環境で行える実験の設計書です。問いの立て方、プロンプトの条件、記録の取り方、結果の読み方、そして誤りやすい点を順に整理します。
問いと準備するもの
問いは次の一つに絞ります。フレームレートの数値を指定すると、生成されるゲームループの方式と速度の持ち方は変わるか。 比べる対象は、数値の大きさ(30・60・120)と、数値を使わずに意図だけを伝えた場合です。
準備するものは次のとおりです。
- コードを生成できるAIアシスタント(毎回、新しい会話を始められるもの)
- ブラウザと、コードを保存して開けるテキストエディタ
- スマートフォンなどのストップウォッチ
- 記録用の表(後述)
- できれば60Hzより速いディスプレイ(なくても実験は成り立ちます)
次の2点も先に決めておきます。1つ目は、使うAIのモデル名と設定を控えておくことです。2つ目は、ベースとなる依頼文を固定することです。この記事では次の一文を使います。
ブラウザで動く、四角が左右の壁で跳ね返るだけの1ファイルのHTMLを作ってください。
この一文はすべての条件で一字も変えません。変えてよいのは、末尾に足す一行だけです。
手順:一行だけ変えて、コードを読み比べる
手順1:5つの条件で生成する
ベース文の後ろに、条件ごとに次の一行を足します。
- A、足す一行:(何も足さない)
- B、足す一行:60fpsで動くようにしてください。
- C、足す一行:30fpsで動くようにしてください。
- D、足す一行:120fpsで動くようにしてください。
- E、足す一行:表示の速さが違う環境でも、四角の動く速さが同じになるようにしてください。
Eは数値を含まず、意図だけを伝える条件です。数値を足したときの変化が「数字そのものの効果」なのか、「速度を気にしてほしいという意図の効果」なのかを切り分けるために置きます。
各条件で5回ずつ、合計25本を生成します。手間が大きいときは3回ずつでも構いませんが、1回だけで結論を出すのは避けてください。生成は毎回、新しい会話で行います。前の会話が残っていると、前回のコードに引きずられるためです。生成されたコードはそのまま A-1.html、B-3.html のように名前を付けて保存します。
手順2:コードを読んで分類する
動かす前に、まずコードを読みます。見る項目は次の7つです。
- ループの方式(requestAnimationFrame、setInterval、setTimeoutのどれか)
- 数値の現れ方(16、16.67、1000/60、1/60など、どんな形で書かれているか)
- 速度の単位(1フレームあたりの移動量か、1秒あたりの移動量か)
- 時刻の差の使用(フレームごとの経過時間を計算して、移動量に掛けているか)
- 経過時間の上限(Math.minなどで、極端に大きな値を抑えているか)
- 更新と描画の分離(一定の刻みで計算を進める「固定ステップ」になっているか)
- 数値が飾りだけか(コメントや変数名に「60」があるだけで、動きに影響していないか)
requestAnimationFrameは、ブラウザが画面を描き換える直前に関数を呼んでくれる仕組みです。通常はディスプレイの更新に合わせて呼ばれるため、60Hzなら約16.7ミリ秒に1回、144Hzならそれより速くなります。ここが、速度の持ち方の違いが表に出る場所です。
手順3:動かして速さを測る
次に、生成した各コードをブラウザで開き、四角が左の壁から右の壁まで進む時間をストップウォッチで測ります。3往復分を計り、平均を記録します。
次に、描画の速さを人工的に落として同じ測定をします。生成したHTMLの<script>より前に、次のスクリプトを貼り付けてください。requestAnimationFrameを包み、指定した間隔より短い呼び出しを間引きます。
<script>
(() => {
const TARGET_FPS = 30; // 15、30、60 に変えて試す
const interval = 1000 / TARGET_FPS;
const realRaf = window.requestAnimationFrame.bind(window);
let last = 0;
window.requestAnimationFrame = (cb) =>
realRaf(function tick(now) {
if (now - last < interval - 1) { realRaf(tick); return; }
last = now;
cb(now);
});
})();
</script>TARGET_FPSを30にした状態と、このスクリプトを入れない状態とで、端から端までの時間を比べます。1フレームあたりの移動量で書かれたコードなら、描画回数が減った分だけ時間が延びるはずです。時刻の差で書かれたコードなら、時間は大きく変わらないはずです。
このスクリプトには限界があります。呼び出しを間引くだけなので、ディスプレイの更新速度を超えて増やすことはできません。60Hzのディスプレイでは、条件Dの「120fps」は再現できません。その場合、Dはコードを読む分析(手順2)だけで扱ってください。また、setIntervalやsetTimeoutでループを回しているコードには、このスクリプトは効きません。その場合は、コード内の間隔の数値を手で書き換えて、速さが変わるかを確かめます。
手順4:基準になるコードで、測定方法そのものを確かめる
測定の道具が正しく動いているかを確かめるために、時刻の差と固定ステップで書いた最小の基準コードを用意します。この基準コードは、速度を「ピクセル/秒」で持ち、更新を1/60秒ずつ進め、経過時間の上限を設けています。
<canvas id="c" width="320" height="60"></canvas>
<script>
const ctx = document.getElementById("c").getContext("2d");
const STEP = 1 / 60, SPEED = 120; // SPEEDはピクセル/秒
let x = 0, dir = 1, acc = 0, prev = performance.now();
function update(dt) {
x += SPEED * dir * dt;
if (x < 0 || x > 300) {
dir = -dir;
x = Math.max(0, Math.min(300, x));
}
}
function frame(now) {
acc += Math.min((now - prev) / 1000, 0.25);
prev = now;
while (acc >= STEP) { update(STEP); acc -= STEP; }
ctx.clearRect(0, 0, 320, 60);
ctx.fillRect(x, 20, 20, 20);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>この基準コードに間引きスクリプトを入れても入れなくても、端から端までの時間がほぼ同じなら、測定の手順に問題はないと判断できます。もし差が出るなら、スクリプトの貼り方やストップウォッチの計り方を見直してください。
最後に、タブ切り替えの確認も行います。ゲームを動かしたまま別のタブを5〜10秒開き、戻ってきたときの四角の動きを見ます。経過時間の上限がないコードでは、離れていた時間をまとめて進めようとして、四角が不自然に飛ぶことがあります。上限があるコードでは、飛びが抑えられます。
記録の取り方
1本のコードにつき1行、次の表で記録します。紙でも表計算ソフトでも構いません。
- B-1、ループ方式:、数値の形:、速度の単位:、時刻差を使うか:、上限の有無:、固定ステップか:、通常の横断時間(秒):、間引き時の横断時間(秒):、タブ復帰時の様子:
記録のルールは次のとおりです。
- 時間の単位は秒、小数点以下1桁までとし、1本につき3回計って平均を書きます。
- 「数値の形」は、コード中の数字をそのまま書き写します。たとえば「1000/60」のように、解釈を加えずに転記します。
- 判断に迷った項目は空欄にせず、「不明」と書きます。
- 条件ごとに5本の行をまとめ、「5本中何本が該当したか」を数えます。
結果の読み方
表がそろったら、条件を横に並べて、5本中の該当数を比べます。読み方の基準を、判断の形で示します。
- Bの5本とAの5本で、ループ方式や速度の単位の分布が違う場合: 数値の指定が、方式の選び方に影響していると考えられます。たとえば、BでsetIntervalと1000/60の組み合わせが増えるなら、「60fps」という言葉が「60回/秒で呼ぶ」という書き方を引き寄せていると読めます。
- BとAで分布がほぼ同じで、「60」の文字がコメントや定数名にだけ増えている場合: 数値は飾りとして使われていると判断できます。この場合、数値を書くだけでは動きは変わらないことになります。
- B・C・Dで、数値に応じて間隔の値が変わっている場合: 数値は忠実に反映されています。ただし、速度が「1フレームあたり」のままなら、30fpsを指定したコードは半分の速さで動く設計になっているかもしれません。横断時間で確かめてください。
- Eだけが時刻の差を使う書き方に偏っている場合: 数値ではなく、意図を言葉にしたことが効いている可能性があります。この場合、「何fpsか」より「何を保ちたいか」を書くほうが有効だという読みになります。
- 間引きで横断時間が大きく延びたコードが多い場合: 速度がフレーム数に依存しています。遊ぶ環境によって速さが変わるため、投稿前に直す価値があります。
どの結論も、5本ずつの偏りとして読んでください。1本の例外があっても、すぐには判断を変えません。
起こりやすい誤り
- 会話を引き継いだまま生成する。 前の条件のコードが文脈に残り、結果が揃ってしまいます。必ず新しい会話で始めてください。
- ベース文を少しずつ言い換える。 一行以外が変わると、何が効いたのか分からなくなります。コピーして貼り付けるのが確実です。
- 表示のリフレッシュレートを確認しない。 自分のディスプレイが60Hzか、それ以上かで、再現できる条件が変わります。記録の冒頭に書いておいてください。
- ストップウォッチの誤差を無視する。 手で押す計測は、0.2秒前後ずれることがあります。差が小さいときは判断を保留し、四角の移動距離を長くして測り直してください。
- コードを読まずに動かして終わる。 見た目が同じでも、仕組みが違うことがあります。手順2の読解は省かないでください。
- タブが非表示のときの挙動を、ゲームの不具合と決めつける。 多くのブラウザは、非表示のタブでrequestAnimationFrameの呼び出しを止めます。復帰時の動きは、上限の有無の確認として読みます。
次の行動
まずは条件AとBだけ、3本ずつ生成して、手順2の7項目で読み比べてみてください。その結果が気になったら、C・D・Eを足し、間引きスクリプトで横断時間を測ります。基準コードを少し改造して自分のゲームの土台にし、HTMLをアソバースへ投稿すれば、環境の違う人の画面で動きを確かめてもらえます。ZIPで投稿する場合は、index.htmlを直下に置いてください。
参考資料
- MDN Web Docs「Window: requestAnimationFrame() メソッド」
- Gaffer On Games「Fix Your Timestep!」

