Godot でプレイヤーのしゃがみを実装するとき、ありがちなパターンは「入力ボタンを押している間だけしゃがむ」実装ですね。
たとえば Input.is_action_pressed("crouch") を毎フレーム監視して、押されている間だけ is_crouching = true にする、というやつです。
でも実際のゲームでは、
- 押しっぱなしではなく「押すたびにトグル(ON/OFF切り替え)」したい
- プレイヤーだけでなく、敵AIやギミックにも同じ「しゃがみトグルロジック」を使い回したい
- しゃがみ処理をプレイヤーの巨大なスクリプトにベタ書きしたくない
…という欲が出てきます。
しかし継承ベースで Player.gd を肥大化させていくと、「このプレイヤーだけ特殊挙動を…」というときに地獄が見えます。
そこで今回は、「しゃがみのトグル制御」だけを担当するコンポーネントとして、ToggleCrouch を用意してみましょう。
プレイヤーや敵など、CharacterBody2D / CharacterBody3D にこのコンポーネントをポン付けするだけで、しゃがみ/立ちのトグル制御が一気に整理されます。
【Godot 4】押すたびにしゃがみON/OFF!「ToggleCrouch」コンポーネント
今回のコンポーネントの役割はシンプルです。
- 指定した入力アクション(例:
"crouch")が 押された瞬間を検知 - 内部の
is_crouchingフラグを true / false でトグル - しゃがみ開始 / 終了時にシグナル発火(アニメーションや当たり判定の変更は接続先で処理)
「しゃがみの状態管理」と「見た目の変更(アニメーション・コリジョン変更)」を分離することで、コンポーネント指向らしいスッキリした構成にできます。
GDScript フルコード
extends Node
class_name ToggleCrouch
## 押すたびに「しゃがみ <-> 立ち」をトグルするコンポーネント
## - 入力アクション名を指定するだけで利用可能
## - しゃがみ開始/終了時にシグナルを発火
## - 実際のアニメーション・コリジョン変更は接続先のスクリプトで行う
## --- シグナル -------------------------------------------------------------
## しゃがみ状態が変化したときに発火
## is_crouching: 現在のしゃがみ状態(true: しゃがみ中, false: 立ち)
signal crouch_toggled(is_crouching: bool)
## しゃがみ開始時(true になった瞬間)に発火
signal crouch_started
## しゃがみ終了時(false になった瞬間)に発火
signal crouch_ended
## --- エクスポートパラメータ ---------------------------------------------
@export_group("Input Settings")
## しゃがみ切り替えに使う入力アクション名
## 例: "crouch" を Input Map に設定しておく
@export var action_name: StringName = &"crouch"
## 入力をポーリングするかどうか
## - true: _process() で Input.is_action_just_pressed() を監視
## - false: 外部から toggle() / set_crouching() を呼び出して制御(AI 等向け)
@export var use_input_polling: bool = true
@export_group("Behavior")
## しゃがみ状態をトグルする前に、外部から「しゃがんでよいか」を確認するためのフラグ
## 例: 空中ではしゃがみ禁止にしたい場合、外側のスクリプトから can_crouch = false にする
@export var can_crouch: bool = true
## シーン開始時に自動でしゃがみ状態にするか
@export var start_crouching: bool = false
## 連打対策用: トグル後に、次の入力を受け付けるまでのクールダウン秒数
## 0 の場合はクールダウンなし
@export var toggle_cooldown: float = 0.0
## --- 内部状態 -------------------------------------------------------------
## 現在しゃがみ中かどうか
var is_crouching: bool = false:
set(value):
if is_crouching == value:
return
is_crouching = value
# シグナル発火
emit_signal("crouch_toggled", is_crouching)
if is_crouching:
emit_signal("crouch_started")
else:
emit_signal("crouch_ended")
## クールダウン残り時間(0 以下なら入力受付可能)
var _cooldown_timer: float = 0.0
func _ready() -> void:
# シーン開始時にしゃがみ状態を初期化
is_crouching = start_crouching
func _process(delta: float) -> void:
# クールダウンを減算
if _cooldown_timer > 0.0:
_cooldown_timer -= delta
# 入力ポーリングが無効なら何もしない
if not use_input_polling:
return
# 入力アクションが設定されていない場合は何もしない
if action_name == StringName():
return
# クールダウン中は入力を無視
if _cooldown_timer > 0.0:
return
# 押された「瞬間」を検知してトグル
if Input.is_action_just_pressed(action_name):
toggle()
## しゃがみ状態をトグルする(true <-> false)
func toggle() -> void:
if not can_crouch:
return
is_crouching = not is_crouching
# クールダウンをリセット
if toggle_cooldown > 0.0:
_cooldown_timer = toggle_cooldown
## 外部から明示的にしゃがみ状態を設定したい場合に使う
## - 例: しゃがみ解除不可な演出中は強制的にしゃがませる etc.
func set_crouching(enabled: bool, force: bool = false) -> void:
# force = true のときは can_crouch やクールダウンを無視して強制変更
if not force:
if not can_crouch:
return
if _cooldown_timer > 0.0:
return
is_crouching = enabled
if toggle_cooldown > 0.0:
_cooldown_timer = toggle_cooldown
## 現在しゃがんでいるかどうかを返すヘルパー
func is_crouching_now() -> bool:
return is_crouching
使い方の手順
ここでは 2D のプレイヤーキャラを例にしますが、3D でも考え方は同じです。
手順①: 入力アクション「crouch」を設定する
- メニューから「Project > Project Settings…」を開く
Input Mapタブを選択crouchという名前でアクションを追加- キーボードの
Cやゲームパッドのボタンなど、好みのキーを割り当て
手順②: プレイヤーシーンに ToggleCrouch をアタッチ
例として、こんなシーン構成を想定します:
Player (CharacterBody2D) ├── Sprite2D ├── CollisionShape2D └── ToggleCrouch (Node)
- Player シーンを開く
- Player の子として
Nodeを追加し、名前をToggleCrouchに変更 - そのノードに、先ほどの
ToggleCrouch.gdをアタッチ - インスペクタで以下を設定
action_name:crouchuse_input_polling:On(デフォルトのままでOK)- 必要に応じて
toggle_cooldownを 0.1〜0.2 くらいにして誤入力防止
手順③: プレイヤースクリプトからシグナルを受け取る
しゃがみ状態が変わったときに、アニメーションやコリジョンを切り替えたいので、Player.gd で ToggleCrouch のシグナルを受け取ります。
extends CharacterBody2D
@onready var toggle_crouch: ToggleCrouch = $ToggleCrouch
@onready var sprite: Sprite2D = $Sprite2D
@onready var collision: CollisionShape2D = $CollisionShape2D
# しゃがみ時のコリジョンサイズなどは、あらかじめ別の Shape2D を用意しておくと楽
var _stand_shape: Shape2D
var _crouch_shape: Shape2D
func _ready() -> void:
# 元のコリジョンを保存
_stand_shape = collision.shape.duplicate()
# 簡易的にしゃがみ用コリジョンを作る(高さを半分にする例)
var rect := collision.shape as RectangleShape2D
if rect:
var crouch_rect := RectangleShape2D.new()
crouch_rect.size = Vector2(rect.size.x, rect.size.y * 0.5)
_crouch_shape = crouch_rect
else:
_crouch_shape = _stand_shape.duplicate()
# シグナル接続
toggle_crouch.crouch_started.connect(_on_crouch_started)
toggle_crouch.crouch_ended.connect(_on_crouch_ended)
func _on_crouch_started() -> void:
# 例: アニメーションを切り替える(AnimatedSprite2D を使う場合など)
# $AnimatedSprite2D.play("crouch")
# コリジョンを低くする
collision.shape = _crouch_shape
# 見た目で少し下にずらす(好みで)
sprite.position.y += 8
func _on_crouch_ended() -> void:
# $AnimatedSprite2D.play("idle") など
# コリジョンを元に戻す
collision.shape = _stand_shape
# 位置を元に戻す
sprite.position.y -= 8
func _physics_process(delta: float) -> void:
# しゃがみ中は移動速度を落とす例
var input_dir := Input.get_axis("move_left", "move_right")
var speed := 200.0
if toggle_crouch.is_crouching_now():
speed = 80.0
velocity.x = input_dir * speed
move_and_slide()
このように、「しゃがみ状態をトグルするロジック」は ToggleCrouch に閉じ込めておき、
プレイヤー本体は「しゃがんだときに何をするか」だけに集中させる構成にできます。
手順④: 敵や動く床にも再利用する
同じコンポーネントを、敵キャラやギミックにも使い回せます。
例: 敵が一定間隔でしゃがみ/立ちを繰り返すトラップ
EnemyTurret (CharacterBody2D) ├── Sprite2D ├── CollisionShape2D └── ToggleCrouch (Node)
extends CharacterBody2D
@onready var toggle_crouch: ToggleCrouch = $ToggleCrouch
var _timer := 0.0
var interval := 1.5 # 1.5 秒ごとにしゃがみ/立ち切り替え
func _ready() -> void:
# 入力ポーリングは不要なので無効化
toggle_crouch.use_input_polling = false
toggle_crouch.crouch_toggled.connect(_on_crouch_toggled)
func _process(delta: float) -> void:
_timer += delta
if _timer >= interval:
_timer = 0.0
# AI からトグル操作
toggle_crouch.toggle()
func _on_crouch_toggled(is_crouching: bool) -> void:
if is_crouching:
# しゃがみ中は攻撃しない、コリジョンを小さくする、など
pass
else:
# 立ち状態で攻撃可能にする、など
pass
このように、「しゃがみのトグルという概念」だけを共有できるのがコンポーネント指向の気持ちよさですね。
メリットと応用
ToggleCrouch コンポーネントを導入することで、次のようなメリットがあります。
- プレイヤーのスクリプトがスリムになる
「しゃがみロジック」「入力のトグル処理」「クールダウン管理」などをコンポーネント側に隔離できるので、Player.gdは「動き」と「演出」に集中できます。 - 継承ではなく合成で機能追加できる
「しゃがみが必要なキャラ」にToggleCrouchをポン付けするだけで、トグルしゃがみ機能を獲得できます。
ベースクラスをいじったり、継承ツリーを増やしたりする必要がありません。 - シーン構造がシンプルで見通しが良い
「このキャラはしゃがみ機能を持っているか?」が、
シーンツリーを見れば一目でわかります(ToggleCrouchノードがあるかどうか)。 - AI / ネットワーク / リプレイなどへの応用がしやすい
use_input_polling = falseにすれば、「外部からトグル命令を送るだけ」の抽象インターフェースとして使えます。
たとえばネットワーク同期で「しゃがみ状態のON/OFF」だけを送る、などもしやすいです。
応用として、例えば「しゃがみ中だけ特定のアクションを禁止する」ロジックを、プレイヤー側に少し足してみましょう。
func can_attack() -> bool:
# しゃがみ中は攻撃禁止にする例
if toggle_crouch.is_crouching_now():
return false
# ここに他の条件(スタミナ、クールダウンなど)を足していく
return true
このように、「しゃがみ状態」という一つの軸をコンポーネントで提供しておけば、
それを参照するだけで他のシステム(攻撃、移動、AI など)を簡単に連携させられます。
継承で巨大なプレイヤークラスを作るのではなく、
「しゃがみ」「ダッシュ」「二段ジャンプ」…といった機能を小さなコンポーネントとして積み上げていくと、
プロジェクト後半になってからの拡張や調整が圧倒的に楽になりますよ。




