Files
train-game/scripts/world.gd
2026-09-16 17:55:38 +02:00

113 lines
3.7 KiB
GDScript

extends Node3D
## Gelaende, Vegetation und Umgebung. Baeume laufen ueber MultiMesh,
## damit auch mehrere hundert Stueck guenstig bleiben.
const Mat = preload("res://scripts/mat.gd")
const Deco = preload("res://scripts/deco.gd")
const GRASS := Color("7cb85a")
const GRASS_DARK := Color("74b054")
const WATER := Color("4f9fd6")
const SAND := Color("e0c98a")
const EXTENT := 360.0
var _rng: RandomNumberGenerator
func build(rng: RandomNumberGenerator, track, stations: Array) -> void:
_rng = rng
_ground()
_scatter(track, stations)
func _flat_disc(pos: Vector3, radius: float, color: Color, height := 0.06) -> void:
var m := MeshInstance3D.new()
m.mesh = Deco.cyl(radius, height, 24)
m.material_override = Mat.flat(color)
m.position = pos
add_child(m)
func _ground() -> void:
var plane := PlaneMesh.new()
plane.size = Vector2(1000, 1000)
var g := MeshInstance3D.new()
g.mesh = plane
g.material_override = Mat.flat(GRASS)
add_child(g)
_flat_disc(Vector3(-230, 0.02, 150), 72.0, SAND, 0.05)
_flat_disc(Vector3(-230, 0.04, 150), 60.0, WATER, 0.05)
for i in 18:
var p := Vector2(_rng.randf_range(-EXTENT, EXTENT), _rng.randf_range(-EXTENT, EXTENT))
_flat_disc(Vector3(p.x, 0.01, p.y), _rng.randf_range(18.0, 44.0), GRASS_DARK, 0.03)
func _multi(mesh: Mesh, items: Array) -> void:
if items.is_empty():
return
var mm := MultiMesh.new()
mm.transform_format = MultiMesh.TRANSFORM_3D
mm.use_colors = true
mm.mesh = mesh
mm.instance_count = items.size()
for i in items.size():
var it: Dictionary = items[i]
mm.set_instance_transform(i, it["xf"])
# Instanzfarben gehen roh in den Shader, deshalb hier nach Linear wandeln.
mm.set_instance_color(i, (it["color"] as Color).srgb_to_linear())
var mmi := MultiMeshInstance3D.new()
mmi.multimesh = mm
mmi.material_override = Mat.flat(Color.WHITE)
add_child(mmi)
func _scatter(track, stations: Array) -> void:
var trunks: Array = []
var cones: Array = []
var blobs: Array = []
var rocks: Array = []
var placed := 0
var tries := 0
while placed < 620 and tries < 12000:
tries += 1
var p := Vector2(_rng.randf_range(-EXTENT, EXTENT), _rng.randf_range(-EXTENT, EXTENT))
if track.distance_to_track(p) < 11.0:
continue
if p.distance_to(Vector2(-230, 150)) < 78.0:
continue
var blocked := false
for st in stations:
if p.distance_to(Vector2(st.global_position.x, st.global_position.z)) < 30.0:
blocked = true
break
if blocked:
continue
placed += 1
var yaw := _rng.randf() * TAU
if _rng.randf() < 0.6:
var h := _rng.randf_range(4.0, 8.5)
var col: Color = Deco.PINE[_rng.randi() % Deco.PINE.size()]
trunks.append(_item(p, h * 0.16, Vector3(1, h * 0.3, 1), yaw, Deco.TRUNK))
var tiers := 3 if h > 6.0 else 2
for i in tiers:
var f := float(i) / float(tiers)
var r := lerpf(h * 0.3, h * 0.14, f)
var th := h * 0.44
var y := h * 0.26 + f * h * 0.32 + th * 0.5
cones.append(_item(p, y, Vector3(r, th, r), yaw, col))
elif _rng.randf() < 0.85:
var h := _rng.randf_range(3.2, 6.0)
var col: Color = Deco.LEAF[_rng.randi() % Deco.LEAF.size()]
trunks.append(_item(p, h * 0.28, Vector3(0.9, h * 0.55, 0.9), yaw, Deco.TRUNK))
var r := h * 0.44
blobs.append(_item(p, h * 0.55 + r * 0.5, Vector3(r, r, r), yaw, col))
else:
var r := _rng.randf_range(0.7, 2.0)
rocks.append(_item(p, r * 0.35, Vector3(r, r * 0.55, r * 1.1), yaw, Deco.ROCK))
_multi(Deco.cyl(0.18, 1.0, 6), trunks)
_multi(Deco.cone(1.0, 1.0, 7), cones)
_multi(Deco.blob(1.0), blobs)
_multi(Deco.blob(1.0), rocks)
func _item(p: Vector2, y: float, scale: Vector3, yaw: float, color: Color) -> Dictionary:
var b := Basis(Vector3.UP, yaw).scaled(scale)
return {"xf": Transform3D(b, Vector3(p.x, y, p.y)), "color": color}