Randoloot/scripts/game/space_level_background.gd
2026-10-15 18:51:38 +02:00

75 lines
2.9 KiB
GDScript

extends Node2D
const WORLD_SIZE := Vector2(5200.0, 3600.0)
const FAR_TEXTURE := preload("res://assets/background/space/deep_space.png")
const DUST_TEXTURE := preload("res://assets/background/space/nebula_dust.png")
const STAR_TILE := Vector2(2048.0, 1536.0)
var _far_sprite: Sprite2D
func _ready() -> void:
# Let the engine synchronize parallax with the interpolated camera transform.
# No camera sampling or per-frame rebuilding of draw commands is needed.
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
var distant := _make_layer("DistantGalaxies", 0.04)
_far_sprite = Sprite2D.new()
_far_sprite.texture = FAR_TEXTURE
_far_sprite.modulate = Color(0.8, 0.85, 0.95)
distant.add_child(_far_sprite)
_resize_background()
get_viewport().size_changed.connect(_resize_background)
var dust := _make_layer("NebulaDust", 0.14)
dust.repeat_size = Vector2(DUST_TEXTURE.get_size())
dust.repeat_times = 3
var dust_sprite := Sprite2D.new()
dust_sprite.texture = DUST_TEXTURE
dust_sprite.centered = false
dust_sprite.modulate = Color(0.65, 0.75, 0.9, 0.22)
dust.add_child(dust_sprite)
_make_stars("DistantStars", 0.18, 210, 0)
_make_stars("NearStars", 0.42, 90, 1)
func _make_layer(layer_name: String, speed: float) -> Parallax2D:
var layer := Parallax2D.new()
layer.name = layer_name
layer.scroll_scale = Vector2.ONE * speed
add_child(layer)
return layer
func _resize_background() -> void:
var view_size := get_viewport_rect().size
var camera := get_viewport().get_camera_2d()
if is_instance_valid(camera):
view_size /= camera.zoom
# Native resolution at 720p; retain aspect ratio and cover camera travel.
_far_sprite.position = view_size * 0.5
var required := view_size + WORLD_SIZE * 0.04 + Vector2(32.0, 32.0)
var ratio := required / FAR_TEXTURE.get_size()
_far_sprite.scale = Vector2.ONE * maxf(1.0, maxf(ratio.x, ratio.y))
func _make_stars(layer_name: String, speed: float, count: int, seed_offset: int) -> void:
var rng := RandomNumberGenerator.new()
rng.seed = GameState.system_seed + seed_offset
var svg := '<svg xmlns="http://www.w3.org/2000/svg" width="2048" height="1536">'
for index in count:
var point := Vector2(rng.randf_range(4.0, STAR_TILE.x - 4.0), rng.randf_range(4.0, STAR_TILE.y - 4.0))
svg += '<circle cx="%.3f" cy="%.3f" r="%.3f" fill="#b8d4ff" opacity="%.3f"/>' % [point.x, point.y, rng.randf_range(0.5, 1.4), rng.randf_range(0.18, 0.65)]
svg += '</svg>'
var image := Image.new()
if image.load_svg_from_string(svg) != OK:
return
image.generate_mipmaps()
var layer := _make_layer(layer_name, speed)
layer.repeat_size = STAR_TILE
layer.repeat_times = 3
var sprite := Sprite2D.new()
sprite.texture = ImageTexture.create_from_image(image)
sprite.centered = false
layer.add_child(sprite)
func _draw() -> void:
# The map boundary remains in world space.
draw_rect(Rect2(-WORLD_SIZE * 0.5, WORLD_SIZE), Color(0.12, 0.52, 0.68, 0.48), false, 3.0)