Skip to content

Instantly share code, notes, and snippets.

@bingeboy
Created June 5, 2025 05:25
Show Gist options
  • Save bingeboy/3ec587d0e903922a59b514dbe810db5e to your computer and use it in GitHub Desktop.
Save bingeboy/3ec587d0e903922a59b514dbe810db5e to your computer and use it in GitHub Desktop.
# 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)
@bingeboy
Copy link
Author

bingeboy commented Jun 5, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment