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

250 lines
9.2 KiB
GDScript
Raw Permalink 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
## Disco-Laser-System mit Audio- und Strobe-Reaktivität
## Version mit AudioManager Singleton (OPTIMIERT)
@export_group("Laser Configuration")
@export_range(1, 20) var laser_count: int = 4
@export_range(1.0, 50.0) var placement_radius: float = 10.0
@export_range(0.0, 50.0) var laser_height: float = 8.0
@export_range(1.0, 100.0) var beam_length: float = 20.0
@export_range(0.01, 1.0) var beam_thickness: float = 0.01
@export_group("Light Settings")
@export_range(0.1, 100.0) var light_energy: float = 10.0
@export_range(1.0, 100.0) var light_range: float = 25.0
@export_range(3.5, 90.0) var spot_angle: float = 5.0
@export var laser_colors: Array[Color] = [
Color.RED, Color.BLUE, Color.GREEN, Color.MAGENTA, Color.CYAN, Color.YELLOW
]
@export_group("Laser Direction")
@export_range(-90.0, 90.0) var laser_pitch: float = -30.0
@export_range(-180.0, 180.0) var laser_yaw_offset: float = 0.0
@export var audio_affects_direction: bool = false
@export_range(-90.0, 90.0) var min_audio_pitch: float = -45.0
@export_range(-90.0, 90.0) var max_audio_pitch: float = 0.0
@export_group("Animation")
@export var rotate_lasers: 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 = 20.0
@export_group("Beam Visual")
@export_range(0.0, 10.0) var beam_emission_strength: float = 3.0
@export_range(0.0, 1.0) var beam_transparency: float = 0.3
@export_group("Audio Reactivity")
@export var audio_player: AudioStreamPlayer3D
@export var audio_reactive: bool = false
@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 = 30.0
@export var audio_affects_rotation: bool = false
@export_range(-3.0, 10.0) var min_rotation_speed: float = 0.5
@export_range(-8.0, 10.0) var max_rotation_speed: float = 3.0
@export var audio_affects_emission: bool = true
@export_range(0.0, 20.0) var min_beam_emission: float = 1.0
@export_range(0.0, 20.0) var max_beam_emission: float = 8.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 Effect")
@export var strobe_enabled: bool = false
@export_range(0.1, 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.5
# Interne Variablen
var laser_nodes: Array[Node3D] = []
var initial_rotations: Array[float] = []
var base_laser_rotations: Array[Vector3] = []
var current_y_rotation: Array[float] = []
# Audio (vereinfacht - nutzt AudioManager!)
var current_audio_magnitude: float = 0.0
var target_audio_magnitude: float = 0.0
# Strobe
var strobe_timer: float = 0.0
var strobe_phase_offsets: Array[float] = []
func _ready():
randomize()
_create_lasers()
# Setup Audio mit AudioManager Singleton
if audio_reactive and audio_player:
if AudioManager.setup(audio_player):
print("Laser '%s': Nutzt AudioManager für Audio-Reaktivität" % name)
else:
push_error("Laser '%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)
_animate_lasers(delta)
_apply_final_rotations(delta)
func _create_lasers():
for i in range(laser_count):
var laser = _create_single_laser(i)
laser_nodes.append(laser)
add_child(laser)
var angle = (float(i) / laser_count) * TAU
initial_rotations.append(angle)
current_y_rotation.append(0.0)
_set_laser_base_direction(laser, angle)
base_laser_rotations.append(laser.rotation)
strobe_phase_offsets.append(randf_range(0.0, TAU))
print("Disco Laser System '%s': %d Laser erstellt" % [name, laser_count])
func _create_single_laser(index: int) -> Node3D:
var laser_node = Node3D.new()
laser_node.name = "Laser_%d" % index
var angle = (float(index) / laser_count) * TAU
var x = cos(angle) * placement_radius
var z = sin(angle) * placement_radius
laser_node.position = Vector3(x, laser_height, z)
var color = laser_colors[index % laser_colors.size()]
var spot = SpotLight3D.new()
spot.name = "SpotLight"
spot.light_color = color
spot.light_energy = light_energy
spot.spot_range = light_range
spot.spot_angle = spot_angle
spot.shadow_enabled = false
laser_node.add_child(spot)
var beam = _create_beam_mesh(color)
laser_node.add_child(beam)
return laser_node
func _set_laser_base_direction(laser: Node3D, _circle_angle: float):
var target_pos = Vector3(0, laser_height * 0.5, 0)
laser.look_at(target_pos, Vector3.UP)
var base_y_rotation = laser.rotation.y
laser.rotation = Vector3.ZERO
laser.rotation.y = base_y_rotation + deg_to_rad(laser_yaw_offset)
laser.rotation.x = deg_to_rad(laser_pitch)
func _create_beam_mesh(color: Color) -> MeshInstance3D:
var mesh_instance = MeshInstance3D.new()
mesh_instance.name = "BeamMesh"
var cylinder = CylinderMesh.new()
cylinder.top_radius = beam_thickness
cylinder.bottom_radius = beam_thickness * 0.8
cylinder.height = beam_length
mesh_instance.mesh = cylinder
mesh_instance.position = Vector3(0, 0, -beam_length * 0.5)
mesh_instance.rotation.x = deg_to_rad(90)
var material = StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
material.albedo_color = color
material.emission_enabled = true
material.emission = color
material.emission_energy_multiplier = beam_emission_strength
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
material.albedo_color.a = 1.0 - beam_transparency
material.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
material.cull_mode = BaseMaterial3D.CULL_DISABLED
mesh_instance.material_override = material
return mesh_instance
## 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 laser in laser_nodes:
if audio_affects_intensity:
var energy = lerp(min_energy, max_energy, current_audio_magnitude)
var spot = laser.get_node("SpotLight") as SpotLight3D
if spot:
spot.light_energy = energy
if audio_affects_emission:
var emission = lerp(min_beam_emission, max_beam_emission, current_audio_magnitude)
var beam = laser.get_node("BeamMesh") as MeshInstance3D
if beam and beam.material_override:
var mat = beam.material_override as StandardMaterial3D
mat.emission_energy_multiplier = emission
func _apply_strobe_effect(delta):
strobe_timer += delta * strobe_speed
var magnitude_normalized = current_audio_magnitude
for i in range(laser_nodes.size()):
var laser = laser_nodes[i]
if not is_instance_valid(laser):
continue
var phase = strobe_timer + strobe_phase_offsets[i]
var light_on = sin(phase) > 0.0 and magnitude_normalized > strobe_threshold
var spot = laser.get_node_or_null("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
var beam = laser.get_node_or_null("BeamMesh") as MeshInstance3D
if beam:
if light_on:
beam.visible = true
if beam.material_override and beam.material_override is StandardMaterial3D:
var mat = beam.material_override as StandardMaterial3D
mat.emission_energy_multiplier = max_beam_emission
else:
beam.visible = false
func _animate_lasers(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_lasers or (audio_reactive and audio_affects_rotation):
for i in range(laser_nodes.size()):
current_y_rotation[i] += current_rotation_speed * delta
func _apply_final_rotations(_delta):
for i in range(laser_nodes.size()):
var laser = laser_nodes[i]
var base_rot = base_laser_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
if audio_reactive and audio_affects_direction:
var audio_pitch = lerp(min_audio_pitch, max_audio_pitch, current_audio_magnitude)
pitch_offset += audio_pitch - laser_pitch
final_rotation.x = base_rot.x + deg_to_rad(pitch_offset)
laser.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")