【Godot 4】GlowPulse (発光演出) コンポーネントの作り方

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

脱・初心者!Godot 4 ゲーム開発の「2歩目」

新品価格
¥831から
(2025/12/13 21:39時点)

Godot4ローグライク入門 ~ダンジョン自動生成~

新品価格
¥831から
(2025/12/13 21:44時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Godot 4で「それっぽいネオン演出」をやろうとすると、まず WorldEnvironment をどこかのシーンに置いて、Glow を有効化して、さらに「明滅させたいオブジェクトごとにアニメーションを組む」みたいな構成になりがちですよね。
AnimationPlayer を仕込んで、タイムラインを調整して…と、ちょっとしたテスト用シーンを作るだけでも手間がかかります。

さらに悪いことに、Glow の強度を「シーン全体で一括制御」したいのに、各シーンごとに WorldEnvironment を持っていたり、深いノード階層のどこかに埋まっていたりすると、あとから調整するのがとても面倒です。

そこで今回は、WorldEnvironment の Glow 強度をサイン波で揺らし、ネオンのような明滅を一括制御できるコンポーネントとして、「GlowPulse」コンポーネントを用意しました。
どのシーンにも簡単にアタッチできて、継承もゴリゴリのノード階層も不要です。Environment を直接いじるのではなく、「Glow を揺らす役目だけを持つ独立コンポーネント」として合成していきましょう。

【Godot 4】ネオンの鼓動をシーン全体に!「GlowPulse」コンポーネント

フルコード(GDScript / Godot 4)


extends Node
class_name GlowPulse
## WorldEnvironment の Glow 強度をサイン波で揺らすコンポーネント。
## シーン内の WorldEnvironment を自動検出して、Glow の intensity を更新します。
##
## 使い方:
## - どこかのノードにこのコンポーネントをアタッチするだけでOK。
## - 同じシーン内に WorldEnvironment があれば、自動で拾って揺らします。

@export_group("Glow 参照設定")
## 明滅させる WorldEnvironment を明示的に指定したい場合に使います。
## 空のままなら、自動的に親シーンから WorldEnvironment を探します。
@export var target_world_environment: WorldEnvironment

## 自動探索の起点にするノード。
## 通常は空でOK。特定のサブツリーからだけ探したいときに指定します。
@export var search_root: Node

@export_group("Glow 明滅パラメータ")
## Glow 強度の「基準値」。この値を中心にサイン波で上下します。
## 例: 1.0 なら、1.0 を中心に強度が揺れるイメージです。
@export_range(0.0, 10.0, 0.01)
var base_intensity: float = 1.0

## サイン波の振幅。どれくらい強く揺らすか。
## 例: 0.5 なら、base_intensity ± 0.5 の範囲で変動します。
@export_range(0.0, 10.0, 0.01)
var amplitude: float = 0.5

## サイン波の速度。1.0 で 1秒に1周期くらいのイメージ。
## 大きくするとチカチカ、小さくするとゆっくり明滅します。
@export_range(0.0, 20.0, 0.01)
var speed: float = 2.0

## サイン波の位相オフセット。複数の GlowPulse をずらして点滅させたいときに使います。
@export_range(-TAU, TAU, 0.01)
var phase_offset: float = 0.0

@export_group("制御フラグ")
## シーン開始時に自動で明滅を開始するかどうか。
@export var auto_start: bool = true

## 実行中に一時停止したいときに使うフラグ。
@export var paused: bool = false

## intensity を clamp するかどうか。
@export var clamp_intensity: bool = true

## clamp する場合の最小値。
@export_range(0.0, 10.0, 0.01)
var min_intensity: float = 0.0

## clamp する場合の最大値。
@export_range(0.0, 10.0, 0.01)
var max_intensity: float = 3.0


## 内部用タイマー。経過時間を積算します。
var _time: float = 0.0
## 実際に操作する Environment への参照。
var _environment: Environment
## 実行中かどうか。
var _running: bool = false


func _ready() -> void:
    # WorldEnvironment が指定されていなければ自動探索します。
    if not target_world_environment:
        target_world_environment = _find_world_environment()
    
    if target_world_environment:
        _environment = target_world_environment.environment
        if _environment == null:
            push_warning("[GlowPulse] WorldEnvironment に Environment が設定されていません。")
        else:
            # 初期値として base_intensity を適用しておく
            _environment.glow_enabled = true
            _environment.glow_intensity = base_intensity
    else:
        push_warning("[GlowPulse] シーン内に WorldEnvironment が見つかりませんでした。Glow を揺らせません。")
    
    _running = auto_start and _environment != null


func _process(delta: float) -> void:
    if not _running or paused or _environment == null:
        return
    
    _time += delta * speed
    # サイン波で intensity を計算
    var wave := sin(_time + phase_offset)
    var intensity := base_intensity + amplitude * wave
    
    if clamp_intensity:
        intensity = clampf(intensity, min_intensity, max_intensity)
    
    _environment.glow_enabled = true
    _environment.glow_intensity = intensity


## シーン内から WorldEnvironment を探すヘルパー。
## search_root が設定されていればそこから、なければ最上位のシーンルートから探索します。
func _find_world_environment() -> WorldEnvironment:
    var root: Node = search_root if search_root else get_tree().current_scene
    if root == null:
        return null
    
    # 深さ優先で最初に見つかった WorldEnvironment を返す
    var stack: Array[Node] = [root]
    while not stack.is_empty():
        var node := stack.pop_back()
        if node is WorldEnvironment:
            return node as WorldEnvironment
        for child in node.get_children():
            if child is Node:
                stack.push_back(child)
    return null


## 明滅を開始します。_environment が取れていない場合は何もしません。
func start() -> void:
    if _environment == null:
        push_warning("[GlowPulse] Environment が見つからないため start() できません。")
        return
    _running = true
    paused = false


## 明滅を停止します。停止時の intensity をどうするかはここで制御します。
func stop(reset_to_base: bool = true) -> void:
    _running = false
    paused = false
    if _environment and reset_to_base:
        _environment.glow_intensity = base_intensity


## パラメータをランタイムで変更するためのヘルパー。
## 「演出カーブを変えてみたい」といったときに、コードから呼び出せます。
func set_pulse_params(new_base: float, new_amp: float, new_speed: float) -> void:
    base_intensity = new_base
    amplitude = new_amp
    speed = new_speed

使い方の手順

ここでは、ネオン街ステージを例にして、「シーン全体のネオン感」を GlowPulse で演出する流れを見ていきます。

手順①: WorldEnvironment をシーンに置く

まずは通常どおり、WorldEnvironment を用意します。

NeonStage (Node2D or Node3D)
 ├── WorldEnvironment
 ├── Player
 └── GlowPulse (Node)
  • WorldEnvironment の Environment に新規 Environment リソースを割り当てる
  • Environment の Glow を有効化(glow_enabled = true)し、好みで強度やブレンドモードを設定

この時点では、まだ GlowPulse は何もしていません。普通の Glow 設定として動作します。

手順②: GlowPulse コンポーネントを追加

NeonStage の子として GlowPulse ノードを追加します(Node でOK)。
インスペクターで以下のように設定してみましょう。

  • target_world_environment: 空のまま(自動検出に任せる)
  • base_intensity: 1.0
  • amplitude: 0.6
  • speed: 2.5(やや早めの明滅)
  • phase_offset: 0.0(単体なら 0 でOK)
  • auto_start: true

これでゲームを再生すると、WorldEnvironment の Glow intensity がサイン波で揺れ、
シーン全体が「ふわっと明滅するネオン街」のような雰囲気になります。

手順③: プレイヤー用シーンと分離してもOK

プレイヤーなどのゲームロジックと、演出ロジック(GlowPulse)は分離しておいた方が管理が楽です。
例えば、プレイヤーシーンはこんな感じにしておきます。

Player (CharacterBody2D)
 ├── Sprite2D
 ├── CollisionShape2D
 └── PlayerMoveComponent (任意の移動コンポーネント)

GlowPulse はあくまで 「シーン全体の環境演出」なので、NeonStage や StageRoot のような「ステージルート側」にだけ置いておきましょう。
このように役割を分けておくと、プレイヤーシーンを別ステージに持って行っても、Glow の設定を気にせず再利用できます。

手順④: 複数の GlowPulse を使ってエリアごとに演出を変える

例えば、クラブエリア路地裏エリアで Glow の揺れ方を変えたい場合、WorldEnvironment をエリアごとに分けて、GlowPulse をそれぞれにアタッチすることもできます。

CityRoot (Node2D)
 ├── ClubArea (Node2D)
 │    ├── WorldEnvironment
 │    └── GlowPulse (Node)  # 速くて派手な明滅
 └── BackAlley (Node2D)
      ├── WorldEnvironment
      └── GlowPulse (Node)  # ゆっくり暗めの明滅
  • ClubArea の GlowPulse: amplitude = 0.8, speed = 4.0
  • BackAlley の GlowPulse: amplitude = 0.3, speed = 1.0

さらに、phase_offset を変えることで、「エリアAとエリアBの明滅タイミングをずらす」といったことも簡単にできます。

メリットと応用

GlowPulse をコンポーネントとして切り出すことで、次のようなメリットがあります。

  • シーン構造がスッキリ:WorldEnvironment 自体は1つ置くだけで、明滅ロジックは GlowPulse に集約。
  • 継承不要で再利用しやすい:WorldEnvironment を継承したカスタムノードを作る必要がなく、どんなステージにも GlowPulse をポン付けできます。
  • パラメータ調整が一箇所で完結:base/amplitude/speed をいじるだけで、演出の雰囲気をガラッと変えられます。
  • テストシーンの作成がラク:仮のステージに WorldEnvironment + GlowPulse を置くだけで、「それっぽいネオン感」をすぐ確認できます。

応用としては、例えば「ボスフェーズ移行時にだけ Glow を激しくする」といった演出も簡単です。
GlowPulse のパラメータをコードから動的に変更しましょう。


## 例: ボス戦管理スクリプトから GlowPulse を制御する
func _on_phase_changed(new_phase: int) -> void:
    var glow_pulse: GlowPulse = $GlowPulse  # シーン内の GlowPulse を取得
    if not glow_pulse:
        return
    
    match new_phase:
        0:
            # 通常状態: 落ち着いたネオン
            glow_pulse.set_pulse_params(
                new_base = 0.8,
                new_amp = 0.3,
                new_speed = 1.5
            )
        1:
            # フェーズ1: ちょっと緊張感アップ
            glow_pulse.set_pulse_params(
                new_base = 1.0,
                new_amp = 0.6,
                new_speed = 2.5
            )
        2:
            # 最終フェーズ: 激しく点滅
            glow_pulse.set_pulse_params(
                new_base = 1.2,
                new_amp = 1.0,
                new_speed = 5.0
            )

こんな感じで、「Environment を直接いじる巨大な継承クラス」ではなく、「Glow を揺らすだけの小さなコンポーネント」として切り出しておくと、
ステージごと・フェーズごとの演出管理がとても楽になりますね。
ぜひ自分のプロジェクトでも、GlowPulse をベースにした「環境演出コンポーネント群」を育てていきましょう。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

脱・初心者!Godot 4 ゲーム開発の「2歩目」

新品価格
¥831から
(2025/12/13 21:39時点)

Godot4ローグライク入門 ~ダンジョン自動生成~

新品価格
¥831から
(2025/12/13 21:44時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

URLをコピーしました!