概要
Godot Engineでゲーム開発を始める際、誰もが最初に触れることになるのが、ノードの動作を制御するための2つの主要なコールバック関数、_process(delta) と _physics_process(delta) です。
しかし、この2つの関数の違いと、それぞれをいつ、どのように使うべきかを正しく理解していないと、ゲームの動作が不安定にな ったり、物理演算が不正確になったり、パフォーマンスが低下したりといった問題に直面します。
この記事では、Godotのゲームループの基本を丁寧に解説し、初心者が陥りがちな間違いを指摘しながら、安定したゲーム開発のためのベストプラクティスを具体的なコード例と共に紹介します。
基本概念の解説:可変フレームと固定フレーム
_processと_physics_processの最も重要な違いは、呼び出される頻度とタイミングにあります。これは、ゲーム開発における「可変タイムステップ」と「固定タイムステップ」という根本的な概念に基づいています。
_process(delta): 可変タイムステップ(Variable Timestep)
- 呼び出し頻度: 可能な限り頻繁に、毎フレーム呼び出されます。
- 用途:
- 描画(レンダリング)に直接関連する処理。
- ユーザーインターフェース(UI)の更新。
- 入力処理(
Input.is_action_pressed()など)。 - 物理演算に影響しない、滑らかなアニメーションや視覚効果。
delta引数: 前のフレームからの経過時間(秒)が入ります。フレームレートが変動するため、このdeltaの値も変動します。
_physics_process(delta): 固定タイムステップ(Fixed Timestep)
- 呼び出し頻度: ゲームのフレームレートに関係なく、一定の間隔(デフォルトでは1秒間に60回、つまり0.0166秒ごと)で呼び出されます。
- 用途:
- 物理演算(
move_and_slide()など、CharacterBody2D/3Dの移動処理)。 - 衝突判定やレイキャストなど、正確なシミュレーションが求められる処理。
- ゲームロジックのうち、時間経過に厳密である必要があるもの(タイマー、クールダウンなど)。
- 物理演算(
delta引数: 常に固定値(デフォルトでは1/60秒)が入ります。
| 特徴 | _process(delta) | _physics_process(delta) |
|---|---|---|
| 呼び出し頻度 | 毎フレーム(可変) | 固定間隔(デフォルト60回/秒) |
| タイムステップ | 可変タイムステップ | 固定タイムステップ |
deltaの値 | 変動する | 固定(デフォルト 0.0166...秒) |
| 主な用途 | 描画、UI、入力、非物理アニメーション | 物理演算、衝突判定、厳密なゲームロジック |
よくあるつまずきポイントと間違い
初心者がこの2つの関数 で最もつまずきやすいのは、「すべての処理を_processに書いてしまう」ことです。
間違い1: 物理演算を_processで行う
キャラクターの移動処理(move_and_slide()など)を_processに記述すると、プレイヤーのPCの性能やフレームレートによって、キャラクターの移動速度や挙動が変わってしまう可能性があります。
- 低フレームレートの場合:
_processの呼び出し回数が減り、キャラクターの動きがカクカクしたり、衝突判定が抜けたりする(トンネリング現象)リスクが高まります。 - 高フレームレートの場合:
_processの呼び出し回数が増え、キャラクターが意図したよりも速く移動してしまう可能性があります(deltaで補正されますが、物理演算の安定性は固定ステップに劣ります)。
さらに、_process内で物理演算を行うと、物理サーバーが想定している「固定ステップ」の前提が崩れます。たとえdeltaを掛けて速度を補正していても、衝突判定やレイキャストの結果がフレームレートに依存しやすくなり、テスト環境と実行環境で挙動がズレる原因になります。
たとえば 144Hz モニター環境と 60Hz モニター環境で、同じコードでも「当たりやすさ」や「すり抜けやすさ」が変わる、といった現象が起こり得ます。
間違い2: deltaを使わずに移動量を計算する
_process内で移動処理を行う際、delta引数を使わずに移動量を固定値で計算すると、フレームレートが変動したときにキャラクターの速度が大きく変わってしまいます。
# 間違いの例: _processでdeltaを使わない
func _process(delta):
# フレームレートが上がると1秒あたりの実行回数が増え、プレイヤーは速く動く
position.x += 5
_processを使う場合は、必ずdeltaを乗算してフレームレートに依存しない速度を保証する必要があります。
ベストプラクティスと実践例
安定したゲーム動作を実現するための、_processと_physics_processの正しい使い分けを学びましょう。
物理演算と入力処理の分離
物理演算は _physics_process に、入力の取得は _process で行い変数に保存する、というのはよく使われるパターンです。シンプルなゲームでは_physics_process内で直接入力を取得することもありますが、処理の役割を分けておくと後からのリファクタリングやデバッグがしやすくなるため、ここでは分離した書き方を紹介します。
プレイヤー移動のベストプラクティス
extends CharacterBody2D
const SPEED = 300.0
var direction = Vector2.ZERO # 入力方向を保存する変数
# 1. 入力処理: 毎フレーム、ユーザー入力を取得する
func _process(delta):
# UIやアニメーションなど、描画に直結する処理もここ
# 入力を取得し、変数に保存
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
# 2. 物理処理: 固定ステップで、物理演算を実行する
func _physics_process(delta):
# 保存された入力方向を使って速度を計算
velocity = direction * SPEED
# move_and_slide()は物理演算なので、必ず_physics_process内で行う
move_and_slide()
この方法により、入力は滑らかに(毎フレーム)取得され、物理演算は安定した固定ステップで実行されるため、フレームレートに依存しない一貫した挙動が保証されます。
視覚的な滑らかさのための補間(Interpolation)
物理演算は固定ステップ(例: 60TPS)で実行されますが、描画は可変ステップ(例: 120FPS)で実行されるため、描画フレームの間に物理オブジェクトの位置が更新されない瞬間が生じ、カクつきに見えることがあります。
Godot 4.3以降では、このジッターを自動的に解消する**物理補間(Physics Interpolation)**機能が標準搭載されています。
有効化方法:
- Project > Project Settings を開く
- Physics > Common > Physics Interpolation を有効化(チェックを入れる)
この設定を有効にすると、_physics_processで計算された位置が描画フレーム間で自動的に補間され、滑らかな動きが実現します。
注意: 2Dの物理補間は Godot 4.3以降 で利用可能です。4.0〜4.2では3Dのみ対応しており、2Dには外部アドオン(Smootherなど)が必要でした。
まとめ
_processと_physics_processの使い分けは、Godot Engineにおける安定性とパフォーマンスの鍵です。
| 関数 | タイムステップ | 使うべき処理 | 避けるべき処理 |
|---|---|---|---|
_process | 可変(Variable) | UI更新、入力取得、非物理アニメーション、描画処理 | 物理演算、衝突判定、厳密な時間管理 |
_physics_process | 固定(Fixed) | 物理演算(move_and_slide)、衝突判定、タイマー、ゲームロジック | 描画に直結する処理(パフォーマンス上の理由) |
次にGodotでコードを書くときは、その処理が「描画に依存するか」それとも「物理に依存するか」を自問自答し、適切な関数を選択してください。