Files
g-world/scripts/game/sky_view.gd
T

116 lines
3.8 KiB
GDScript

class_name SkyView
extends Node2D
## Небо над убежищем: градиент по времени суток, звёзды, солнце и луна.
##
## Рисуется первым, за пейзажем и породой. Что рисовать — решает SkyCycle,
## насколько это видно сквозь облака — WeatherSystem.
const SUN_TEXTURE := preload("res://background/PNG/sun.png")
const MOON_TEXTURE := preload("res://background/PNG/moon_full.png")
## Небо начинается здесь и идёт до уровня земли (y = 0).
const TOP := -1700.0
const LEFT := -1600.0
const RIGHT := 2900.0
## Дуга, по которой идут светила.
const ARC_RADIUS := Vector2(900.0, 620.0)
const ARC_BASE := -40.0
const STAR_COUNT := 240
const STAR_SEED := 20120401
## Час суток; сцена обновляет его вслед за часами.
var hour := 6.0
## Плотность облаков 0..1 — сквозь них хуже видно звёзды и светила.
var clouds := 0.0
var _stars: Array[Dictionary] = []
func _ready() -> void:
_generate_stars()
## Одним вызовом — чтобы не гонять перерисовку дважды.
func set_conditions(hour_of_day: float, cloudiness: float) -> void:
hour = hour_of_day
clouds = cloudiness
queue_redraw()
func _draw() -> void:
var sky := SkyCycle.sample(hour)
_draw_gradient(sky)
_draw_stars(float(sky["stars"]))
_draw_bodies()
func _draw_gradient(sky: Dictionary) -> void:
var points := PackedVector2Array([
Vector2(LEFT, TOP), Vector2(RIGHT, TOP), Vector2(RIGHT, 0.0), Vector2(LEFT, 0.0),
])
var top: Color = sky["top"]
var bottom: Color = sky["bottom"]
draw_polygon(points, PackedColorArray([top, top, bottom, bottom]))
func _draw_stars(brightness: float) -> void:
# Плотная облачность гасит звёзды почти полностью.
var visibility := brightness * (1.0 - clampf(clouds, 0.0, 1.0) * 0.9)
if visibility <= 0.01:
return
for star: Dictionary in _stars:
var alpha: float = float(star["alpha"]) * visibility
draw_circle(star["position"], float(star["radius"]), Color(star["tint"], alpha))
func _draw_bodies() -> void:
# Сквозь облака светило просвечивает, но тускло.
var through := 1.0 - clampf(clouds, 0.0, 1.0) * 0.75
var sun := SkyCycle.sun_progress(hour)
if sun >= 0.0:
var position := _arc_position(sun)
draw_circle(position, 150.0, Color("#ffd27a", 0.10 * through))
draw_circle(position, 92.0, Color("#ffdc93", 0.14 * through))
_draw_body(SUN_TEXTURE, position, 1.7, Color(1.0, 1.0, 1.0, through))
return
var moon := SkyCycle.moon_progress(hour)
if moon >= 0.0:
var position := _arc_position(moon)
draw_circle(position, 110.0, Color("#cfe0ff", 0.07 * through))
_draw_body(MOON_TEXTURE, position, 1.35, Color(1.0, 1.0, 1.0, through))
func _draw_body(texture: Texture2D, position: Vector2, scale: float, tint: Color) -> void:
var size := Vector2(texture.get_size()) * scale
draw_texture_rect(texture, Rect2(position - size * 0.5, size), false, tint)
## Точка на дуге: 0 — у левого горизонта, 1 — у правого.
func _arc_position(progress: float) -> Vector2:
var angle := PI * (1.0 - progress)
var center_x := VaultGrid.bounds().get_center().x
return Vector2(
center_x + cos(angle) * ARC_RADIUS.x,
ARC_BASE - sin(angle) * ARC_RADIUS.y
)
func _generate_stars() -> void:
var rng := RandomNumberGenerator.new()
rng.seed = STAR_SEED
for index in STAR_COUNT:
_stars.append({
"position": Vector2(
rng.randf_range(LEFT, RIGHT), rng.randf_range(TOP, -140.0)
),
"radius": rng.randf_range(1.4, 3.6),
"alpha": rng.randf_range(0.35, 1.0),
"tint": Color("#bcd2ff").lerp(Color("#fff2d8"), rng.randf()),
})