Godot 4のコンポーネント指向開発シリーズ、今回はゲームの見た目と手触りを劇的に良くする**「SquashStretch (伸縮演出)」**です。
「アニメーションの12原則」の一つにも数えられるこの技術は、キャラクターがジャンプした瞬間に**「縦に伸び」、着地した瞬間に「横に潰れる」**演出を加えます。これを入れるだけで、あなたのキャラクターはまるでゴムボールのような弾力と生命力を持ちます。
このコンポーネントは、親ノード(キャラクター)の状態を監視し、ジャンプや着地のタイミングに合わせて、スプライト(画像)のスケールを動的に変化させます。
1. コンポーネントのコード (Full Code)
以下のコードをコピーして、SquashStretch.gd という名前で保存してください。
class_name SquashStretch
extends Node
## ジャンプや着地時にスプライトを伸縮(スカッシュ&ストレッチ)させるコンポーネント
## 注意: 親のCharacterBody2D自体ではなく、その子にある「Sprite2D」を操作します。
## (PhysicsBody自体を変形させると当たり判定がおかしくなるため)
# --- 設定パラメータ ---
@export_group("Deformation Settings")
@export var stretch_strength: Vector2 = Vector2(0.6, 1.4) ## ジャンプ時の伸び率 (幅, 高さ)
@export var squash_strength: Vector2 = Vector2(1.4, 0.6) ## 着地時の潰れ率 (幅, 高さ)
@export var recovery_speed: float = 10.0 ## 元の形(1,1)に戻る速さ
# --- 内部変数 ---
var _parent: CharacterBody2D
var _sprite: Node2D
var _was_on_floor: bool = true
func _ready() -> void:
# 親を取得
_parent = get_parent() as CharacterBody2D
if not _parent:
push_error("SquashStretch: 親が CharacterBody2D ではありません。")
set_physics_process(false)
return
# 操作対象のスプライトを探す
# 基本的に「Sprite2D」または「AnimatedSprite2D」という名前の子ノードを探します
_sprite = _parent.get_node_or_null("Sprite2D")
if not _sprite:
_sprite = _parent.get_node_or_null("AnimatedSprite2D")
if not _sprite:
push_warning("SquashStretch: 親の下に 'Sprite2D' または 'AnimatedSprite2D' が見つかりません。")
set_physics_process(false)
func _physics_process(delta: float) -> void:
# 1. 常に元の形 (1.0, 1.0) に向かって滑らかに戻ろうとする (Lerp)
_sprite.scale = _sprite.scale.lerp(Vector2.ONE, recovery_speed * delta)
# 現在の接地状態を取得
var is_on_floor = _parent.is_on_floor()
# --- 状態変化の検知 ---
# A. ジャンプ開始検知 (床にいた -> いなくなった && 上昇中)
if _was_on_floor and not is_on_floor and _parent.velocity.y < 0:
_sprite.scale = stretch_strength # びよーんと伸びる
# B. 着地検知 (床にいなかった -> いるようになった)
elif not _was_on_floor and is_on_floor:
_sprite.scale = squash_strength # むにゅっと潰れる
# 状態を保存
_was_on_floor = is_on_floor
2. 使い方チュートリアル
このコンポーネントは、「当たり判定(Collision)」を変形させずに、「見た目(Sprite)」だけを変形させるのがコツです。
手順①:プレイヤーの構成確認
プレイヤーのシーン構成が以下のようになっていることを確認してください。スプライトの名前が重要です。
Player (CharacterBody2D)
├── CollisionShape2D (これは変形させたくない!)
├── Sprite2D (これが変形対象。名前は "Sprite2D" にする)
├── JumpController (ジャンプ用)
└── GravityComponent (重力用)
※もし AnimatedSprite2D を使っている場合も、その名前なら自動で認識します。
手順②:コンポーネントのアタッチ
Playerの子ノードとしてNodeを追加し、名前をSquashStretchにします。- スクリプト
SquashStretch.gdをアタッチします。
手順③:スプライトの原点(Offset)調整
これが最も重要です!
通常、スプライトの中心は画像の真ん中です。そのまま縮めると「空中に浮いて縮む」ように見えてしまいます。
「足元」を基準に変形させるため、以下の設定を行ってください。
Sprite2Dノードを選択。- インスペクターの Offset セクションを開く。
- Centered のチェックを外す。
- 画像がズレるので、
Positionを調整して、画像の足元がPlayerの原点(0,0)に来るように合わせます。 - または、
Offsetのy値を調整して足元を合わせます。
- 画像がズレるので、
手順④:実行と調整
ゲームを実行してジャンプしてみてください。
- 飛び上がる瞬間に縦長になり、
- 着地した瞬間に横長になり、
- すぐに「プルン」と元の形に戻れば成功です!
3. このコンポーネントの仕組みと効果
なぜ PhysicsBody を変形させないの?
親(CharacterBody2D)自体の scale を変更すると、子ノードにある CollisionShape2D(当たり判定)まで変形してしまいます。
着地した瞬間に当たり判定が横に広がると、壁にめり込んだり、床を突き抜けたりするバグの原因になります。
そのため、このコンポーネントはあえて親ではなく、兄弟である「スプライトだけ」を探して変形させています。
lerp によるゴムの挙動
コード内の以下の行が、ゴムのような質感の正体です。
_sprite.scale = _sprite.scale.lerp(Vector2.ONE, recovery_speed * delta)
lerp(線形補間)を使って、毎フレーム「現在の形」から「正常な形(1, 1)」に向かって少しずつ近づけています。
Recovery Speed を高くすると硬いゴム(すぐ戻る)、低くすると柔らかいゼリー(ゆっくり戻る)のような質感になります。
応用:ダメージ時にも使う
このスクリプトに外部から呼べる関数を追加すれば、ダメージを受けた時の演出にも使えます。
# 追加関数の例
func apply_impact():
# びっくりして縮こまる演出
_sprite.scale = Vector2(1.5, 0.5)
これを KnockbackReceiver などから呼び出せば、ダメージを受けた瞬間にキャラクターがグシャッと潰れるコミカルな表現が可能になります。
