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.0amplitude: 0.6speed: 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 をベースにした「環境演出コンポーネント群」を育てていきましょう。




