敵を攻撃したとき、「スカッ」とした手応えだと寂しいですよね。
名作アクションゲームを観察すると、攻撃が当たった瞬間にキャラが一瞬**「真っ白に発光」**していることに気づくはずです。
しかし、Godotの標準機能である modulate(色調補正)では、画像を赤や青にすることはできても、「真っ白(シルエット)」にすることはできません。
そこで今回は、**「アタッチするだけで、専用シェーダーを自動生成して光らせる」**という、少しプロっぽいコンポーネントを作ります。
どんなことができるの?
この「HitFlashComponent」を敵やプレイヤーに追加するだけです。
- 真っ白に光る: シェーダーを使って、画像のピクセルを一時的に純白に置き換えます。
- 準備いらず: 面倒なシェーダーファイル(.gdshader)を作る必要はありません。スクリプトが自動で適用します。
- ワンラインで実行: ダメージ処理のところに
flash()と書くだけで動きます。
ステップ1:スクリプトの作成
hit_flash_component.gd という名前でスクリプトを作成し、以下のコードをコピペしてください。
このスクリプトは、実行時(ゲーム開始時)に親ノードに「白く光る能力(マテリアル)」を自動でプレゼントします。
class_name HitFlashComponent
extends Node
## 親ノード(Spriteなど)を一時的に単色(白)で光らせるコンポーネント
## シェーダーを動的に生成・適用するため、事前のマテリアル設定は不要です。
# 光らせる色(デフォルトは白)
@export var flash_color: Color = Color.WHITE
# 光っている時間(秒)
@export var flash_duration: float = 0.2
# 内部で使用するシェーダーコード(2D用)
# これをスクリプト内で定義することで、ファイル管理を楽にしています
const SHADER_CODE_2D = """
shader_type canvas_item;
uniform vec4 flash_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float flash_modifier : hint_range(0.0, 1.0) = 0.0;
void fragment() {
vec4 color = texture(TEXTURE, UV);
// 元の色とフラッシュ色を混ぜる
color.rgb = mix(color.rgb, flash_color.rgb, flash_modifier);
COLOR = color;
}
"""
var _parent_sprite: CanvasItem
var _material: ShaderMaterial
var _tween: Tween
func _ready() -> void:
# 親がSpriteなどのCanvasItemかチェック
var parent = get_parent()
if parent is CanvasItem:
_parent_sprite = parent
_setup_material()
func _setup_material() -> void:
# 新しいシェーダーマテリアルを作成
_material = ShaderMaterial.new()
var shader = Shader.new()
shader.code = SHADER_CODE_2D
_material.shader = shader
# 親に適用(元のマテリアルがある場合は上書きになるため注意)
# ※既存のマテリアルと共存させたい場合は、構成を工夫する必要があります
_parent_sprite.material = _material
## 外部からこの関数を呼ぶと光ります
func flash() -> void:
if not _material:
return
# 前の点滅が残っていたらキャンセル
if _tween:
_tween.kill()
# Tweenを作成してアニメーション
_tween = create_tween()
# 1. 色をセット
_material.set_shader_parameter("flash_color", flash_color)
# 2. フラッシュ強度を 1.0 (真っ白) にする
_material.set_shader_parameter("flash_modifier", 1.0)
# 3. 指定時間かけて 0.0 (元の色) に戻す
_tween.tween_method(
func(value): _material.set_shader_parameter("flash_modifier", value),
1.0, # 開始値
0.0, # 終了値
flash_duration
).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
ステップ2:実際に使ってみよう
1. ノードを追加する
光らせたい敵キャラ(例えば Enemy シーン)の Sprite2D の下に、このコンポーネントを追加します。
もちろん、親ノード(CharacterBody2Dなど)の下に置いてもOKですが、「画像を持っているノード(Spriteなど)」の下に追加する方が、このスクリプトの仕様上(get_parent()を使っているため)確実です。
- Enemy (CharacterBody2D)
- Sprite2D ← ここに HitFlashComponent を追加
- CollisionShape2D
2. ダメージ処理で呼び出す
敵キャラがダメージを受けるスクリプトの中に、1行追加します。
# Enemy.gd (敵のスクリプト例)
@onready var hit_flash = $Sprite2D/HitFlashComponent
func take_damage(amount: int):
hp -= amount
# ここでフラッシュを実行!
hit_flash.flash()
if hp <= 0:
die()
これだけで、ダメージを受けるたびに敵が「ピカッ」と白く光り、徐々に元の色に戻るようになります。
応用:もっと気持ちよくする「ヒットストップ」
光るだけでも十分ですが、さらに**「ヒットストップ(一瞬時間が止まる演出)」**を組み合わせると、プロの味になります。これもコンポーネント内で処理できます。
HitFlashComponent に以下の機能を追加してみてください。
# ヒットストップの時間(秒)
@export var hit_stop_duration: float = 0.05
func flash() -> void:
# ...(さっきのTween処理)...
# ヒットストップ実行
if hit_stop_duration > 0:
Engine.time_scale = 0.05 # 全体の時間を遅くする
# 指定時間待ってから戻す
await get_tree().create_timer(hit_stop_duration, true, false, true).timeout
Engine.time_scale = 1.0
※ Engine.time_scale をいじるとゲーム全体の時間が止まるため、プレイヤーの動きも止まって「重い一撃」感が出ます。
まとめ:演出の「外注化」
今回のコンポーネントのポイントは、**「敵のスクリプトは『光れ』と命令するだけで、どうやって光るかは知らなくていい」**という点です。
もし将来、「光るんじゃなくて、赤く点滅させたいな」と思ったとしても、敵のコードは修正せず、このコンポーネントの設定を変えるだけで済みます。これがコンポーネント指向の強みです!
(ブログ執筆者へのメモ)
- 3Dの場合: 上記コードは
CanvasItem(2D)用です。3D(MeshInstance3D)で行う場合は、shader_type spatial;に書き換え、ALBEDO = mix(ALBEDO, flash_color.rgb, flash_modifier);とすることで同様に実現できます。記事のボリューム次第で「3D版はコードのここを変えるだけ!」と補足しても良いでしょう。 - 既存マテリアル問題: このスクリプトは
materialを上書きします。もし敵キャラがすでに特殊なシェーダーを使っている場合は、そのシェーダーの中にflash_modifierのロジックを組み込む必要があります。この記事では「一番簡単な導入法」として上書き方式を紹介しています。
