370 lines
11 KiB
GDScript
370 lines
11 KiB
GDScript
@tool
|
|
class_name DayNightController
|
|
extends Node
|
|
|
|
signal time_changed(time_of_day: float)
|
|
signal hour_changed(hour: int)
|
|
signal day_changed(day: int)
|
|
signal sunrise_started()
|
|
signal sunset_started()
|
|
signal night_started()
|
|
signal noon_reached()
|
|
|
|
const HOURS_PER_DAY := 24.0
|
|
const SUNRISE_START := 0.2 # ~5 AM
|
|
const SUNRISE_END := 0.3 # ~7 AM
|
|
const SUNSET_START := 0.75 # ~6 PM
|
|
const SUNSET_END := 0.85 # ~8 PM
|
|
const NOON := 0.5
|
|
|
|
@export_group("Time Settings")
|
|
@export var time_of_day: float = 0.25:
|
|
set(value):
|
|
var old_hour := get_current_hour()
|
|
time_of_day = wrapf(value, 0.0, 1.0)
|
|
var new_hour := get_current_hour()
|
|
if old_hour != new_hour:
|
|
hour_changed.emit(new_hour)
|
|
time_changed.emit(time_of_day)
|
|
_check_time_events(old_hour, new_hour)
|
|
|
|
@export var day_length_minutes: float = 30.0:
|
|
set(value):
|
|
day_length_minutes = clampf(value, 1.0, 1440.0)
|
|
|
|
@export var paused: bool = false
|
|
@export var time_scale: float = 1.0
|
|
@export var current_day: int = 1
|
|
|
|
@export_group("Sun Arc")
|
|
@export var sun_arc_tilt: float = 23.5
|
|
@export var sun_arc_offset: float = 0.0
|
|
|
|
@export_group("Moon Arc")
|
|
@export var moon_arc_tilt: float = 15.0
|
|
@export var moon_arc_offset: float = 180.0
|
|
@export var moon_phase: float = 0.0
|
|
|
|
@export_group("Sun Curves")
|
|
@export var sun_color_curve: Gradient
|
|
@export var sun_intensity_curve: Curve
|
|
|
|
@export_group("Moon Curves")
|
|
@export var moon_color_curve: Gradient
|
|
@export var moon_intensity_curve: Curve
|
|
|
|
@export_group("Ambient Curves")
|
|
@export var ambient_color_curve: Gradient
|
|
@export var ambient_intensity_curve: Curve
|
|
|
|
@export_group("Fog Curves")
|
|
@export var fog_color_curve: Gradient
|
|
@export var fog_density_curve: Curve
|
|
|
|
@export_group("Sky Gradient Curves")
|
|
@export var sky_top_color_curve: Gradient
|
|
@export var sky_horizon_color_curve: Gradient
|
|
@export var sky_bottom_color_curve: Gradient
|
|
|
|
@export_group("Stars")
|
|
@export var stars_visibility_curve: Curve
|
|
|
|
var _last_hour: int = -1
|
|
var _sunrise_emitted := false
|
|
var _sunset_emitted := false
|
|
var _night_emitted := false
|
|
var _noon_emitted := false
|
|
|
|
|
|
func _ready() -> void:
|
|
_initialize_default_curves()
|
|
_last_hour = get_current_hour()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if paused:
|
|
return
|
|
|
|
var time_increment := delta / (day_length_minutes * 60.0) * time_scale
|
|
var old_time := time_of_day
|
|
time_of_day += time_increment
|
|
|
|
if time_of_day < old_time:
|
|
current_day += 1
|
|
day_changed.emit(current_day)
|
|
_reset_time_events()
|
|
|
|
|
|
func _initialize_default_curves() -> void:
|
|
if sun_color_curve == null:
|
|
sun_color_curve = Gradient.new()
|
|
sun_color_curve.set_color(0, Color(1.0, 0.6, 0.4)) # Dawn
|
|
sun_color_curve.add_point(0.25, Color(1.0, 0.95, 0.85)) # Morning
|
|
sun_color_curve.add_point(0.5, Color(1.0, 1.0, 0.95)) # Noon
|
|
sun_color_curve.add_point(0.75, Color(1.0, 0.7, 0.4)) # Sunset
|
|
sun_color_curve.set_color(1, Color(1.0, 0.5, 0.3)) # Dusk
|
|
|
|
if sun_intensity_curve == null:
|
|
sun_intensity_curve = Curve.new()
|
|
sun_intensity_curve.add_point(Vector2(0.0, 0.0))
|
|
sun_intensity_curve.add_point(Vector2(0.2, 0.0))
|
|
sun_intensity_curve.add_point(Vector2(0.3, 0.8))
|
|
sun_intensity_curve.add_point(Vector2(0.5, 1.0))
|
|
sun_intensity_curve.add_point(Vector2(0.7, 0.8))
|
|
sun_intensity_curve.add_point(Vector2(0.85, 0.0))
|
|
sun_intensity_curve.add_point(Vector2(1.0, 0.0))
|
|
|
|
if moon_color_curve == null:
|
|
moon_color_curve = Gradient.new()
|
|
moon_color_curve.set_color(0, Color(0.7, 0.8, 1.0))
|
|
moon_color_curve.set_color(1, Color(0.7, 0.8, 1.0))
|
|
|
|
if moon_intensity_curve == null:
|
|
moon_intensity_curve = Curve.new()
|
|
moon_intensity_curve.add_point(Vector2(0.0, 0.4))
|
|
moon_intensity_curve.add_point(Vector2(0.15, 0.3))
|
|
moon_intensity_curve.add_point(Vector2(0.22, 0.0))
|
|
moon_intensity_curve.add_point(Vector2(0.85, 0.0))
|
|
moon_intensity_curve.add_point(Vector2(0.92, 0.3))
|
|
moon_intensity_curve.add_point(Vector2(1.0, 0.4))
|
|
|
|
if ambient_color_curve == null:
|
|
ambient_color_curve = Gradient.new()
|
|
ambient_color_curve.set_color(0, Color(0.15, 0.18, 0.3)) # Night
|
|
ambient_color_curve.add_point(0.25, Color(0.5, 0.55, 0.65)) # Morning
|
|
ambient_color_curve.add_point(0.5, Color(0.6, 0.65, 0.75)) # Noon
|
|
ambient_color_curve.add_point(0.75, Color(0.55, 0.5, 0.55)) # Evening
|
|
ambient_color_curve.set_color(1, Color(0.15, 0.18, 0.3)) # Night
|
|
|
|
if ambient_intensity_curve == null:
|
|
ambient_intensity_curve = Curve.new()
|
|
ambient_intensity_curve.add_point(Vector2(0.0, 0.05))
|
|
ambient_intensity_curve.add_point(Vector2(0.2, 0.1))
|
|
ambient_intensity_curve.add_point(Vector2(0.3, 0.5))
|
|
ambient_intensity_curve.add_point(Vector2(0.5, 0.6))
|
|
ambient_intensity_curve.add_point(Vector2(0.7, 0.5))
|
|
ambient_intensity_curve.add_point(Vector2(0.85, 0.1))
|
|
ambient_intensity_curve.add_point(Vector2(1.0, 0.05))
|
|
|
|
if fog_color_curve == null:
|
|
fog_color_curve = Gradient.new()
|
|
fog_color_curve.set_color(0, Color(0.1, 0.12, 0.2))
|
|
fog_color_curve.add_point(0.25, Color(0.6, 0.65, 0.75))
|
|
fog_color_curve.add_point(0.5, Color(0.7, 0.75, 0.85))
|
|
fog_color_curve.add_point(0.75, Color(0.7, 0.55, 0.5))
|
|
fog_color_curve.set_color(1, Color(0.1, 0.12, 0.2))
|
|
|
|
if fog_density_curve == null:
|
|
fog_density_curve = Curve.new()
|
|
fog_density_curve.add_point(Vector2(0.0, 0.02))
|
|
fog_density_curve.add_point(Vector2(0.25, 0.015))
|
|
fog_density_curve.add_point(Vector2(0.5, 0.01))
|
|
fog_density_curve.add_point(Vector2(0.75, 0.015))
|
|
fog_density_curve.add_point(Vector2(1.0, 0.02))
|
|
|
|
if sky_top_color_curve == null:
|
|
sky_top_color_curve = Gradient.new()
|
|
sky_top_color_curve.set_color(0, Color(0.01, 0.01, 0.04)) # Midnight
|
|
sky_top_color_curve.add_point(0.2, Color(0.03, 0.05, 0.1)) # Pre-dawn
|
|
sky_top_color_curve.add_point(0.3, Color(0.18, 0.35, 0.8)) # Morning
|
|
sky_top_color_curve.add_point(0.5, Color(0.15, 0.35, 0.82)) # Noon
|
|
sky_top_color_curve.add_point(0.75, Color(0.12, 0.15, 0.4)) # Sunset
|
|
sky_top_color_curve.add_point(0.85, Color(0.02, 0.02, 0.06)) # Dusk end
|
|
sky_top_color_curve.set_color(1, Color(0.01, 0.01, 0.04)) # Midnight
|
|
|
|
if sky_horizon_color_curve == null:
|
|
sky_horizon_color_curve = Gradient.new()
|
|
sky_horizon_color_curve.set_color(0, Color(0.03, 0.04, 0.08)) # Midnight
|
|
sky_horizon_color_curve.add_point(0.2, Color(0.3, 0.22, 0.28)) # Pre-dawn
|
|
sky_horizon_color_curve.add_point(0.25, Color(0.85, 0.55, 0.45))# Dawn
|
|
sky_horizon_color_curve.add_point(0.35, Color(0.45, 0.6, 0.85))# Morning
|
|
sky_horizon_color_curve.add_point(0.5, Color(0.5, 0.65, 0.88)) # Noon
|
|
sky_horizon_color_curve.add_point(0.75, Color(0.95, 0.45, 0.25))# Sunset
|
|
sky_horizon_color_curve.add_point(0.85, Color(0.06, 0.06, 0.1))# Dusk end
|
|
sky_horizon_color_curve.set_color(1, Color(0.03, 0.04, 0.08)) # Midnight
|
|
|
|
if sky_bottom_color_curve == null:
|
|
sky_bottom_color_curve = Gradient.new()
|
|
sky_bottom_color_curve.set_color(0, Color(0.02, 0.02, 0.05)) # Midnight
|
|
sky_bottom_color_curve.add_point(0.2, Color(0.12, 0.1, 0.15)) # Pre-dawn
|
|
sky_bottom_color_curve.add_point(0.3, Color(0.35, 0.45, 0.55)) # Morning
|
|
sky_bottom_color_curve.add_point(0.5, Color(0.35, 0.45, 0.55)) # Noon
|
|
sky_bottom_color_curve.add_point(0.75, Color(0.3, 0.2, 0.2)) # Sunset
|
|
sky_bottom_color_curve.add_point(0.85, Color(0.03, 0.03, 0.06))# Dusk end
|
|
sky_bottom_color_curve.set_color(1, Color(0.02, 0.02, 0.05)) # Midnight
|
|
|
|
if stars_visibility_curve == null:
|
|
stars_visibility_curve = Curve.new()
|
|
stars_visibility_curve.add_point(Vector2(0.0, 1.0))
|
|
stars_visibility_curve.add_point(Vector2(0.2, 0.8))
|
|
stars_visibility_curve.add_point(Vector2(0.3, 0.0))
|
|
stars_visibility_curve.add_point(Vector2(0.7, 0.0))
|
|
stars_visibility_curve.add_point(Vector2(0.85, 0.8))
|
|
stars_visibility_curve.add_point(Vector2(1.0, 1.0))
|
|
|
|
|
|
func _check_time_events(old_hour: int, new_hour: int) -> void:
|
|
if time_of_day >= SUNRISE_START and time_of_day < SUNRISE_END and not _sunrise_emitted:
|
|
_sunrise_emitted = true
|
|
sunrise_started.emit()
|
|
|
|
if time_of_day >= SUNSET_START and time_of_day < SUNSET_END and not _sunset_emitted:
|
|
_sunset_emitted = true
|
|
sunset_started.emit()
|
|
|
|
if time_of_day >= SUNSET_END and not _night_emitted:
|
|
_night_emitted = true
|
|
night_started.emit()
|
|
|
|
if time_of_day >= NOON - 0.01 and time_of_day < NOON + 0.01 and not _noon_emitted:
|
|
_noon_emitted = true
|
|
noon_reached.emit()
|
|
|
|
|
|
func _reset_time_events() -> void:
|
|
_sunrise_emitted = false
|
|
_sunset_emitted = false
|
|
_night_emitted = false
|
|
_noon_emitted = false
|
|
|
|
|
|
func get_current_hour() -> int:
|
|
return int(time_of_day * HOURS_PER_DAY)
|
|
|
|
|
|
func get_current_hour_float() -> float:
|
|
return time_of_day * HOURS_PER_DAY
|
|
|
|
|
|
func set_time_from_hours(hours: float) -> void:
|
|
time_of_day = fmod(hours, HOURS_PER_DAY) / HOURS_PER_DAY
|
|
|
|
|
|
func get_sun_direction() -> Vector3:
|
|
var angle := (time_of_day - 0.25) * TAU
|
|
var tilt_rad := deg_to_rad(sun_arc_tilt)
|
|
var offset_rad := deg_to_rad(sun_arc_offset)
|
|
|
|
var direction := Vector3(
|
|
cos(angle + offset_rad),
|
|
sin(angle),
|
|
sin(angle) * sin(tilt_rad)
|
|
).normalized()
|
|
|
|
return direction
|
|
|
|
|
|
func get_moon_direction() -> Vector3:
|
|
var angle := (time_of_day + 0.25) * TAU
|
|
var tilt_rad := deg_to_rad(moon_arc_tilt)
|
|
var offset_rad := deg_to_rad(moon_arc_offset)
|
|
|
|
var direction := Vector3(
|
|
cos(angle + offset_rad),
|
|
sin(angle),
|
|
sin(angle) * sin(tilt_rad)
|
|
).normalized()
|
|
|
|
return direction
|
|
|
|
|
|
func get_sun_color() -> Color:
|
|
if sun_color_curve:
|
|
return sun_color_curve.sample(time_of_day)
|
|
return Color.WHITE
|
|
|
|
|
|
func get_sun_intensity() -> float:
|
|
if sun_intensity_curve:
|
|
return sun_intensity_curve.sample(time_of_day)
|
|
return 1.0
|
|
|
|
|
|
func get_moon_color() -> Color:
|
|
if moon_color_curve:
|
|
return moon_color_curve.sample(time_of_day)
|
|
return Color(0.7, 0.8, 1.0)
|
|
|
|
|
|
func get_moon_intensity() -> float:
|
|
var base_intensity := 1.0
|
|
if moon_intensity_curve:
|
|
base_intensity = moon_intensity_curve.sample(time_of_day)
|
|
var phase_factor := 0.5 + 0.5 * cos(moon_phase * TAU)
|
|
return base_intensity * phase_factor
|
|
|
|
|
|
func get_ambient_color() -> Color:
|
|
if ambient_color_curve:
|
|
return ambient_color_curve.sample(time_of_day)
|
|
return Color(0.5, 0.55, 0.65)
|
|
|
|
|
|
func get_ambient_intensity() -> float:
|
|
if ambient_intensity_curve:
|
|
return ambient_intensity_curve.sample(time_of_day)
|
|
return 0.5
|
|
|
|
|
|
func get_fog_color() -> Color:
|
|
if fog_color_curve:
|
|
return fog_color_curve.sample(time_of_day)
|
|
return Color(0.7, 0.75, 0.85)
|
|
|
|
|
|
func get_fog_density() -> float:
|
|
if fog_density_curve:
|
|
return fog_density_curve.sample(time_of_day)
|
|
return 0.01
|
|
|
|
|
|
func get_stars_visibility() -> float:
|
|
if stars_visibility_curve:
|
|
return stars_visibility_curve.sample(time_of_day)
|
|
return 0.0
|
|
|
|
|
|
func get_sky_top_color() -> Color:
|
|
if sky_top_color_curve:
|
|
return sky_top_color_curve.sample(time_of_day)
|
|
return Color(0.2, 0.4, 0.8)
|
|
|
|
|
|
func get_sky_horizon_color() -> Color:
|
|
if sky_horizon_color_curve:
|
|
return sky_horizon_color_curve.sample(time_of_day)
|
|
return Color(0.6, 0.7, 0.9)
|
|
|
|
|
|
func get_sky_bottom_color() -> Color:
|
|
if sky_bottom_color_curve:
|
|
return sky_bottom_color_curve.sample(time_of_day)
|
|
return Color(0.4, 0.5, 0.6)
|
|
|
|
|
|
func is_daytime() -> bool:
|
|
return time_of_day >= SUNRISE_END and time_of_day < SUNSET_START
|
|
|
|
|
|
func is_nighttime() -> bool:
|
|
return time_of_day < SUNRISE_START or time_of_day >= SUNSET_END
|
|
|
|
|
|
func is_dawn() -> bool:
|
|
return time_of_day >= SUNRISE_START and time_of_day < SUNRISE_END
|
|
|
|
|
|
func is_dusk() -> bool:
|
|
return time_of_day >= SUNSET_START and time_of_day < SUNSET_END
|
|
|
|
|
|
func get_time_period() -> String:
|
|
if is_dawn():
|
|
return "dawn"
|
|
elif is_dusk():
|
|
return "dusk"
|
|
elif is_daytime():
|
|
return "day"
|
|
else:
|
|
return "night"
|