extends SceneTree ## Editor-Tests (headless): exakter Klick-Treffer (Mauer-Klick-Fix 24.10.). ## Ausführen: ## godot --headless --path . -s res://tests/test_klick_exakt.gd ## Exit 0 = alles grün. ## ## K1 ray_mesh_distance trifft eine Box im richtigen Abstand, verfehlt sie ## daneben, respektiert Drehung + Scale der Transform. ## K2 Sly-Szenario: großes Gebäude (20×10×20) direkt neben schmaler Mauer ## (6×2×0,4). Der Strahl trifft die Mauer, liegt aber auch IN der ## Gebäude-Kugel und tritt dort früher ein → Kugel allein wählt das ## Gebäude (alter Fehler), der exakte Test wählt die Mauer. var failures := 0 var checks := 0 func _initialize() -> void: _run_all.call_deferred() func _check(cond: bool, label: String) -> void: checks += 1 print("%s: %s" % ["ok " if cond else "FAIL", label]) if not cond: failures += 1 func _box_faces(size: Vector3) -> PackedVector3Array: var m := BoxMesh.new() m.size = size return m.get_faces() func _run_all() -> void: # K1: Grundfälle var faces := _box_faces(Vector3(2, 2, 2)) var xf := Transform3D(Basis(), Vector3(0, 1, 0)) var d := SelectionMath.ray_mesh_distance(xf, faces, Vector3(0, 1, 10), Vector3(0, 0, -1)) _check(absf(d - 9.0) < 0.001, "K1 Box frontal: Abstand 9 m (ist %.3f)" % d) d = SelectionMath.ray_mesh_distance(xf, faces, Vector3(3, 1, 10), Vector3(0, 0, -1)) _check(d < 0.0, "K1 daneben: kein Treffer") var xf_rot := Transform3D(Basis(Vector3.UP, deg_to_rad(45.0)), Vector3(0, 1, 0)) d = SelectionMath.ray_mesh_distance(xf_rot, faces, Vector3(1.2, 1, 10), Vector3(0, 0, -1)) _check(d > 0.0, "K1 gedreht 45°: Ecke ragt bis x≈1,41 → Treffer bei x=1,2") var xf_scale := Transform3D(Basis().scaled(Vector3.ONE * 3.0), Vector3(0, 1, 0)) d = SelectionMath.ray_mesh_distance(xf_scale, faces, Vector3(0, 1, 10), Vector3(0, 0, -1)) _check(absf(d - 7.0) < 0.001, "K1 Scale 3: Abstand in Welt-Metern 7 (ist %.3f)" % d) # K2: Gebäude neben Mauer var building_size := Vector3(20, 10, 20) var wall_size := Vector3(6, 2, 0.4) var building_xf := Transform3D(Basis(), Vector3(0, 5, 0)) var wall_xf := Transform3D(Basis(), Vector3(0, 1, 12)) # Mauer 2 m vor der Gebäudefront (z = 10) var origin := Vector3(0, 1.5, 40) var dir := Vector3(0, 0, -1) # Kugel-Näherung wie map_view_builder: halbe Diagonale + max(0.6, half.y·0.25) var bh := building_size * 0.5 var wh := wall_size * 0.5 var b_rad := bh.length() + maxf(0.6, bh.y * 0.25) var w_rad := wh.length() + maxf(0.6, wh.y * 0.25) var b_center := Vector3(0, 5, 0) var w_center := Vector3(0, 1, 12) var b_sphere := _sphere_t(origin, dir, b_center, b_rad) var w_sphere := _sphere_t(origin, dir, w_center, w_rad) _check(b_sphere >= 0.0 and b_sphere < w_sphere, "K2 Ausgangslage: Gebäude-Kugel wird VOR der Mauer-Kugel getroffen (alter Fehler reproduziert)") var b_exact := SelectionMath.ray_mesh_distance(building_xf, _box_faces(building_size), origin, dir) var w_exact := SelectionMath.ray_mesh_distance(wall_xf, _box_faces(wall_size), origin, dir) _check(w_exact > 0.0 and (b_exact < 0.0 or w_exact < b_exact), "K2 exakt: Mauer (%.2f m) vor Gebäude (%.2f m) → Mauer wird gewählt" % [w_exact, b_exact]) print("test_klick_exakt: %d/%d ok" % [checks - failures, checks]) quit(1 if failures > 0 else 0) func _sphere_t(origin: Vector3, dir: Vector3, center: Vector3, radius: float) -> float: var to_c := center - origin var tca := to_c.dot(dir) var d2 := to_c.length_squared() - tca * tca if tca <= 0.0 or d2 > radius * radius: return -1.0 return tca - sqrt(radius * radius - d2)