Created
June 5, 2025 05:25
-
-
Save bingeboy/3ec587d0e903922a59b514dbe810db5e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SunSystem.gd - Complete file with EventBus integration | |
extends Node | |
class_name SunSystem | |
# Sun Parameters | |
@export_group("Sun Position") | |
@export var sun_azimuth: float = 43.0 | |
@export var sun_elevation: float = 45.0 | |
@export var sun_distance: float = 100.0 | |
@export_group("Time of Day") | |
@export var use_time_cycle: bool = false | |
@export var day_length_minutes: float = 10.0 | |
@export var current_time_hours: float = 12.0 | |
@export_group("Lighting") | |
@export var sun_energy: float = 1.0 | |
@export var sun_color: Color = Color.WHITE | |
@export var ambient_energy: float = 0.3 | |
@export var ambient_color: Color = Color(0.4, 0.6, 1.0, 1.0) | |
@export_group("Shadows") | |
@export var enable_shadows: bool = true | |
@export var shadow_bias: float = 0.1 | |
@export var shadow_normal_bias: float = 2.0 | |
# Node references | |
@onready var directional_light: DirectionalLight3D = $DirectionalLight3D | |
@onready var environment_node: Node3D = get_viewport().get_camera_3d().get_parent() if get_viewport().get_camera_3d() else null | |
# Time tracking | |
var time_speed: float = 1.0 | |
var is_day_time: bool = true | |
var previous_day_state: bool = true | |
func _ready() -> void: | |
# Wait for EventBusManager to be ready | |
if not EventBusManager: | |
await get_tree().process_frame | |
_setup_sun_light() | |
_setup_environment() | |
_update_sun_position() | |
# Fire initial state using weather bus | |
var weather_bus = EventBusManager.get_weather_bus() | |
if weather_bus: | |
EventBusManager.fire_weather(weather_bus.LIGHTING_INITIALIZED, { | |
"sun_position": directional_light.global_position if directional_light else Vector3.ZERO, | |
"is_day": is_day_time, | |
"time_hours": current_time_hours, | |
"sun_energy": sun_energy, | |
"sun_direction": get_sun_direction() | |
}) | |
func _process(delta: float) -> void: | |
if use_time_cycle: | |
_update_time_cycle(delta) | |
_update_sun_position() | |
_update_lighting_based_on_time() | |
func _setup_sun_light() -> void: | |
if not directional_light: | |
push_error("DirectionalLight3D node not found! Make sure it's a child of SunSystem.") | |
return | |
directional_light.light_energy = sun_energy | |
directional_light.light_color = sun_color | |
directional_light.shadow_enabled = enable_shadows | |
directional_light.shadow_bias = shadow_bias | |
directional_light.shadow_normal_bias = shadow_normal_bias | |
# Improve Shadow Quality | |
directional_light.directional_shadow_mode = DirectionalLight3D.SHADOW_PARALLEL_4_SPLITS | |
directional_light.directional_shadow_max_distance = 200.0 | |
func _setup_environment() -> void: | |
var env = Environment.new() | |
# Sky setup | |
env.background_mode = Environment.BG_SKY | |
env.sky = Sky.new() | |
env.sky.sky_material = ProceduralSkyMaterial.new() | |
# Configure procedural sky | |
var sky_material = env.sky.sky_material as ProceduralSkyMaterial | |
sky_material.sky_top_color = Color(0.385, 0.647, 0.909) | |
sky_material.sky_horizon_color = Color(0.646, 0.655, 0.671) | |
sky_material.ground_bottom_color = Color(0.1, 0.1, 0.13) | |
sky_material.ground_horizon_color = Color(0.37, 0.33, 0.31) | |
# Ambient lighting | |
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY | |
env.ambient_light_energy = ambient_energy | |
env.ambient_light_color = ambient_color | |
# Apply to camera or scene | |
if get_viewport().get_camera_3d(): | |
get_viewport().get_camera_3d().environment = env | |
else: | |
get_tree().current_scene.set_meta("default_environment", env) | |
func _update_time_cycle(delta: float) -> void: | |
if not use_time_cycle: | |
return | |
var hours_per_second = 24.0 / (day_length_minutes * 60.0) | |
current_time_hours += hours_per_second * delta * time_speed | |
# Wrap around 24 hours | |
if current_time_hours >= 24.0: | |
current_time_hours = 0.0 | |
var day_count = get_meta("day_count", 0) + 1 | |
set_meta("day_count", day_count) | |
var weather_bus = EventBusManager.get_weather_bus() | |
if weather_bus: | |
EventBusManager.fire_weather(weather_bus.NEW_DAY_STARTED, { | |
"day_count": day_count, | |
"time_hours": current_time_hours | |
}) | |
# Update day/night state | |
previous_day_state = is_day_time | |
is_day_time = current_time_hours >= 6.0 and current_time_hours <= 18.0 | |
if previous_day_state != is_day_time: | |
var weather_bus = EventBusManager.get_weather_bus() | |
if weather_bus: | |
weather_bus.fire_day_night_changed( | |
is_day_time, | |
current_time_hours, | |
"sunrise" if is_day_time else "sunset" | |
) | |
# Fire specific transition events | |
if is_day_time: | |
EventBusManager.fire_weather(weather_bus.SUNRISE_STARTED, { | |
"time_hours": current_time_hours | |
}) | |
else: | |
EventBusManager.fire_weather(weather_bus.SUNSET_STARTED, { | |
"time_hours": current_time_hours | |
}) | |
func _update_sun_position() -> void: | |
if not directional_light: | |
return | |
var azimuth_rad: float | |
var elevation_rad: float | |
if use_time_cycle: | |
var normalized_time = (current_time_hours - 6.0) / 12.0 | |
normalized_time = clamp(normalized_time, 0.0, 1.0) | |
azimuth_rad = deg_to_rad(90 + normalized_time * 180) | |
elevation_rad = deg_to_rad(sin(normalized_time * PI) * 90) | |
else: | |
azimuth_rad = deg_to_rad(sun_azimuth) | |
elevation_rad = deg_to_rad(sun_elevation) | |
var x = cos(elevation_rad) * cos(azimuth_rad) | |
var y = sin(elevation_rad) | |
var z = cos(elevation_rad) * sin(azimuth_rad) | |
var sun_direction = Vector3(x, y, z).normalized() | |
directional_light.global_position = sun_direction * sun_distance | |
directional_light.look_at(Vector3.ZERO, Vector3.UP) | |
# Fire position update event | |
EventBusManager.fire_weather("sun_position_changed", { | |
"position": directional_light.global_position, | |
"direction": sun_direction, | |
"azimuth": rad_to_deg(azimuth_rad), | |
"elevation": rad_to_deg(elevation_rad) | |
}) | |
func _update_lighting_based_on_time() -> void: | |
if not use_time_cycle or not directional_light: | |
return | |
var time_factor = 0.0 | |
var previous_energy = directional_light.light_energy | |
if current_time_hours >= 6.0 and current_time_hours <= 18.0: | |
var day_progress = (current_time_hours - 6.0) / 12.0 | |
time_factor = sin(day_progress * PI) | |
else: | |
time_factor = 0.1 | |
directional_light.light_energy = sun_energy * time_factor | |
# Update color based on time | |
var new_color = sun_color | |
if current_time_hours < 7.0 or current_time_hours > 17.0: | |
new_color = Color(1.0, 0.8, 0.6) # Dawn/Dusk | |
elif current_time_hours < 8.0 or current_time_hours > 16.0: | |
new_color = Color(1.0, 0.95, 0.8) # Early morning/late afternoon | |
directional_light.light_color = new_color | |
# Fire lighting change event if significant change | |
if abs(previous_energy - directional_light.light_energy) > 0.1: | |
var weather_bus = EventBusManager.get_weather_bus() | |
if weather_bus: | |
weather_bus.fire_sun_intensity_changed( | |
directional_light.light_energy, | |
time_factor, | |
new_color, | |
current_time_hours | |
) | |
# ============================================================================ | |
# PUBLIC METHODS | |
# ============================================================================ | |
func set_time_of_day(hours: float) -> void: | |
var old_time = current_time_hours | |
current_time_hours = clamp(hours, 0.0, 24.0) | |
_update_sun_position() | |
_update_lighting_based_on_time() | |
EventBusManager.fire_weather("time_manually_set", { | |
"old_time": old_time, | |
"new_time": current_time_hours | |
}) | |
func set_sun_position(azimuth: float, elevation: float) -> void: | |
sun_azimuth = azimuth | |
sun_elevation = clamp(elevation, 0.0, 90.0) | |
_update_sun_position() | |
EventBusManager.fire_weather("sun_manually_positioned", { | |
"azimuth": sun_azimuth, | |
"elevation": sun_elevation | |
}) | |
func toggle_time_cycle() -> void: | |
use_time_cycle = !use_time_cycle | |
EventBusManager.fire_weather("time_cycle_toggled", { | |
"enabled": use_time_cycle, | |
"current_time": current_time_hours | |
}) | |
func set_time_speed(speed: float) -> void: | |
time_speed = max(0.0, speed) | |
EventBusManager.fire_weather("time_speed_changed", { | |
"speed": time_speed | |
}) | |
func get_sun_direction() -> Vector3: | |
if directional_light: | |
return -directional_light.global_transform.basis.z | |
return Vector3.DOWN | |
func is_sun_visible() -> bool: | |
return sun_elevation > 0.0 and (not use_time_cycle or is_day_time) | |
func get_lighting_info() -> Dictionary: | |
return { | |
"time_hours": current_time_hours, | |
"is_day": is_day_time, | |
"sun_energy": directional_light.light_energy if directional_light else 0.0, | |
"sun_direction": get_sun_direction(), | |
"sun_color": directional_light.light_color if directional_light else Color.WHITE, | |
"time_cycle_enabled": use_time_cycle, | |
"time_speed": time_speed | |
} | |
# ============================================================================ | |
# DEBUG INPUT HANDLING | |
# ============================================================================ | |
func _input(event): | |
if not Engine.is_editor_hint(): | |
return | |
# Debug controls (only in editor) | |
if event.is_action_pressed("ui_up"): | |
sun_elevation = min(sun_elevation + 5.0, 90.0) | |
elif event.is_action_pressed("ui_down"): | |
sun_elevation = max(sun_elevation - 5.0, 0.0) | |
elif event.is_action_pressed("ui_left"): | |
sun_azimuth = fmod(sun_azimuth - 5.0, 360.0) | |
elif event.is_action_pressed("ui_right"): | |
sun_azimuth = fmod(sun_azimuth + 5.0, 360.0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you want to see the full implementation, I will show you Just let me know... it might be a departure from ur current architecture. I had it working without a message bus so I can dig that up in my git log if needed.