139 lines
4.3 KiB
GDScript
139 lines
4.3 KiB
GDScript
extends Node3D
|
|
class_name StrobeEffect
|
|
|
|
## Audio-reaktiver Stroboskop-Effekt für Light3D-Nodes
|
|
## Version mit AudioManager Singleton (OPTIMIERT)
|
|
|
|
@export_group("Target Lights")
|
|
@export var target_lights: Array[Light3D] = []
|
|
|
|
@export_group("Audio Settings")
|
|
@export var audio_player: AudioStreamPlayer3D
|
|
@export var audio_reactive: bool = true
|
|
@export_enum("Bass", "Mitten", "Höhen", "Alle") var frequency_range: int = 0
|
|
@export_range(0.1, 5.0) var audio_sensitivity: float = 1.5
|
|
@export_range(0.0, 1.0) var audio_smoothing: float = 0.2
|
|
|
|
@export_group("Strobe Settings")
|
|
@export var strobe_enabled: bool = true
|
|
@export_range(1.0, 30.0) var strobe_speed: float = 12.0
|
|
@export_range(0.0, 100.0) var strobe_energy: float = 25.0
|
|
@export_range(0.0, 1.0) var strobe_threshold: float = 0.3
|
|
@export var async_strobes: bool = true
|
|
|
|
# Interne Variablen
|
|
var current_audio_magnitude: float = 0.0
|
|
var target_audio_magnitude: float = 0.0
|
|
var strobe_timer: float = 0.0
|
|
var strobe_phase_offsets: Array[float] = []
|
|
var original_energies: Array[float] = []
|
|
|
|
func _ready():
|
|
randomize()
|
|
_initialize_lights()
|
|
|
|
# Setup Audio mit AudioManager Singleton
|
|
if audio_reactive and audio_player:
|
|
if AudioManager.setup(audio_player):
|
|
print("StrobeEffect '%s': Nutzt AudioManager für Audio-Reaktivität" % name)
|
|
else:
|
|
push_error("StrobeEffect '%s': AudioManager-Setup fehlgeschlagen!" % name)
|
|
audio_reactive = false
|
|
|
|
func _process(delta):
|
|
# Hole Audio-Daten vom Manager (bereits berechnet!)
|
|
if audio_reactive and AudioManager.is_playing():
|
|
_update_audio_reactivity(delta)
|
|
|
|
if strobe_enabled:
|
|
_apply_strobe_effect(delta)
|
|
|
|
func _initialize_lights():
|
|
# Speichere Original-Energiewerte und erstelle Phasen-Offsets
|
|
for light in target_lights:
|
|
if is_instance_valid(light):
|
|
original_energies.append(light.light_energy)
|
|
if async_strobes:
|
|
strobe_phase_offsets.append(randf_range(0.0, TAU))
|
|
else:
|
|
strobe_phase_offsets.append(0.0)
|
|
|
|
print("StrobeEffect '%s': %d Lichter initialisiert" % [name, target_lights.size()])
|
|
|
|
## OPTIMIERT: Nutzt vorberechnete Werte vom AudioManager
|
|
func _update_audio_reactivity(_delta):
|
|
# Hole Magnitude vom Manager (bereits gefiltert nach frequency_range)
|
|
var raw_magnitude = AudioManager.get_magnitude(frequency_range)
|
|
|
|
# Anwenden von Sensitivity und Smoothing
|
|
target_audio_magnitude = clamp(raw_magnitude * audio_sensitivity, 0.0, 1.0)
|
|
current_audio_magnitude = lerp(current_audio_magnitude, target_audio_magnitude, 1.0 - audio_smoothing)
|
|
|
|
func _apply_strobe_effect(delta):
|
|
strobe_timer += delta * strobe_speed
|
|
var magnitude_normalized = current_audio_magnitude
|
|
|
|
for i in range(target_lights.size()):
|
|
if i >= target_lights.size():
|
|
break
|
|
|
|
var light = target_lights[i]
|
|
if not is_instance_valid(light):
|
|
continue
|
|
|
|
var phase = strobe_timer + strobe_phase_offsets[i]
|
|
var light_on = sin(phase) > 0.0 and magnitude_normalized > strobe_threshold
|
|
|
|
# Strobe-Effekt anwenden
|
|
if light_on:
|
|
light.light_energy = strobe_energy
|
|
light.visible = true
|
|
else:
|
|
light.light_energy = 0.0
|
|
light.visible = false
|
|
|
|
# === Public Methods ===
|
|
|
|
func add_light(light: Light3D):
|
|
"""Fügt ein Licht zum Strobe-Effekt hinzu"""
|
|
if light and not light in target_lights:
|
|
target_lights.append(light)
|
|
original_energies.append(light.light_energy)
|
|
if async_strobes:
|
|
strobe_phase_offsets.append(randf_range(0.0, TAU))
|
|
else:
|
|
strobe_phase_offsets.append(0.0)
|
|
|
|
func remove_light(light: Light3D):
|
|
"""Entfernt ein Licht vom Strobe-Effekt"""
|
|
var idx = target_lights.find(light)
|
|
if idx != -1:
|
|
target_lights.remove_at(idx)
|
|
original_energies.remove_at(idx)
|
|
strobe_phase_offsets.remove_at(idx)
|
|
|
|
func enable_strobe(enable: bool):
|
|
"""Aktiviert/Deaktiviert den Strobe-Effekt"""
|
|
strobe_enabled = enable
|
|
if not enable:
|
|
# Stelle Original-Energien wieder her
|
|
for i in range(target_lights.size()):
|
|
if is_instance_valid(target_lights[i]) and i < original_energies.size():
|
|
target_lights[i].light_energy = original_energies[i]
|
|
target_lights[i].visible = true
|
|
|
|
func set_all_lights_in_parent():
|
|
"""Automatisch alle Light3D-Kinder des Parent-Nodes hinzufügen"""
|
|
if not get_parent():
|
|
return
|
|
|
|
target_lights.clear()
|
|
original_energies.clear()
|
|
strobe_phase_offsets.clear()
|
|
|
|
for child in get_parent().get_children():
|
|
if child is Light3D:
|
|
add_light(child)
|
|
|
|
print("StrobeEffect '%s': %d Lichter automatisch gefunden" % [name, target_lights.size()])
|