【Godot】GodotのFPSを安定させるフレームレート管理と最適化

作成: 2025-12-10最終更新: 2026-07-09

Godotでゲームのカクつきを解消する方法を解説。物理ティックとレンダリングフレームの違い、V-Syncとmax_fps、ジッターを根絶する物理補間、そして計測に基づく最適化の考え方まで図解します。

「V-Syncを入れたのに滑らかにならない」「多数の敵を出した瞬間だけカクつく」「_process_physics_process の使い分けが曖昧」——フレームレートまわりの悩みは、Godotの 描画と物理演算が別々のサイクルで動いている ことを掴むと、驚くほどスッキリ解決します。

この記事では、まずカクつき(ジッター)の 根本原因 を押さえ、V-Syncと max_fps によるフレームレート制御、ジッターを根絶する 物理補間 、そして「計測してから最適化する」考え方まで、図とコードで整理します。

フレームレート管理のイメージ。ガタガタした階段状の動き(ジッター)と、なめらかに補間された動きが対比され、安定したFPSがゲーム体験を支えることを示す

この記事でわかること

  • カクつきの正体—— 物理ティックレンダリングフレーム のズレ
  • V-SyncEngine.max_fps によるフレームレート制御
  • 物理補間 でジッターを根絶する(設定1つ+手動補間)
  • 「推測するな、計測せよ」——プロファイラに基づく最適化の順番

Sponsored

なぜカクつくのか:物理ティック vs レンダリングフレーム

Godotのパフォーマンスを理解する鍵は、 物理ティック(Physics Tick)レンダリングフレーム(Rendering Frame) という、独立した2つのサイクルを区別することです。この2つが 別々の速さで動く ことが、ジッターの直接の原因です。

物理ティックとレンダリングフレームのズレを示す図。上段は固定60Hzで等間隔に打たれる物理ティック、下段は可変144Hzで細かく打たれるレンダリングフレーム。物理の更新の合間に複数の描画が挟まり、物体が同じ位置に留まってから次のティックで飛ぶ階段状の動きになる
物理ティックレンダリングフレーム
役割物理演算・衝突判定(_physics_process画面描画・入力(_process
頻度固定(デフォルト60Hz)可変(性能・V-Sync依存)
delta固定値(1 / 物理レート)可変値(前フレームからの経過時間)

例えば物理が60Hz、モニターが144Hzのとき、物理エンジンは1/60秒ごとにしか位置を更新しません。一方で画面は1/144秒ごとに描こうとするので、 複数の描画フレームで物体が同じ位置に留まり、次の物理ティックで突然ジャンプ します。この「階段状の動き」がジッターの正体です。だから「入力や描画は _process、物理的な移動は _physics_process」という 使い分け が基本になります。

フレームレート制御:V-Syncとmax_fps

まずは描画側のフレームレートを制御します。手段は V-Sync(垂直同期)Engine.max_fps の2つです。

V-Syncモードの使い分け

V-Syncは描画をモニターのリフレッシュレートに同期させ、画面が途中で切り替わる ティアリング(裂け目) を防ぎます。Display > Window > Vsync > Vsync Mode で設定します。

V-Syncの4モード比較図。Disabled(同期なし・遅延最小だがティアリング大)、Enabled(ティアリング防止だがFPS低下時に入力遅延)、Adaptive(高FPS時のみ同期)、Mailbox(ティアリングと遅延を両立して抑制)の4つを、ティアリングと入力遅延の��観点で並べる
モード概要向いている場面
Disabled同期しない。FPS上限なし入力遅延を最小にしたい(ティアリングは出る)
Enabled常に同期ティアリングを完全に防ぎたい(FPS低下時に遅延増)
Adaptive高FPS時だけ同期ティアリング防止+FPS低下時の遅延回避
Mailbox最新フレームを保持して表示ティアリングと遅延を両立して抑えたい(VRAM増)

注意: Godotで設定しても効かない場合、NVIDIA/AMDのドライバ設定がGodotを上書きしていることがあります。ドライバ側を「アプリケーションによるコントロール」にしてください。

Engine.max_fpsで固定する

特定のFPSに固定したいとき(レトロ風の演出、モバイルの省電力など)は Engine.max_fps を使います。

func _ready() -> void:
    Engine.max_fps = 60   # 最大FPSを60に固定(V-SyncがDisabledのときに有効)

一度設定すればゲーム全体に効きます。ただし V-SyncがEnabledのときは max_fps は無視される 点に注意してください。

Sponsored

物理補間でジッターを根絶する

フレームレートを制御しても、物理とレンダリングの ズレそのもの は残ります。それを埋めるのが 物理補間(Physics Interpolation) です。固定された物理ティックの 間の位置を、描画時になめらかに補間 することで、階段状の動きを消します。ジッター対策として最も効果的です。

物理補間の前後を対比する図。補間なしは、物理ティックの位置にだけ物体が現れ、間はカクカクとジャンプする(階段状)。補間ありは、物理ティックの間の位置が計算され、なめらかな直線の動きとして描画される

プロジェクト設定で有効化(推奨)

Godot 4.3以降、2D・3Dの両方で物理補間が標準サポートされています。多くの場合、次の設定だけで解決します。

  1. プロジェクト設定 を開く
  2. Physics > Common > Physics InterpolationOn にする

これで CharacterBody2D/3DRigidBody2D/3D など、物理の影響を受けるノードの動きが自動で補間され、なめらかになります。

注意: 物理補間を有効にすると、ノードの位置は「補間された見た目の位置」で描画されます。物理ティックの合間に global_position を直接いじると補間と競合するので、 移動は _physics_process 内で行う のが基本です。

実践:カクつかないカメラ追従を組む

物理補間は物理ノードには自動で効きますが、 カメラやUIのような非物理ノード を物理ノードに追従させるときは、手動の補間が要ります。アクションの主人公追従、レースの車載カメラ、見下ろしARPGの視点——どれも「プレイヤーの動きになめらかに付いていくカメラ」が欲しい場面です。

鍵は Engine.get_physics_interpolation_fraction() です。これは 今の描画フレームが、物理ティックの間のどこにいるか(0.0〜1.0) を返します。これを使って、ターゲットの「描画時点での本当の位置」を計算します。

カメラ追従の2段階補間の図。物理ティック間のターゲット位置をget_physics_interpolation_fractionで補間して「今の本当の位置」を求め(1段目)、さらにカメラ自身をその位置へsmoothingでゆっくり寄せる(2段目)流れ
# smooth_camera_2d.gd(Camera2Dにアタッチ)
extends Camera2D

@export var target: Node2D
@export var smoothing: float = 0.1     # 小さいほどゆったり追従

var _previous_target_position: Vector2

func _ready() -> void:
    if target:
        global_position = target.global_position
        _previous_target_position = target.global_position

func _process(_delta: float) -> void:
    if not target:
        return
    # 1段目:物理ティック間を補間し、ターゲットの「今の本当の位置」を求める
    var fraction := Engine.get_physics_interpolation_fraction()
    var interpolated := _previous_target_position.lerp(target.global_position, fraction)
    # 2段目:カメラ自身をその位置へゆっくり寄せる
    global_position = global_position.lerp(interpolated, smoothing)

func _physics_process(_delta: float) -> void:
    if target:
        # 次フレームの補間用に、物理ティック時点の位置を記録
        _previous_target_position = target.global_position

ポイントは2つです。

  • 2段階のlerpで滑らかさを作る :1段目で物理ティック間を補間して「ターゲットの真の現在位置」を求め、2段目でカメラをそこへゆっくり寄せます。1段目だけだと物理レートのカクつきが、2段目だけだとターゲットの急な動きが、それぞれ残ります。
  • 描画は _process、記録は _physics_process :補間の計算は毎フレーム(_process)、前回位置の記録は物理ティック(_physics_process)で行います。役割が分かれているのがミソです。

カメラの構え方そのものは Camera2D実践テクニック集 も参考になります。

Sponsored

計測してから最適化する

多数のオブジェクトでFPSが落ちると、つい「弾が多いからObject Poolingだ」と手を動かしたくなります。でも最適化の鉄則は 「推測するな、計測せよ」 です。

計測してから最適化する流れの図。まずGodotのProfiler/Monitorsでボトルネックを特定し、原因に応じてアルゴリズム改善・物理補間・Object Poolingなどの手を選ぶ。推測でいきなり最適化に飛びつかない、という流れ

Godotには デバッガーの Monitors(FPSやメモリの推移)と Profiler(各関数の処理時間)が備わっています。まずこれで どこが重いのかを特定 してから、原因に合った手を選びます。ボトルネックがノードの生成・破棄なら Object Pooling 、描画コストならシェーダーや描画数の見直し、ロード時間なら 動的ロードとリソース管理 ——と、計測結果が打ち手を教えてくれます。

特に Object Pooling は、弾やエフェクトのような 短命で高頻度に生成されるオブジェクト が計測でボトルネックだと分かったときにだけ導入します。ボスやUIのような低頻度のものにまで適用すると、コードが複雑になるだけで効果がありません(詳しい実装は Object Pooling完全ガイド を参照)。

よくある間違いとベストプラクティス

よくある間違いベストプラクティス
移動もロジックも全部 _process に書く物理的な動きは _physics_process、描画・入力は _processdelta の役割を正しく理解する
推測でObject Poolingを導入するプロファイラでボトルネックを 特定してから 最適化する
_physics_process 内で直接 queue_free() するcall_deferred("queue_free") で物理ステップ完了後に安全に削除する
ジッター対策に物理ティックレートを上げるティックレートは 固定(60Hz) のまま、ジッター対策は 物理補間 に任せる

特に 物理ステップ中の削除 は事故のもとです。衝突処理の最中にノードを消すとエラーになることがあるため、queue_free()call_deferred() 経由で予約するのが安全です。

おまけ:先に知っておくと良いこと

  • delta を必ず掛ける_process はフレームレートで呼ばれる回数が変わります。移動量に delta を掛けないと、FPSが違うPCで速さが変わってしまいます。物理補間とは別に、これは基本として押さえておきましょう。
  • モバイルは省電力とのバランス :高FPSはバッテリーを食います。モバイルでは Engine.max_fps を控えめ(30〜60)にする選択も一般的です。
  • プロファイルは「重いと感じるシーン」で :常時プロファイルする必要はありません。カクつくシーンを再現しながら計測すると、原因が素早く見つかります。

まとめ

  • カクつき(ジッター)の正体は、 固定の物理ティック可変のレンダリングフレーム のズレ
  • V-Sync はティアリング対策、Engine.max_fps はFPS固定。V-Sync Enabled時は max_fps 無視
  • ジッターは 物理補間(Physics Interpolation を On)で根絶するのが最善
  • 非物理ノード(カメラ等)の追従は get_physics_interpolation_fraction()手動補間
  • 最適化は 計測してから 。Object Poolingは短命・高頻度オブジェクトが計測でボトルネックのときだけ

まずはプロジェクト設定で 物理補間を On にして、動きが滑らかになるのを体感してみてください。多くのカクつきは、これ一つで解決します。

さらに学ぶために