「V-Syncを入れたのに滑らかにならない」「多 数の敵を出した瞬間だけカクつく」「_process と _physics_process の使い分けが曖昧」——フレームレートまわりの悩みは、Godotの 描画と物理演算が別々のサイクルで動いている ことを掴むと、驚くほどスッキリ解決します。
この記事では、まずカクつき(ジッター)の 根本原因 を押さえ、V-Syncと max_fps によるフレームレート制御、ジッターを根絶する 物理補間 、そして「計測してから最適化する」考え方まで、図とコードで整理します。
この記事でわかること
- カクつきの正体—— 物理ティック と レンダリングフレーム のズレ
- V-Sync と
Engine.max_fpsによるフレームレート制御- 物理補間 でジッターを根絶する(設定1つ+手動補間)
- 「推測するな、計測せよ」——プロファイラに基づく最適化の順番
なぜカクつくのか:物理ティック vs レンダリングフレーム
Godotのパフォーマンスを理解する鍵は、 物理ティック(Physics Tick) と レンダリングフレーム(Rendering Frame) という、独立した2つのサイクルを区別することです。この2つが 別々の速さで動く ことが、ジッターの直接の原因です。

| 物理ティ ック | レンダリングフレーム | |
|---|---|---|
| 役割 | 物理演算・衝突判定(_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 で設定します。

| モード | 概要 | 向いている場面 |
|---|---|---|
| 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 は無視される 点に注意してください。
物理補間でジッターを根絶する
フレームレートを制御しても、物理とレンダリングの ズレそのもの は残ります。それを埋めるのが 物理補間(Physics Interpolation) です。固定された物理ティックの 間の位置を、描画時になめらかに補間 することで、階段状の動きを消します。ジッター対策として最も効果的です。

プロジェクト設定で有効化(推奨)
Godot 4.3以降、2D・3Dの両方で物理補間が標準サポートされています。多くの場合、次の設定だけで解決します。
- プロジェクト設定 を開く
- Physics > Common > Physics Interpolation を
Onにする
これで CharacterBody2D/3D や RigidBody2D/3D など、物理の影響を受けるノードの動きが自動で補間され、なめらかになります。
注意: 物理補間を有効にすると、ノードの位置は「補間された見た目の位置」で描画されます。物理ティックの合間に
global_positionを直接いじると補間と競合するので、 移動は_physics_process内で行う のが基本です。
実践:カクつかないカメラ追従を組む
物理補間は物理ノードには自動で効きますが、 カメラやUIのような非物理ノード を物理ノードに追従させるときは、手動の補間が要ります。アクションの主人公追従、レースの車載カメラ、見下ろしARPGの視点——どれも「プレイヤーの動きになめらかに付いていくカメラ」が欲しい場面です。
鍵は Engine.get_physics_interpolation_fraction() です。これは 今の描画フレームが、物理ティックの間のどこにいるか(0.0〜1.0) を返します。これを使って、ターゲットの「描画時点での本当の位置」を計算します。

# 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実践テクニック集 も参考になります。
計測してから最適化する
多数のオブジェクトでFPSが落ちると、つい「弾が多いからObject Poolingだ」と手を動かしたくなります。でも最適化の鉄則は 「推測するな、計測せよ」 です。

Godotには デバッガーの Monitors(FPSやメモリの推移)と Profiler(各関数の処理時間)が備わっています。まずこれで どこが重いのかを特定 してから、原因に合った手を選びます。ボトルネックがノードの生成・破棄なら Object Pooling 、描画コストならシェーダーや描画数の見直し、ロード時間なら 動的ロードとリソース管理 ——と、計測結果が打ち手を教えてくれます。
特に Object Pooling は、弾やエフェクトのような 短命で高頻度に生成されるオブジェクト が計測でボトルネックだと分かったときにだけ導入します。ボスやUIのような低頻度のものにまで適用すると、コードが複雑になるだけで効果がありません(詳しい実装は Object Pooling完全ガイド を参照)。
よくある間違いとベストプラクティス
| よくある間違い | ベストプラクティス |
|---|---|
移動もロジックも全部 _process に書く | 物理的な動きは _physics_process、描画・入力は _process。delta の役割を正しく理解する |
| 推測で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 にして、動きが滑らかになるのを体感してみてください。多くのカクつきは、これ一つで解決します。
さらに学ぶために
- _processと_physics_processの正しい使い分け ——deltaと2つの更新サイクルの基礎
- Object Pooling完全ガイド ——生成・破棄コストを抑える具体的な実装
- Camera2D実践テクニック集 ——追従カメラの構え方
- Godot公式ドキュメント:Physics interpolation ——物理補間の一次情報