Godotのリストに戻る

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

作成: 2025-06-20更新: 2025-06-20移動・物理ブログ記事を読む

概要

Godotの移動処理では、move_and_slide()で物理的な移動を行い、move_toward()で速度の変化を制御します。特にノックバック処理では、この組み合わせが効果的です。

ノックバックの実装で最初につまずいたのは、「敵に当たった瞬間に吹き飛ばされるけど、すぐに通常速度に戻ってしまう」という問題でした。move_toward()を使うことで、「じわじわと復帰する」感覚が出せるようになり、ゲームの手触りが格段に良くなりました。

Godotのノックバック処理

各関数の役割

move_and_slide()

  • 役割: CharacterBody2Dの主力移動関数
  • 機能: velocityに基づいて移動し、衝突を自動処理
  • 特徴: 壁や床でスライド、物理演算を考慮

move_toward(current, target, delta)

  • 役割: 現在値から目標値へ段階的に変化
  • 機能: 指定した量(delta)だけ目標に近づく
  • 用途: 滑らかな加速・減速、慣性の表現

なぜノックバック処理にmove_towardが必要か

問題:直接代入では効果が消える

# 問題のあるコード
func move_player():
    var move_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    velocity = move_vector * move_speed  # 直接代入
    # ノックバック後、入力があると即座に通常速度に戻る

func apply_knockback(direction: Vector2, strength: float):
    velocity += direction * strength  # ノックバック適用

解決:move_towardで段階的に復帰

@export var acceleration: float = 500.0  # 1秒間に500ピクセル/秒の加速

func move_player():
    var move_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    var target_velocity = move_vector * move_speed
    # 段階的変化:現在の速度から目標速度へ少しずつ近づく
    velocity = velocity.move_toward(target_velocity, acceleration * delta)

func apply_knockback(direction: Vector2, strength: float):
    velocity += direction * strength  # velocityに直接力を加える

具体的な動作例

ノックバック時のフレーム単位の変化

初期状態: velocity = (100, 0)  # 右に移動中
攻撃直後: velocity = (300, 0)  # 右に吹き飛ばされる
以降のフレーム(acceleration = 500, delta = 0.016秒):
- フレーム1: velocity = (292, 0)
- フレーム2: velocity = (284, 0)
- フレーム3: velocity = (276, 0)
- ...約25フレーム後: velocity = (100, 0)  # 通常速度に復帰

完全な実装例

プレイヤーの移動とノックバック

extends CharacterBody2D

@export var move_speed = 200.0
@export var acceleration = 500.0
@export var knockback_strength = 300.0

func _physics_process(delta):
    # 移動処理
    move_player(delta)
    # 物理演算を適用
    move_and_slide()

func move_player(delta):
    var input_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    var target_velocity = input_vector * move_speed
    
    # move_towardで段階的に目標速度へ
    velocity = velocity.move_toward(target_velocity, acceleration * delta)

func take_damage(damage_amount: int, from_position: Vector2):
    # ダメージ処理
    health -= damage_amount
    
    # ノックバック方向を計算
    var knockback_direction = global_position - from_position
    knockback_direction = knockback_direction.normalized()
    
    # ノックバックを適用
    apply_knockback(knockback_direction, knockback_strength)

func apply_knockback(direction: Vector2, strength: float):
    velocity += direction * strength

敵AIでの活用

# Enemy.gd
extends CharacterBody2D

@export var chase_speed = 150.0
@export var chase_acceleration = 300.0
var target: Node2D = null

func _physics_process(delta):
    if target:
        chase_target(delta)
    move_and_slide()

func chase_target(delta):
    var direction = global_position.direction_to(target.global_position)
    var target_velocity = direction * chase_speed
    # ノックバック効果を保持しつつ、段階的に追跡速度へ変化
    velocity = velocity.move_toward(target_velocity, chase_acceleration * delta)

move_towardの3つの効果

  1. ノックバック保持: 即座に消えず、段階的に復帰
  2. 制御可能な復帰速度: accelerationで調整可能
  3. 滑らかな操作感: 急激な方向転換を避け、慣性を表現

パラメータ調整の指針

パラメータ効果推奨値
acceleration(小)ゆっくり復帰、重い動き200-400
acceleration(中)バランス型400-800
acceleration(大)素早い復帰、機敏な動き800-1500

トラブルシューティング

ノックバックが効かない

  • velocityの直接代入を避ける
  • move_towardを使用しているか確認

動きがカクカクする

  • deltaを掛け忘れていないか確認
  • accelerationの値が大きすぎないか確認

実装して学んだこと

move_toward()の存在を知るまでは、ノックバックの実装に本当に苦労しました。「一瞬だけ吹き飛び、すぐに通常移動に戻る」という動きは、プレイ感覚を損ないます。

特に印象的だったのは、accelerationの値を調整することで、キャラクターの「重さ」を表現できることです。重たいキャラクターは小さいacceleration、軽いキャラクターは大きいaccelerationとすることで、ゲームプレイに多様性が生まれます。

また、この手法はノックバックだけでなく、氷上での滑りやすい動き、風の影響など、様々な環境効果にも応用できることがわかりました。