Godot 4のコンポーネント指向開発シリーズ、今回は操作感(Game Feel)の要となる**「SurfaceFriction (摩擦制御)」**です。

アクションゲームで、キーを離した瞬間にピタッと止まるキャラと、少し滑って止まるキャラでは、プレイ感覚が全く違います。このコンポーネントを使えば、「氷の床」や「泥沼」といった地形による滑り具合を、移動コードとは独立して管理できるようになります。


このコンポーネントは、親ノードが「床にいる時」だけ、横方向の速度(velocity.x)を徐々に 0 に近づけます。つまり、ブレーキ担当の部品です。

1. コンポーネントのコード (Full Code)

以下のコードをコピーして、SurfaceFriction.gd という名前で保存してください。

class_name SurfaceFriction
extends Node

## 床の上にいる時、自動でブレーキ(摩擦)をかけて減速させるコンポーネント
## 親ノードは CharacterBody2D を想定しています。

# --- 設定パラメータ ---
@export_group("Friction Settings")
@export var friction: float = 1000.0  ## 摩擦力(1秒間に減速する速度量)
@export var enabled: bool = true      ## 有効/無効の切替(空中などでOFFにしたい場合用)

# --- 内部変数 ---
var _parent: CharacterBody2D

func _ready() -> void:
	_parent = get_parent() as CharacterBody2D
	if not _parent:
		push_error("SurfaceFriction: 親が CharacterBody2D ではありません。")
		set_physics_process(false)

func _physics_process(delta: float) -> void:
	if not enabled: return
	
	# 床にいて、かつ速度が出ている場合のみ処理
	if _parent.is_on_floor() and _parent.velocity.x != 0:
		
		# move_toward は、現在の値を目標値(0)に向かって、指定量(friction * delta)だけ近づける関数
		# これにより、滑らかかつ正確に0で止まります。
		_parent.velocity.x = move_toward(_parent.velocity.x, 0, friction * delta)

2. 使い方チュートリアル

このコンポーネントの最大の利点は、**移動スクリプトから「止まる処理」を削除できる(または上書きできる)**点です。

手順①:プレイヤーの準備

  1. CharacterBody2D のプレイヤーを用意します。
  2. 移動用のコンポーネント(KeyboardMoverなど)がついている前提で進めます。

手順②:移動スクリプトの修正(重要!)

もし、移動用のスクリプト(KeyboardMoverなど)の中に「キーを離したら velocity = Vector2.ZERO にする」という記述があると、このコンポーネントの出番がありません(一瞬で止まってしまうため)。

摩擦コンポーネントを活かすには、移動側は「加速」だけを担当し、「減速」はこのコンポーネントに任せる構成が理想です。

(例:KeyboardMoverのパラメータ調整)

第1回で紹介した KeyboardMover を使っている場合、そのインスペクター設定にある Friction を 0 にしてください。

そうすると KeyboardMover はブレーキをかけなくなり、代わりに今回追加する SurfaceFriction がブレーキを担当するようになります。

手順③:コンポーネントのアタッチ

  1. プレイヤーの子ノードとして Node を追加し、名前を SurfaceFriction にします。
  2. スクリプト SurfaceFriction.gd をアタッチします。

シーン構成図:

Player (CharacterBody2D)
 ├── Sprite2D
 ├── CollisionShape2D
 ├── KeyboardMover (加速担当: Frictionは0にしておく)
 └── SurfaceFriction (減速担当) <-- 今回追加!

手順④:パラメータ調整と実行

インスペクターで Friction の値をいじってテストしてみてください。

  • Friction = 1000: 通常のアスファルトや土の上。キュッと止まります。
  • Friction = 100: 氷の上。キーを離してもツルツルと長く滑ります。
  • Friction = 3000: 粘着床や泥沼。一瞬で止まります。

3. 応用:地形ごとに滑り具合を変える

このコンポーネントを独立させたことで、**「エリアに入ったら摩擦を変える」**というギミックが簡単に作れます。

「氷の床エリア」を作る

Area2D を使い、プレイヤーが入ってきたら SurfaceFriction の値を書き換えるギミックです。

# IceZone.gd (氷エリアのスクリプト)
extends Area2D

func _on_body_entered(body: Node2D):
    # プレイヤーが持つ摩擦コンポーネントを探す
    var friction_comp = body.get_node_or_null("SurfaceFriction")
    if friction_comp:
        friction_comp.friction = 100.0 # ツルツルにする!

func _on_body_exited(body: Node2D):
    var friction_comp = body.get_node_or_null("SurfaceFriction")
    if friction_comp:
        friction_comp.friction = 1000.0 # 普通に戻す

これを床に配置するだけで、プレイヤーの移動コードを一切書き換えることなく、スーパーマリオのような「氷のステージ」が実装できます。

空中制御との兼ね合い

今回のコードは is_on_floor() のチェックを入れているため、**「空中では空気抵抗(摩擦)がかからない」**ようになっています。

これにより、「ジャンプ中は慣性が残り、着地した瞬間にブレーキがかかる」という、アクションゲームとして自然な挙動が自動的に実現できています。