vrp-gruppe-sechs/disco-escape-roomv-5/spotlight_animation.gd

268 lines
9.4 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

extends Node3D
class_name SpotlightAnimator
## Disco-Spotlight-System mit Audio-Reaktivität, Farbwechsel und Strobe
## Version mit AudioManager Singleton (OPTIMIERT)
@export_group("Spotlight Configuration")
@export_range(1, 20) var spotlight_count: int = 6
@export_range(1.0, 50.0) var placement_radius: float = 10.0
@export_range(0.0, 50.0) var spotlight_height: float = 5.0
@export_range(1.0, 100.0) var light_range: float = 20.0
@export_range(5.0, 90.0) var spot_angle: float = 25.0
@export_group("Color Settings")
@export var color_palette_1: Array[Color] = [Color.RED, Color.ORANGE, Color.MAGENTA]
@export var color_palette_2: Array[Color] = [Color.BLUE, Color.CYAN, Color.DEEP_SKY_BLUE]
@export var use_random_palettes: bool = true
@export var color_change_speed: float = 2.0
@export_group("Light Settings")
@export_range(0.1, 100.0) var light_energy: float = 15.0
@export_range(0.0, 10.0) var base_emission_strength: float = 2.0
@export_group("Spotlight Direction")
@export_range(-90.0, 90.0) var spotlight_pitch: float = -45.0
@export_range(-180.0, 180.0) var spotlight_yaw_offset: float = 0.0
@export_group("Animation")
@export var rotate_spotlights: bool = true
@export_range(-10.0, 10.0) var rotation_speed: float = 1.0
@export var vertical_movement: bool = true
@export_range(0.1, 5.0) var vertical_speed: float = 0.5
@export_range(0.0, 45.0) var vertical_amplitude: float = 15.0
@export_group("Audio Reactivity")
@export var audio_player: AudioStreamPlayer3D
@export var audio_reactive: bool = true
@export_enum("Bass", "Mitten", "Höhen", "Alle") var frequency_range: int = 0
@export var audio_affects_intensity: bool = true
@export_range(0.0, 100.0) var min_energy: float = 5.0
@export_range(0.0, 100.0) var max_energy: float = 40.0
@export var audio_affects_rotation: bool = true
@export_range(-10.0, 10.0) var min_rotation_speed: float = 0.5
@export_range(-10.0, 10.0) var max_rotation_speed: float = 4.0
@export var audio_affects_color: bool = true
@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 Effect")
@export var strobe_enabled: bool = false
@export_range(1.0, 30.0) var strobe_speed: float = 12.0
@export_range(0.0, 100.0) var strobe_energy: float = 30.0
@export_range(0.0, 1.0) var strobe_threshold: float = 0.3
@export var async_strobe: bool = true
# Interne Variablen
var spotlight_nodes: Array[Node3D] = []
var spotlight_colors: Array[Array] = []
var current_color_indices: Array[int] = []
var color_transition_timers: Array[float] = []
var initial_rotations: Array[float] = []
var base_spotlight_rotations: Array[Vector3] = []
var current_y_rotation: Array[float] = []
var strobe_phase_offsets: Array[float] = []
# Audio (vereinfacht!)
var current_audio_magnitude: float = 0.0
var target_audio_magnitude: float = 0.0
var strobe_timer: float = 0.0
func _ready():
randomize()
_create_spotlights()
# Setup Audio mit AudioManager Singleton
if audio_reactive and audio_player:
if AudioManager.setup(audio_player):
print("Spotlight '%s': Nutzt AudioManager für Audio-Reaktivität" % name)
else:
push_error("Spotlight '%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 audio_affects_color:
_update_color_transitions(delta)
_animate_spotlights(delta)
_apply_final_rotations(delta)
func _create_spotlights():
var available_palettes = [color_palette_1, color_palette_2]
for i in range(spotlight_count):
var spotlight = _create_single_spotlight(i, available_palettes)
spotlight_nodes.append(spotlight)
add_child(spotlight)
var angle = (float(i) / spotlight_count) * TAU
initial_rotations.append(angle)
current_y_rotation.append(0.0)
current_color_indices.append(0)
color_transition_timers.append(randf_range(0.0, color_change_speed))
strobe_phase_offsets.append(randf_range(0.0, TAU) if async_strobe else 0.0)
_set_spotlight_base_direction(spotlight, angle)
base_spotlight_rotations.append(spotlight.rotation)
print("Spotlight Animator '%s': %d Spotlights erstellt" % [name, spotlight_count])
func _create_single_spotlight(index: int, palettes: Array) -> Node3D:
var spotlight_node = Node3D.new()
spotlight_node.name = "Spotlight_%d" % index
# Position im Kreis
var angle = (float(index) / spotlight_count) * TAU
var x = cos(angle) * placement_radius
var z = sin(angle) * placement_radius
spotlight_node.position = Vector3(x, spotlight_height, z)
# Farbpalette zuweisen
var palette: Array[Color] = []
if use_random_palettes:
palette = palettes[randi() % palettes.size()].duplicate()
else:
palette = palettes[index % palettes.size()].duplicate()
spotlight_colors.append(palette)
# SpotLight3D erstellen
var spot = SpotLight3D.new()
spot.name = "SpotLight"
spot.light_color = palette[0]
spot.light_energy = light_energy
spot.spot_range = light_range
spot.spot_angle = spot_angle
spot.shadow_enabled = true
spotlight_node.add_child(spot)
return spotlight_node
func _create_emission_texture(color: Color) -> GradientTexture2D:
var gradient = Gradient.new()
gradient.set_color(0, color)
gradient.set_color(1, Color(color.r, color.g, color.b, 0.0))
var gradient_texture = GradientTexture2D.new()
gradient_texture.gradient = gradient
gradient_texture.fill = GradientTexture2D.FILL_RADIAL
gradient_texture.width = 256
gradient_texture.height = 256
return gradient_texture
func _set_spotlight_base_direction(spotlight: Node3D, _circle_angle: float):
var target_pos = Vector3(0, 0, 0)
spotlight.look_at(target_pos, Vector3.UP)
var base_y_rotation = spotlight.rotation.y
spotlight.rotation = Vector3.ZERO
spotlight.rotation.y = base_y_rotation + deg_to_rad(spotlight_yaw_offset)
spotlight.rotation.x = deg_to_rad(spotlight_pitch)
## OPTIMIERT: Nutzt Rohdaten vom AudioManager, verarbeitet aber lokal
func _update_audio_reactivity(delta):
# Hole RAW Magnitude vom Manager (ohne Sensitivity/Smoothing)
var raw_magnitude = AudioManager.get_raw_magnitude(frequency_range)
# Lokale Verarbeitung mit eigenen Parametern für individuelles Verhalten
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)
if strobe_enabled:
_apply_strobe_effect(delta)
else:
_apply_audio_visual_effects()
func _apply_audio_visual_effects():
for spotlight in spotlight_nodes:
var spot = spotlight.get_node("SpotLight") as SpotLight3D
if spot and audio_affects_intensity:
var energy = lerp(min_energy, max_energy, current_audio_magnitude)
spot.light_energy = energy
func _apply_strobe_effect(delta):
strobe_timer += delta * strobe_speed
var magnitude_normalized = current_audio_magnitude
for i in range(spotlight_nodes.size()):
var spotlight = spotlight_nodes[i]
if not is_instance_valid(spotlight):
continue
var phase = strobe_timer + strobe_phase_offsets[i]
var light_on = sin(phase) > 0.0 and magnitude_normalized > strobe_threshold
var spot = spotlight.get_node("SpotLight") as SpotLight3D
if spot:
if light_on:
spot.light_energy = strobe_energy
spot.visible = true
else:
spot.light_energy = 0.0
spot.visible = false
func _update_color_transitions(delta):
for i in range(spotlight_nodes.size()):
color_transition_timers[i] -= delta
if color_transition_timers[i] <= 0.0:
# Farbwechsel triggern (verstärkt bei starkem Beat)
var change_speed = color_change_speed
if audio_reactive:
change_speed = lerp(color_change_speed * 2.0, color_change_speed * 0.5, current_audio_magnitude)
color_transition_timers[i] = change_speed
# Nächste Farbe
current_color_indices[i] = (current_color_indices[i] + 1) % spotlight_colors[i].size()
var new_color = spotlight_colors[i][current_color_indices[i]]
# Farbe auf Spotlight anwenden
var spotlight = spotlight_nodes[i]
var spot = spotlight.get_node("SpotLight") as SpotLight3D
if spot:
spot.light_color = new_color
func _animate_spotlights(delta):
var current_rotation_speed = rotation_speed
if audio_reactive and audio_affects_rotation:
current_rotation_speed = lerp(min_rotation_speed, max_rotation_speed, current_audio_magnitude)
if rotate_spotlights or (audio_reactive and audio_affects_rotation):
for i in range(spotlight_nodes.size()):
current_y_rotation[i] += current_rotation_speed * delta
func _apply_final_rotations(_delta):
for i in range(spotlight_nodes.size()):
var spotlight = spotlight_nodes[i]
var base_rot = base_spotlight_rotations[i]
var final_rotation = base_rot
final_rotation.y = base_rot.y + current_y_rotation[i]
var pitch_offset = 0.0
if vertical_movement:
var time_offset = initial_rotations[i]
pitch_offset += sin(Time.get_ticks_msec() * 0.001 * vertical_speed + time_offset) * vertical_amplitude
final_rotation.x = base_rot.x + deg_to_rad(pitch_offset)
spotlight.rotation = final_rotation
# === Public Methods ===
func enable_strobe(enable: bool):
strobe_enabled = enable
if enable:
audio_affects_intensity = false
print("Stroboskop aktiviert Audio-Intensity deaktiviert")
else:
print("Stroboskop deaktiviert Audio-Intensity kann wieder genutzt werden")
func set_color_palette(spotlight_index: int, new_palette: Array[Color]):
if spotlight_index >= 0 and spotlight_index < spotlight_colors.size():
spotlight_colors[spotlight_index] = new_palette