Godotのリストに戻る

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

作成: 2025-06-20更新: 2025-06-20UI・エフェクトブログ記事を読む

概要

modulateプロパティは、CanvasItem(Sprite2D、CharacterBody2Dなど)の色に乗算するカラー値です。これを利用して、ダメージを受けた際の白いフラッシュ効果を簡単に実装できます。

ゲームの「ジューシーさ」を高める重要な要素の一つが、フィードバックの明確さです。特にアクションゲームでは、「ダメージを受けた」ことをプレイヤーに瞬時に伝えることが必要です。modulateを使ったホワイトフラッシュは、シンプルでありながら非常に効果的な手法です。

使い方も簡単で、たった一行のコードで即座に効果が実感できるため、ゲーム開発初心者の方にもおすすめです。

Godotのノックバック効果

modulateの特徴

  • 乗算処理: 元の色 × modulate値 = 表示色
  • 継承性: 親ノードから子ノードに自動継承
  • 範囲: 各成分0.0〜上限なし(1.0が基準)
  • アルファ対応: 透明度も制御可能

基本的な使い方

単純なフラッシュ効果

# Player.gd
extends CharacterBody2D

func take_damage(amount: int):
    health -= amount
    flash_white()

func flash_white():
    # 白く光らせる(明度を上げる)
    modulate = Color(2, 2, 2)
    
    # 0.1秒待機
    await get_tree().create_timer(0.1).timeout
    
    # 元に戻す
    modulate = Color(1, 1, 1)

高度な実装例

段階的なフラッシュ

func smooth_flash():
    # Tweenを使って滑らかに変化
    var tween = create_tween()
    tween.tween_property(self, "modulate", Color(2, 2, 2), 0.05)
    tween.tween_property(self, "modulate", Color(1, 1, 1), 0.15)

色付きフラッシュ

func colored_flash(flash_color: Color):
    # 赤いフラッシュ
    modulate = Color(2, 0.5, 0.5)  # 赤強調
    await get_tree().create_timer(0.1).timeout
    modulate = Color.WHITE
    
    # 青いフラッシュ(凍結効果など)
    modulate = Color(0.5, 0.5, 2)  # 青強調
    await get_tree().create_timer(0.2).timeout
    modulate = Color.WHITE

点滅効果

func blink_effect(times: int = 3):
    for i in times:
        modulate.a = 0.3  # 半透明
        await get_tree().create_timer(0.1).timeout
        modulate.a = 1.0  # 不透明
        await get_tree().create_timer(0.1).timeout

実践的な活用例

ダメージシステムの完全実装

extends CharacterBody2D

@export var max_health: int = 100
@export var invincibility_duration: float = 1.0

var health: int
var is_invincible: bool = false

func _ready():
    health = max_health

func take_damage(amount: int, knockback_direction: Vector2 = Vector2.ZERO):
    if is_invincible:
        return
    
    health -= amount
    
    # 視覚効果
    flash_damage()
    
    # ノックバック
    if knockback_direction != Vector2.ZERO:
        velocity += knockback_direction * 200
    
    # 無敵時間
    become_invincible()
    
    if health <= 0:
        die()

func flash_damage():
    # 初期フラッシュ
    modulate = Color(2, 2, 2)
    await get_tree().create_timer(0.1).timeout
    
    # 点滅しながら元に戻る
    var tween = create_tween()
    tween.set_loops(5)
    tween.tween_property(self, "modulate:a", 0.5, 0.1)
    tween.tween_property(self, "modulate:a", 1.0, 0.1)
    
    # 最後に完全に元に戻す
    await tween.finished
    modulate = Color.WHITE

func become_invincible():
    is_invincible = true
    set_collision_mask_bit(1, false)  # 敵との衝突を無効化
    
    await get_tree().create_timer(invincibility_duration).timeout
    
    is_invincible = false
    set_collision_mask_bit(1, true)
    modulate = Color.WHITE  # 念のため正常化

状態異常の視覚表現

# StatusEffects.gd
extends Node

enum Status {
    NORMAL,
    POISONED,
    BURNING,
    FROZEN,
    CONFUSED
}

var current_status = Status.NORMAL
var status_tween: Tween

func apply_status(status: Status, duration: float):
    current_status = status
    
    # 既存の効果をキャンセル
    if status_tween:
        status_tween.kill()
    
    match status:
        Status.POISONED:
            apply_poison_effect(duration)
        Status.BURNING:
            apply_burn_effect(duration)
        Status.FROZEN:
            apply_freeze_effect(duration)

func apply_poison_effect(duration: float):
    # 緑がかった色に
    owner.modulate = Color(0.7, 1.2, 0.7)
    
    # パルス効果
    status_tween = create_tween()
    status_tween.set_loops(int(duration * 2))
    status_tween.tween_property(owner, "modulate:g", 1.5, 0.25)
    status_tween.tween_property(owner, "modulate:g", 1.2, 0.25)
    
    await get_tree().create_timer(duration).timeout
    clear_status()

func apply_burn_effect(duration: float):
    # オレンジ色の点滅
    status_tween = create_tween()
    status_tween.set_loops(int(duration * 4))
    status_tween.tween_property(owner, "modulate", Color(2, 1, 0.5), 0.125)
    status_tween.tween_property(owner, "modulate", Color(1.5, 0.8, 0.5), 0.125)
    
    await get_tree().create_timer(duration).timeout
    clear_status()

func clear_status():
    current_status = Status.NORMAL
    if status_tween:
        status_tween.kill()
    owner.modulate = Color.WHITE

modulateの計算方法

# 元の色
var original = Color(0.5, 0.8, 0.3)  # 緑っぽい色

# modulate適用
modulate = Color(2, 1, 1)

# 結果の色
# R: 0.5 × 2 = 1.0(最大値にクランプ)
# G: 0.8 × 1 = 0.8
# B: 0.3 × 1 = 0.3
# → より明るい緑色に

パフォーマンスの考慮

# 良い例:必要な時だけ変更
func _on_damage_taken():
    modulate = Color(2, 2, 2)
    # ...

# 悪い例:毎フレーム計算
func _process(delta):
    modulate = Color(1 + sin(Time.get_ticks_msec() * 0.001), 1, 1)

トラブルシューティング

フラッシュが見えない

  • modulate値が小さすぎる(2.0以上推奨)
  • 背景色との対比が弱い
  • 効果時間が短すぎる(0.05秒以上推奨)

色が戻らない

# 確実に戻す処理
func _exit_tree():
    modulate = Color.WHITE

func reset_visual():
    modulate = Color.WHITE
    visible = true
    # その他の視覚効果もリセット

実装して学んだこと

modulateを使ったフラッシュ効果は、シンプルでありながら非常に強力な視覚効果です。最初は単純なホワイトフラッシュから始まりましたが、状態異常の表現や属性攻撃の演出など、様々な応用ができることを学びました。

特に印象的だったのは、Tweenと組み合わせることで得られる滑らかなアニメーションです。即座に白く光って、ゆっくりと元に戻る動きは、プレイヤーに「筋の通った反応」を与えてくれます。

modulateの乗算の仕組みを理解することで、単純な明度変化だけでなく、色味の変化や透明度のコントロールなど、幅広い表現が可能になりました。この機能を使いこなせるようになってから、ゲームのフィードバックの品質が大幅に向上しました。