攻撃を当てても、敵がダメージを受けた実感が薄い——アクションゲームで「手応え」が足りないと感じたことはないでしょうか。プレイヤーが敵を殴った瞬間、あるいは自分が被弾した瞬間に、キャラクターが一瞬パッと白く光る。たったこれだけで、打撃の気持ちよさは驚くほど増します。この演出が ホワイトフラッシュ です。
しかも実装はとても手軽で、シェーダーのような難しい知識は要りません。modulate プロパティと Tween を組み合わせれば、数行のGDScriptで作れます。
この記事でわかること
modulateが色を 乗算 で変える仕組み- RGB値を 1より大きく すると白く光るHDRの原理
Tweenで「一瞬白く → なめらかに戻す」を作る基本実装- シェーダーとの使い分けと、被弾リアクションへの組み込み方
modulateプロパティの仕組み
modulate は、CanvasItem を継承するすべての2Dノード(Sprite2D・Label・ColorRect など)が最初から持っている色調整用のプロパティです。ここに 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 を掛ければ暗く、というのがポイントです。

なぜ白く光るのか? HDR効果
modulate のおもしろいところは、RGB値を1より大きく設定できる 点です。通常、色は0(光なし)〜1(元の色)で考えますが、modulate のRGBを1以上にすると、そのスプライトはHDR(ハイダイナミックレンジ)カラーとして扱われ、内側から発光しているように輝きます。これが「白飛び」した見た目になり、ホワイトフラッシュの正体です。
上の図の右端のように、modulate = Color(10, 10, 10) のような大きな値にすると、元のテクスチャの色に関係なく、スプライトはほぼ真っ白に光ります。この 「一瞬だけ大きな値にして、すぐ元に戻す」 がフラッシュ演出の核です。
基本の実装:Tweenで一瞬白くして戻す
ホワイトフラッシュは「一瞬で白くなり、すぐ元の色になめらかに戻る」という短いアニメーションです。この時間変化は Tween の得意分野です。

ダメージを受けたキャラクター(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を超えた分がふわっとにじんで、より発光らしくなります。
よくある間違いとベストプラクティス
modulate の演出は手軽ですが、いくつか押さえておくと安定します。特に 暗くしようとして真っ黒にしてしまう のは初心者がやりがちな失敗です。

| よくある間違い | ベストプラクティス |
|---|---|
_process 内でカウンター変数を使い、自力でアニメーションさせる | Tween や AnimationPlayer を使い、宣言的に書く |
暗くしようと Color(0, 0, 0) にして、真っ黒で消えてしまう | 白く光らせるならRGBを 1 より大きく。暗くしたい場合も Color(0.1, 0.1, 0.1) のように少し明るさを残す |
| ダメージ処 理とエフェクト処理が同じ関数に混ざっている | シグナルでダメージ判定と視覚エフェクトを分離し、演出の変更がロジックに波及しないようにする |
エフェクトのたびに create_tween() を呼びっぱなしにする | 短時間に連続するなら、既存の Tween を kill() してから作り直す |
modulateとシェーダーの使い分け
ホワイトフラッシュはシェーダーでも作れます。手軽さ重視の modulate + Tween と、表現力重視のシェーダーを比べてみましょう。

| 特徴 | modulate + Tween | カスタムシェーダー |
|---|---|---|
| 手軽さ | ◎ 数行のGDScriptで実装できる | △ シェーダー言語の学習が必要 |
| パフォーマンス | ○ 非 常に軽量。多数のオブジェクトでも問題になりにくい | ○〜△ シンプルなら軽量だが、複雑だと高負荷になりうる |
| 表現力 | △ 色の乗算とアルファ変更のみ。限定的 | ◎ アウトライン・ディゾルブ・発光などピクセル単位で自由 |
| 向いている用途 | ダメージフラッシュ、アイテム取得時の発光などシンプルな演出 | ボスの特殊な登場演出など、凝った視覚効果 |
ほとんどのダメージフラッシュは、手軽さと軽さから modulate + Tween で十分です。輪郭だけを光らせる、溶けるように消す、といった凝った表現が欲しくなったら Fragment Shaderの基礎 へ進みましょう。
実践:被弾リアクションを疎結合で組む
フラッシュ単体でも効果的ですが、実際のゲームでは「被弾したら、白フラッシュ+ノックバック+効果音がまとめて起きる」ことが多いはずです。
- アクション :敵を斬ると、その敵が白フラッシュしてのけぞる
- シューティング :弾が当たった機体が一瞬白く光り、被弾音が鳴る
- ローグライク :攻撃が当たるたびに対象がフラッシュして手応えを出す
ここで大事なのは、 ダメージのロジック(HPを減らす)と、見た目の演出(フラッシュ)を分ける ことです。分けておくと、同じ演出をプレイヤー・敵・破壊できる木箱などで使い回せます。橋渡しには シグナル を使います。

まず、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_modulate:modulateは子ノードすべてに掛かります。UIのパネルだけ色を変えて中のラベルはそのままにしたい、というときはself_modulateを使うと自分だけに適用されます。 - 色で意味を分ける :味方の被弾は赤みがかったフラッシュ(
Color(4, 1, 1))、敵へのヒットは白(Color(8, 8, 8))のように色を変えると、状況が一目で伝わります。 - もっと発光させたいなら Glow :
WorldEnvironmentの Glow を有効にすると、modulateを1以上にしたスプライトがふわっとにじんで光り、フラッシュがよりリッチになります。 - 凝った表現はシェーダーへ :輪郭だけ光らせる、ノイ ズで溶けるように消す、といった表現は Fragment Shaderの基礎 の領域です。
まとめ
modulate:CanvasItemの色を乗算で変えるプロパティ。RGB値を1以上にするとHDR効果で白く光る- フラッシュの作り方 :代入で一瞬白くして、
TweenでColor(1, 1, 1)へなめらかに戻す - 連続ヒット対策 :
flash_tween.kill()で前のフラッシュを止めてから始める - 設計 :ダメージのロジックと演出を シグナル で分け、同じ演出を使い回す
ホワイトフラッシュは、少ない手間で「手応え」を大きく底上げできる、コスパの高い演出です。まずは敵のヒットに1つ入れてみて、そこに効果音やノックバック、AnimationPlayer での揺れを足していくと、打撃感がどんどん育っていきます。