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

95 lines
3.1 KiB
GDScript

extends Node3D
## Streckennetz aus Abschnitten. Ein Abschnitt hat einen oder zwei
## Nachfolger - bei zweien steht dort eine Weiche.
const Mat = preload("res://scripts/mat.gd")
const Deco = preload("res://scripts/deco.gd")
const BALLAST := Color("d98d4a")
const SLEEPER := Color("5e4130")
const RAIL := Color("9a9da2")
const STEP := 1.2
## Jeder Eintrag: { "name", "curve": Curve3D, "length", "next": [ids], "prev": [ids] }
var segments: Array = []
## Abschnitt anlegen. Anfangs- und Endrichtung werden vorgegeben, damit
## sich mehrere Abschnitte an einer Weiche ohne Knick treffen.
func add_segment(seg_name: String, points: Array, t_start: Vector2, t_end: Vector2) -> int:
var c := Curve3D.new()
var n := points.size()
for i in n:
var p: Vector2 = points[i]
var t: Vector2
if i == 0:
t = t_start.normalized() * p.distance_to(points[1]) / 3.0
elif i == n - 1:
t = t_end.normalized() * p.distance_to(points[n - 2]) / 3.0
else:
t = (points[i + 1] - points[i - 1]) / 6.0
c.add_point(Vector3(p.x, 0, p.y), Vector3(-t.x, 0, -t.y), Vector3(t.x, 0, t.y))
segments.append({"name": seg_name, "curve": c, "length": c.get_baked_length(),
"next": [], "prev": []})
return segments.size() - 1
func connect_segments(from: int, to: int) -> void:
segments[from]["next"].append(to)
segments[to]["prev"].append(from)
func finish() -> void:
for i in segments.size():
_build_visuals(i)
func seg_length(i: int) -> float:
return float(segments[i]["length"])
func seg_name(i: int) -> String:
return String(segments[i]["name"])
func next_of(i: int) -> Array:
return segments[i]["next"]
func prev_of(i: int) -> Array:
return segments[i]["prev"]
func is_switch(i: int) -> bool:
return segments[i]["next"].size() > 1
func sample(i: int, offset: float) -> Transform3D:
var c: Curve3D = segments[i]["curve"]
return c.sample_baked_with_rotation(clampf(offset, 0.0, seg_length(i)), true, false)
## Nächster Punkt auf irgendeinem Abschnitt - für die Deko-Verteilung.
func distance_to_track(p: Vector2) -> float:
var best := INF
for s in segments:
var c: Curve3D = s["curve"]
var q: Vector3 = c.get_closest_point(Vector3(p.x, 0, p.y))
best = minf(best, Vector2(q.x, q.z).distance_to(p))
return best
func _build_visuals(i: int) -> void:
var count := int(seg_length(i) / STEP)
var xf: Array[Transform3D] = []
for k in count:
xf.append(sample(i, k * STEP + STEP * 0.5))
_multi(Deco.box(Vector3(2.7, 0.16, STEP * 1.12)), BALLAST, xf, [Vector3(0, 0.08, 0)])
_multi(Deco.box(Vector3(2.1, 0.09, 0.34)), SLEEPER, xf, [Vector3(0, 0.20, 0)])
_multi(Deco.box(Vector3(0.09, 0.11, STEP * 1.08)), RAIL, xf,
[Vector3(-0.62, 0.29, 0), Vector3(0.62, 0.29, 0)])
func _multi(mesh: Mesh, color: Color, xf: Array[Transform3D], offsets: Array) -> void:
var mm := MultiMesh.new()
mm.transform_format = MultiMesh.TRANSFORM_3D
mm.mesh = mesh
mm.instance_count = xf.size() * offsets.size()
var i := 0
for t in xf:
for o in offsets:
mm.set_instance_transform(i, t.translated_local(o))
i += 1
var mmi := MultiMeshInstance3D.new()
mmi.multimesh = mm
mmi.material_override = Mat.flat(color)
add_child(mmi)