1. はじめに
Godot 4ではキャラクター移動に大きな変更が加えられました。旧KinematicBody2DとKinematicBody3DノードはCharacterBody2DとCharacterBody3Dに改名され、move_and_slide() APIは完全に再設計されました。Godot 3からの移行でも、新規開始でも、このガイドで必要な情報を全てカバーします。
CharacterBody2DとCharacterBody3Dはコードで制御されるキャラクター用の物理ボディです。RigidBodyとは異なり、力に自動的に反応せず、移動はすべてスクリプトで制御します。そのため、プレイヤーキャラクター、NPC、そして正確で決定論的な動きが必要なすべてのものに最適です。
2. Godot 3からの変更点
Godot 3からGodot 4でのキャラクター移動における主要な変更点は以下の通りです:
-
KinematicBody2DがCharacterBody2Dに変更 -
KinematicBody3DがCharacterBody3Dに変更 -
velocityは組み込みプロパティになりました —move_and_slide()に渡す必要はありません -
move_and_slide()は引数なしになりました —velocityプロパティから直接読み取ります -
move_and_slide_with_snap()は削除されました — 代わりにfloor_snap_lengthプロパティを使用 -
is_on_floor()、is_on_wall()、is_on_ceiling()は従来通り動作します -
get_gravity()がGodot 4.4で追加され、プロジェクトのデフォルト重力をVectorとして取得できるようになりました
Godot 3ではmove_and_slide()は結果の速度を返していました。Godot 4ではbool(衝突が発生したかどうか)を返し、velocityはvelocityプロパティ上で直接更新されます。
3. 基本的な2Dプラットフォーマーコントローラー
これは標準的な横スクロールプラットフォーマーコントローラーです。キャラクターは左右に移動し、地面にいるときにジャンプできます。これはGodotが新しいCharacterBody2Dスクリプトを作成する際に生成するテンプレートです。
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
func _physics_process(delta: float) -> void:
# Apply gravity when not on floor
if not is_on_floor():
velocity += get_gravity() * delta
# Jump when on floor and jump pressed
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Horizontal movement
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
1行ずつの解説
-
extends CharacterBody2D— CharacterBody2Dを継承し、velocity、move_and_slide()、is_on_floor()等にアクセスできます。 -
SPEED = 300.0— 水平方向の移動速度(ピクセル/秒)。 -
JUMP_VELOCITY = -400.0— 2DではY軸が下向きなので負の値。負の値でキャラクターが上方向に移動します。 -
get_gravity() * delta— 毎フレーム重力を適用します。get_gravity()は下向きのVector2(例:Vector2(0, 980))を返します。deltaを掛けることでフレームレートに依存しなくなります。 -
Input.get_axis("ui_left", "ui_right")— 押されている方向キーに応じて-1.0から1.0のfloatを返します。アナログ入力にも対応。 -
move_toward(velocity.x, 0, SPEED)— 入力がないとき滑らかにゼロまで減速します。3番目の引数は1回の呼び出しでの最大変化量です。 -
move_and_slide()— 現在のvelocityを使ってボディを移動し、衝突を処理し、面に沿ってスライドし、velocityを自動的に更新します。
CharacterBody2Dには少なくとも1つのCollisionShape2D子ノードに形状(RectangleShape2DやCapsuleShape2D等)を設定する必要があります。これがないとmove_and_slide()は衝突を検出しません。
4. 基本的な2Dトップダウンコントローラー
トップダウンゲーム(RPG、ツインスティックシューター等)では、重力なしで4方向に移動します。プラットフォーマーよりコードはシンプルです。
extends CharacterBody2D
const SPEED = 200.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input_direction * SPEED
move_and_slide()
仕組み
-
Input.get_vector()は4つの入力アクションから正規化されたVector2を返します。斜め移動が正しく処理され、ベクトルの長さは常に1.0または0.0なので、斜め移動が縦横移動より速くなりません。 -
トップダウンビューなので重力は適用されません。
velocityを直接設定するため(慣性なし)、入力がないときキャラクターは即座に停止します。
加速と摩擦の追加
加速と減速でより滑らかな操作感にするには:
extends CharacterBody2D
const SPEED = 200.0
const ACCELERATION = 1200.0
const FRICTION = 1000.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if input_direction != Vector2.ZERO:
velocity = velocity.move_toward(input_direction * SPEED, ACCELERATION * delta)
else:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
move_and_slide()
5. 3Dサードパーソンコントローラー
3D版も同じパターンに従います。主な違いはVector3を使用し、transform.basisで2D入力を3Dワールド空間の移動に変換する点です。
extends CharacterBody3D
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity += get_gravity() * delta
# Jump
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get input and convert to 3D direction
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
2Dとの主な違い
-
単位: 3Dでは1単位 = 1メートル(慣例)。
SPEED = 5.0は秒速5メートルを意味し、2Dのピクセル値よりずっと小さい数値です。 -
ジャンプ速度は正: 3DではY軸が上向き(2Dとは逆)なので、
JUMP_VELOCITY = 4.5は正の値です。 -
方向変換:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)は2D入力をキャラクターの向きに対する3D方向に変換します。入力のYがZ軸(3Dの前後)にマッピングされます。 -
XとZのみ制御:
velocity.xとvelocity.zを個別に設定し、velocity.yは重力とジャンプ用に残します。
get_gravity()メソッドはGodot 4.4で追加されました。古いバージョンでは、2Dの場合はVector2(0, ProjectSettings.get_setting("physics/2d/default_gravity"))、3Dの場合はVector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0)を使用してください。
6. 主要プロパティリファレンス
CharacterBody2DとCharacterBody3Dの両方が共有する重要なプロパティです。インスペクタまたはコードで調整できます。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
キャラクターの速度。move_and_slide()を呼ぶ前に設定。呼び出し後は結果の速度で更新されます。
|
floor_snap_length |
float | 1.0 |
move_and_slide_with_snap()の代替。キャラクターを床にスナップする距離。0で無効化。坂道や階段に便利。
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
「上」の方向を定義。これにより床・壁・天井の判定が決まります。デフォルトは2Dで(0, -1)、3Dで(0, 1, 0)。
|
floor_stop_on_slope |
bool | true |
trueの場合、静止時にキャラクターが坂道で滑りません。プラットフォーマーに必須。 |
floor_max_angle |
float | 0.785 (45°) |
歩行可能な最大勾配角度(ラジアン)。これより急な面は壁として扱われます。60度に設定するにはdeg_to_rad(60)を使用。
|
max_slides |
int | 6 |
move_and_slide()呼び出しごとの最大衝突反復回数。値が大きいほど正確だが遅くなります。
|
wall_min_slide_angle |
float | 0.262 (15°) |
壁スライドの最小角度。ほぼ平行な壁にキャラクターが引っかかるのを防ぎます。 |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
移動プラットフォームを離れた時の挙動。ADD_VELOCITYで運動量を保存、ADD_UPWARD_VELOCITYで上方成分のみ追加、DO_NOTHINGでプラットフォームの速度を無視。
|
slide_on_ceiling |
bool | true |
trueの場合、天井でのスライドを許可。falseの場合、天井に当たると水平速度が停止。 |
7. move_and_slide()後の衝突検出
move_and_slide()を呼んだ後、発生した衝突を検査できます。特定のコライダータイプへの反応、着地時のサウンドエフェクト再生、壁ジャンプの実装などに便利です。
func _physics_process(delta: float) -> void:
# ... set velocity here ...
move_and_slide()
# Check all collisions from this frame
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
var collider := collision.get_collider()
print("Collided with: ", collider.name)
print("Normal: ", collision.get_normal())
print("Position: ", collision.get_position())
# Practical example: bounce off enemies
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider().is_in_group("enemies"):
velocity = collision.get_normal() * 300
break
便利なKinematicCollisionメソッド
get_collider()— ヒットしたオブジェクト。get_normal()— 衝突面の法線。面から離れる方向。get_position()— 衝突が発生したワールド空間上の点。get_travel()— 衝突前にボディが移動した距離。get_remainder()— 適用されなかった残りのモーション。get_collider_velocity()— コライダーの速度(移動プラットフォームに便利)。
8. 移行チートシート
Godot 3からGodot 4への変換クイックリファレンス:
| Godot 3 | Godot 4 |
|---|---|
KinematicBody2D |
CharacterBody2D |
KinematicBody3D |
CharacterBody3D |
velocity = move_and_slide(velocity, Vector2.UP) |
velocity = ... |
move_and_slide_with_snap(vel, snap, up) |
floor_snap_length = 4.0 |
var vel = move_and_slide(...)velocityを返す |
move_and_slide()velocityプロパティが直接更新される |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (プロパティとして設定) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (プロパティとして設定) |
Godot 4でGodot 3プロジェクトを開くと、エンジンがプロジェクトの変換を提案します。ノードの名前変更やスクリプトの更新を試みますが、move_and_slide()の呼び出しは必ずしも正しく変換されません。移動コードの手動修正が必要になることが多いです。
9. よくある間違い
間違い1: move_and_slide()に引数を渡す
# ERROR: move_and_slide() takes no arguments in Godot 4
velocity = move_and_slide(velocity, Vector2.UP)
# Set velocity, then call move_and_slide() with no arguments
velocity.x = direction * SPEED
move_and_slide()
間違い2: move_and_slide()の前にvelocityを設定し忘れる
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
間違い3: _physics_process()の代わりに_process()を使用
move_and_slide()は固定レート(デフォルト60Hz)で実行される_physics_process()内で呼ぶ必要があります。_process()を使うと物理がレンダリングフレームレートに連動し、挙動が不安定になります。
間違い4: CollisionShapeを設定していない
CollisionShape子ノードのないCharacterBody2D/CharacterBody3Dはすべてを通り抜けます。衝突形状がない場合、エディタに警告アイコンが表示されます。
間違い5: 毎フレームvelocity.yを重力で上書きする
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
間違い6: 床の上にいるとき重力を適用する
重力は必ずif not is_on_floor()で囲んでください。このチェックがないと、床の上でも重力が蓄積し続けます。キャラクターが崖から歩いて落ちるとき、自然に落下する代わりに極端な速度で急降下します。