【Godot】modulateプロパティによるダメージ時のホワイトフラッシュ

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

Godotでキャラクターが被弾した瞬間に白く光るホワイトフラッシュを、modulateプロパティとTweenで実装する方法を解説。HDRの原理、連続ヒット対策、シェーダーとの使い分け、被弾リアクションへの組み込みまで紹介します。

攻撃を当てても、敵がダメージを受けた実感が薄い——アクションゲームで「手応え」が足りないと感じたことはないでしょうか。プレイヤーが敵を殴った瞬間、あるいは自分が被弾した瞬間に、キャラクターが一瞬パッと白く光る。たったこれだけで、打撃の気持ちよさは驚くほど増します。この演出が ホワイトフラッシュ です。

しかも実装はとても手軽で、シェーダーのような難しい知識は要りません。modulate プロパティと Tween を組み合わせれば、数行のGDScriptで作れます。

被弾した瞬間に白く光るキャラクターのイメージ。青い粘土マネキンが白く発光し、周囲にインパクトの放射線が広がる

この記事でわかること

  • modulate が色を 乗算 で変える仕組み
  • RGB値を 1より大きく すると白く光るHDRの原理
  • Tween で「一瞬白く → なめらかに戻す」を作る基本実装
  • シェーダーとの使い分けと、被弾リアクションへの組み込み方

Sponsored

modulateプロパティの仕組み

modulate は、CanvasItem を継承するすべての2Dノード(Sprite2DLabelColorRect など)が最初から持っている色調整用のプロパティです。ここに Color を設定すると、そのノード(と すべての子ノード)の色が 乗算 で変化します。

  • modulate = Color(1, 1, 1)(白): 元の色のまま(デフォルト)
  • modulate = Color(1, 0, 0)(赤): R成分だけ残り、赤くなる
  • modulate = Color(0.5, 0.5, 0.5)(グレー): 全体が暗くなる
  • modulate = Color(1, 1, 1, 0.5): 4つ目の値(アルファ)で半透明にできる

「元の色 × 指定した色」なので、1 を掛ければそのまま、0.5 を掛ければ暗く、というのがポイントです。

modulateの値によるスプライトの見え方の変化。値0.5で暗く沈み、1.0で元のまま、2.0で明るく、10でほぼ真っ白に発光する

なぜ白く光るのか? HDR効果

modulate のおもしろいところは、RGB値を1より大きく設定できる 点です。通常、色は0(光なし)〜1(元の色)で考えますが、modulate のRGBを1以上にすると、そのスプライトはHDR(ハイダイナミックレンジ)カラーとして扱われ、内側から発光しているように輝きます。これが「白飛び」した見た目になり、ホワイトフラッシュの正体です。

上の図の右端のように、modulate = Color(10, 10, 10) のような大きな値にすると、元のテクスチャの色に関係なく、スプライトはほぼ真っ白に光ります。この 「一瞬だけ大きな値にして、すぐ元に戻す」 がフラッシュ演出の核です。

Sponsored

基本の実装:Tweenで一瞬白くして戻す

ホワイトフラッシュは「一瞬で白くなり、すぐ元の色になめらかに戻る」という短いアニメーションです。この時間変化は Tween の得意分野です。

ホワイトフラッシュのアニメーションのタイムライン。時間0でColor(10,10,10)に白く光り、0.3秒かけてColor(1,1,1)へなめらかに戻る

ダメージを受けたキャラクター(Sprite2D を持つノード)にフラッシュをかける関数です。

# CharacterBody2Dなどのスクリプト

# 連続ダメージの制御用に、Tweenを変数で持っておく
var flash_tween: Tween

func apply_damage_effect() -> void:
    # ... HPを減らす・無敵時間を始めるなどの処理 ...
    _start_white_flash()

func _start_white_flash() -> void:
    # すでにフラッシュ中なら止めてから始める(連続ヒット対策)
    if flash_tween and flash_tween.is_valid():
        flash_tween.kill()

    # 1. まず一瞬で白くする(大きな値でHDR発光させる)
    $Sprite2D.modulate = Color(10, 10, 10)

    # 2. 0.3秒かけて元の色 Color(1,1,1) へなめらかに戻す
    flash_tween = create_tween().set_trans(Tween.TRANS_QUINT).set_ease(Tween.EASE_OUT)
    flash_tween.tween_property($Sprite2D, "modulate", Color(1, 1, 1), 0.3)

組み立てのコツは、 光らせる瞬間は代入で一発、戻すところだけTweenでなめらかに という点です。TRANS_QUINT + EASE_OUT は「パッと始まってスッと収まる」ので、フラッシュの余韻に向いています。連続でダメージを受けても、flash_tween.kill() で前のフラッシュを片付けてから始めるので破綻しません。

注意: Color(10, 10, 10) にしても白く見えない場合、描画が色を1でクランプしていることがあります。まず Color(2, 2, 2)Color(3, 3, 3) 程度から試してください。WorldEnvironment の Glow を有効にすると、1を超えた分がふわっとにじんで、より発光らしくなります。

Sponsored

よくある間違いとベストプラクティス

modulate の演出は手軽ですが、いくつか押さえておくと安定します。特に 暗くしようとして真っ黒にしてしまう のは初心者がやりがちな失敗です。

modulateの色指定の悪い例と良い例。悪い例はColor(0,0,0)で真っ黒になり見えなくなる。良い例はColor(10,10,10)で白く光らせる
よくある間違いベストプラクティス
_process 内でカウンター変数を使い、自力でアニメーションさせるTweenAnimationPlayer を使い、宣言的に書く
暗くしようと Color(0, 0, 0) にして、真っ黒で消えてしまう白く光らせるならRGBを 1 より大きく。暗くしたい場合も Color(0.1, 0.1, 0.1) のように少し明るさを残す
ダメージ処理とエフェクト処理が同じ関数に混ざっているシグナルでダメージ判定と視覚エフェクトを分離し、演出の変更がロジックに波及しないようにする
エフェクトのたびに create_tween() を呼びっぱなしにする短時間に連続するなら、既存の Tweenkill() してから作り直す
Sponsored

modulateとシェーダーの使い分け

ホワイトフラッシュはシェーダーでも作れます。手軽さ重視の modulate + Tween と、表現力重視のシェーダーを比べてみましょう。

modulate+Tweenとシェーダーの比較図。modulate+Tweenは手軽・軽量だが表現は限定的、シェーダーは学習が必要だが表現力が高い
特徴modulate + Tweenカスタムシェーダー
手軽さ◎ 数行のGDScriptで実装できる△ シェーダー言語の学習が必要
パフォーマンス○ 非常に軽量。多数のオブジェクトでも問題になりにくい○〜△ シンプルなら軽量だが、複雑だと高負荷になりうる
表現力△ 色の乗算とアルファ変更のみ。限定的◎ アウトライン・ディゾルブ・発光などピクセル単位で自由
向いている用途ダメージフラッシュ、アイテム取得時の発光などシンプルな演出ボスの特殊な登場演出など、凝った視覚効果

ほとんどのダメージフラッシュは、手軽さと軽さから modulate + Tween で十分です。輪郭だけを光らせる、溶けるように消す、といった凝った表現が欲しくなったら Fragment Shaderの基礎 へ進みましょう。

実践:被弾リアクションを疎結合で組む

フラッシュ単体でも効果的ですが、実際のゲームでは「被弾したら、白フラッシュ+ノックバック+効果音がまとめて起きる」ことが多いはずです。

  • アクション :敵を斬ると、その敵が白フラッシュしてのけぞる
  • シューティング :弾が当たった機体が一瞬白く光り、被弾音が鳴る
  • ローグライク :攻撃が当たるたびに対象がフラッシュして手応えを出す

ここで大事なのは、 ダメージのロジック(HPを減らす)と、見た目の演出(フラッシュ)を分ける ことです。分けておくと、同じ演出をプレイヤー・敵・破壊できる木箱などで使い回せます。橋渡しには シグナル を使います。

被弾リアクションを疎結合で組む流れの図。被弾したキャラのtake_damage()からHealthノードがdamagedシグナル��を出し、フラッシュ・ノックバック・効果音の3つがそれぞれ反応する

まず、HPを持つ担当(Health)は「ダメージを受けた」という事実をシグナルで知らせるだけにします。

# health.gd (HPを管理する担当ノード)
class_name Health
extends Node

signal damaged(amount: int)
signal died

@export var max_hp: int = 100
var hp: int = max_hp

func take_damage(amount: int) -> void:
    hp = max(hp - amount, 0)
    damaged.emit(amount)   # 「食らった」と通知するだけ。演出はしない
    if hp == 0:
        died.emit()

演出担当(Sprite2D に付ける)は、そのシグナルを聞いてフラッシュします。

# hit_feedback.gd (見た目の演出担当)
extends Sprite2D

@export var health: Health
var flash_tween: Tween

func _ready() -> void:
    health.damaged.connect(_on_damaged)

func _on_damaged(_amount: int) -> void:
    _flash_white()
    # ここにノックバックや効果音の再生を足していける

func _flash_white() -> void:
    if flash_tween and flash_tween.is_valid():
        flash_tween.kill()
    modulate = Color(8, 8, 8)
    flash_tween = create_tween().set_ease(Tween.EASE_OUT)
    flash_tween.tween_property(self, "modulate", Color(1, 1, 1), 0.25)

ポイントは2つです。

  • ロジックと演出をシグナルで分けるHealth は「食らった」と言うだけ、hit_feedback.gd はそれを聞いて光るだけ。だから同じ hit_feedback.gd を、敵にもプレイヤーにも壊せる木箱にも付け回せます。演出を差し替えても Health 側は一切変えずに済みます。
  • フラッシュは前のTweenを止めてから :連続ヒットで何度も _flash_white() が呼ばれても、kill() してから作り直すので、明るさが中途半端なまま固まったりしません。

効果音やノックバックも、この _on_damaged() に足すだけで増やせます。演出の追加が1箇所にまとまるのが、分けておく最大のうまみです。

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

フラッシュが作れたら、次はこのあたりが役立ちます。今は「そういう手がある」と知っておくだけで十分です。

  • 子に影響させたくないなら self_modulatemodulate は子ノードすべてに掛かります。UIのパネルだけ色を変えて中のラベルはそのままにしたい、というときは self_modulate を使うと自分だけに適用されます。
  • 色で意味を分ける :味方の被弾は赤みがかったフラッシュ(Color(4, 1, 1))、敵へのヒットは白(Color(8, 8, 8))のように色を変えると、状況が一目で伝わります。
  • もっと発光させたいなら GlowWorldEnvironment の Glow を有効にすると、modulate を1以上にしたスプライトがふわっとにじんで光り、フラッシュがよりリッチになります。
  • 凝った表現はシェーダーへ :輪郭だけ光らせる、ノイズで溶けるように消す、といった表現は Fragment Shaderの基礎 の領域です。

まとめ

  • modulateCanvasItem の色を乗算で変えるプロパティ。RGB値を1以上にするとHDR効果で白く光る
  • フラッシュの作り方 :代入で一瞬白くして、TweenColor(1, 1, 1) へなめらかに戻す
  • 連続ヒット対策flash_tween.kill() で前のフラッシュを止めてから始める
  • 設計 :ダメージのロジックと演出を シグナル で分け、同じ演出を使い回す

ホワイトフラッシュは、少ない手間で「手応え」を大きく底上げできる、コスパの高い演出です。まずは敵のヒットに1つ入れてみて、そこに効果音やノックバック、AnimationPlayer での揺れを足していくと、打撃感がどんどん育っていきます。