【Godot】move_and_slideとmove_towardの使い分け:ノックバック処理の実装

作成: 2025-06-20最終更新: 2026-07-08

GodotのCharacterBody2Dで使うmove_and_slide()と、値を近づけるmove_toward()の違いを、ノックバック処理、壁抜けの失敗例、velocityの減衰まで含めて解説します。

プレイヤーを動かすときは move_and_slide()、HPバーをなめらかに動かすときは move_toward()。どちらも「動く」ように見えるので、最初は同じ種類の関数に見えます。

しかし実際には、役割がかなり違います。move_and_slide()キャラクターを物理ワールドの中で動かす処理move_toward()値を目標へ少しずつ近づける処理 です。

ノックバックでは、この2つを組み合わせます。壁との衝突は move_and_slide() に任せ、吹き飛びの勢いを弱めるところに move_toward() を使います。

衝撃で押されたキャラクターが壁にめり込まず、勢いを弱めながら滑るイメージ

この記事でわかること

  • move_and_slide()move_toward() の役割の違い
  • CharacterBody2Dで position を直接動かすと危ない理由
  • ノックバック中に入力を止める考え方
  • velocitymove_toward() で減衰させる実装
  • UIやカメラでは move_toward() をどう使うか

Sponsored

2つは同じ「移動関数」ではない

まず、2つの役割を分けて考えます。

左は壁に沿って滑るキャラクター、右は目標点へ少しずつ近づく球で、物理移動と値の補間の違いを示す図

move_and_slide() は、CharacterBody2Dvelocity をもとに、物理フレームで移動と衝突処理を行います。壁に当たれば止まったり、壁沿いに滑ったりします。

move_toward() は、値を目標値へ近づけるだけです。壁や床を知りません。Vector2 に使えばベクトルを近づけられますし、数値に使えばHPバーやゲージの値を近づけられます。

使うもの主な役割得意な場面
move_and_slide()物理ワールドでキャラクターを動かすプレイヤー、敵、壁との衝突、坂や床
move_toward()値を目標へ一定量近づけるvelocityの減衰、HPバー、カメラ追従、UI

つまり、ノックバックでは「どちらか一方を選ぶ」のではありません。

  • キャラクターを実際に動かす: move_and_slide()
  • ノックバックの勢いを弱める: move_toward()

この分担で考えると、コードの役割がかなり読みやすくなります。

move_and_slide()は物理キャラクターを動かす

move_and_slide() は、CharacterBody2D のための移動処理です。基本は、毎物理フレームで velocity を決めてから呼び出します。

extends CharacterBody2D

const SPEED := 220.0

func _physics_process(delta: float) -> void:
    var input_vector := Input.get_vector(
        &"move_left",
        &"move_right",
        &"move_up",
        &"move_down"
    )

    # このフレームで進みたい速度を決める
    velocity = input_vector * SPEED

    # 実際の移動と衝突処理はGodotに任せる
    move_and_slide()

Godot 4では、move_and_slide()velocity プロパティを使って移動します。壁に当たったときの滑りや衝突応答も、この呼び出しの中で処理されます。

ここで大事なのは、move_and_slide() を「座標を変える便利関数」として見るのではなく、物理ワールドへ移動を申請する処理 として見ることです。

position += direction * speed * delta のように座標を直接動かすと、物理エンジンが衝突を解決する前に位置だけが変わってしまいます。その結果、壁抜けやめり込みの原因になります。

Sponsored

move_toward()は値を目標へ近づける

move_toward() は、物理を動かす関数ではありません。現在の値を、目標値へ一定量だけ近づける関数です。

たとえば、HPバーを現在値へなめらかに追従させるなら、物理は関係ありません。

var displayed_hp := 100.0
var actual_hp := 40.0

func _process(delta: float) -> void:
    # 表示用HPを、実際のHPへ少しずつ近づける
    displayed_hp = move_toward(displayed_hp, actual_hp, 80.0 * delta)
    hp_bar.value = displayed_hp

Vector2 にも使えます。ノックバックでは、吹き飛びの速度を少しずつ Vector2.ZERO へ近づけます。

var knockback_velocity := Vector2(500.0, 0.0)

func _physics_process(delta: float) -> void:
    # ノックバックの勢いを0へ近づける
    knockback_velocity = knockback_velocity.move_toward(
        Vector2.ZERO,
        1200.0 * delta
    )

ここでは move_toward() がキャラクターを動かしているわけではありません。あくまで「次に使う速度の値」を計算しています。

実装例: ノックバックを状態で分ける

ノックバック処理で一番大事なのは、通常移動と被弾中の移動を混ぜないことです。

通常移動、のけぞり、復帰の3状態でノックバックの流れを示す図

通常時はプレイヤー入力で velocity を決めます。ノックバック中は入力を一時的に無視し、外から与えられた吹き飛び速度で動かします。

extends CharacterBody2D

enum PlayerState {
    MOVE,
    KNOCKBACK,
}

const SPEED := 220.0
const KNOCKBACK_DECAY := 900.0
const KNOCKBACK_END_SPEED := 20.0

var current_state := PlayerState.MOVE
var knockback_velocity := Vector2.ZERO

func _physics_process(delta: float) -> void:
    match current_state:
        PlayerState.MOVE:
            update_move()
        PlayerState.KNOCKBACK:
            update_knockback(delta)

    # どの状態でも、最後の移動と衝突処理はここに集める
    move_and_slide()

func update_move() -> void:
    var input_vector := Input.get_vector(
        &"move_left",
        &"move_right",
        &"move_up",
        &"move_down"
    )

    # 通常時はプレイヤー入力で速度を決める
    velocity = input_vector * SPEED

func update_knockback(delta: float) -> void:
    # ノックバック中は入力を見ず、吹き飛び速度だけを使う
    velocity = knockback_velocity

    # 次のフレームへ向けて、吹き飛びの勢いを弱める
    knockback_velocity = knockback_velocity.move_toward(
        Vector2.ZERO,
        KNOCKBACK_DECAY * delta
    )

    if knockback_velocity.length() <= KNOCKBACK_END_SPEED:
        knockback_velocity = Vector2.ZERO
        current_state = PlayerState.MOVE

func apply_knockback(source_position: Vector2, power: float) -> void:
    # 攻撃してきた位置から、自分が離れる方向を作る
    var direction := source_position.direction_to(global_position)

    knockback_velocity = direction.normalized() * power
    current_state = PlayerState.KNOCKBACK

このコードでは、move_and_slide() を最後に1回だけ呼んでいます。通常移動でもノックバックでも、最終的に velocity を決めてから物理移動へ渡す、という形です。

ノックバック中に入力を完全に無視するか、少しだけ操作を残すかはゲーム性によって変えられます。アクションRPGなら短時間だけ操作不能にすると被弾感が出ますし、ツインスティックシューターならノックバック中も少し入力を混ぜた方が気持ちよく動くことがあります。

変えるならどこか

  • 吹き飛びを強くしたい: power を大きくする
  • すぐ止まりたい: KNOCKBACK_DECAY を大きくする
  • 長くのけぞらせたい: KNOCKBACK_DECAY を小さくする
  • 小さな揺れを早めに止めたい: KNOCKBACK_END_SPEED を大きくする

数字だけを調整するより、「入力を止める時間」「勢いが弱まる速度」「復帰する条件」を別々に考えると、ゲームごとの手触りを作りやすくなります。

Sponsored

よくある失敗: positionを直接動かす

初心者がつまずきやすいのは、CharacterBody2Dを position で直接動かしてしまうことです。

position��で直接動かすと壁を抜け、velocityで動かすと壁に沿って滑る違いを示す図

次のようなコードは、見た目には動いているように見えます。

# 避けたい例: 物理キャラクターの座標を直接動かす
func _physics_process(delta: float) -> void:
    position = position.move_toward(target_position, 240.0 * delta)

しかし、これは CharacterBody2D の衝突解決を通していません。小さい移動なら気づきにくいですが、速い移動、狭い壁、フレーム落ちが重なると、めり込みや壁抜けにつながります。

物理キャラクターでは、次のように考えます。

# よい例: velocityを決めて、移動と衝突はmove_and_slide()へ任せる
func _physics_process(delta: float) -> void:
    var direction := global_position.direction_to(target_position)

    velocity = direction * 240.0
    move_and_slide()

move_toward() は悪い関数ではありません。使う場所を間違えると問題になります。UI、HPバー、カメラ、ノックバック速度の減衰には便利です。CharacterBody2Dの座標を直接動かす用途には向きません。

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

move_and_slide() の戻り値は速度ではない

Godot 4の move_and_slide() は、衝突したかどうかを表す bool を返します。速度を返す関数として扱わないようにします。

衝突後の動きは velocity プロパティや、is_on_floor()get_slide_collision_count() などを必要に応じて見ます。普段の移動では、まず「velocity を決めて move_and_slide() を呼ぶ」と覚えるのが安全です。

move_toward()lerp() は止まり方が違う

move_toward() は、指定した量だけ目標へ近づきます。一定の減り方で勢いを弱めたいノックバックに向いています。

lerp() は、現在値と目標値の割合で近づきます。目標に近づくほど変化量が小さくなるため、ふわっとした追従やUI演出に向いています。

どちらが正しいかではなく、「一定量で減らしたいか」「割合でなめらかに近づけたいか」で選びます。

強いノックバックは別の問題も出る

ノックバック速度が極端に大きいと、壁との接触、坂、狭い通路、敵との連続ヒットなど、別の設計問題が出やすくなります。

その場合は、単に power を大きくするだけでなく、無敵時間、ヒットストップ、衝突レイヤー、連続ヒット制限も一緒に考えます。ノックバックは「移動処理」だけでなく、被ダメージ演出全体の一部です。

まとめ

  • move_and_slide() は、CharacterBody2D/3Dを物理ワールドで動かす処理
  • move_toward() は、値を目標へ一定量近づける補助
  • CharacterBody2Dでは position を直接動かさず、velocity を決めて move_and_slide() に任せる
  • ノックバックでは、入力を止める状態と、吹き飛び速度を減衰させる処理を分ける
  • move_toward() は、ノックバック速度、HPバー、UI、カメラ追従などの値調整に向いている

判断に迷ったら、「これは物理ワールドで体を動かす話か、それとも値を近づける話か」と考えると整理しやすくなります。

さらに学ぶために