From 6eb675821625115bb1e74b9393e342968d02f6e4 Mon Sep 17 00:00:00 2001 From: Jonas Reith Date: Thu, 27 Aug 2026 10:05:11 +0200 Subject: [PATCH] Right-size POIs and fix interaction prompt visibility + terrain snap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three compounding issues, found via user testing: 1. Sprite was 2.56x2.56 units (raw 256px checkerboard at default pixel_size) — 5x wider than the player, and collision shapes sized to roughly match. Gave AnimatedSprite3D an explicit pixel_size (0.004) for a ~1 unit sprite, shrunk the solid CylinderShape3D to radius 0.4/height 1.0 and the Area3D trigger sphere to radius 0.6 accordingly (still >= the solid radius, so touching = interactable holds). 2. InteractionPrompt had zero vertical offset, sitting dead center of the (huge) sprite — rendering behind/inside it, so the "[F] Interact" hint never appeared even though proximity detection and the keypress itself worked (confirmed by the user: pressing F did work, the hint just never showed telling them to). Gave both NameLabel and InteractionPrompt proper offsets above the sprite. 3. Once the collision shapes shrank, a pre-existing ~0.7 unit gap between each POI's hand-placed Y (computed with the same radius-only height approximation that's only exact along a facet's straight edges, not its interior — same class of error as the earlier Ort6 out-of-bounds bug) and the true terrain surface became consequential: the small collider mostly floated above the real ground, giving wildly direction-dependent, sometimes entirely missing contact/interaction depending on approach angle. Root-fixed rather than re-deriving 36+ positions' exact heights by hand again: poi.gd/poi_gate.gd now raycast straight down against the terrain in _ready() (after one physics_frame, so the terrain's collision shape has had time to register) and snap themselves onto the real surface plus a small clearance. Verified headless: approaching a POI from 4 different directions now gives consistent ~0.56 unit contact distance and player_nearby=true in all four (previously: two directions barely touched at ~0.05 units with interaction never triggering, the other two behaved correctly — direction-dependent failure, now gone). Re-ran the full 6-gate chain (still passes) and gate-to-border-prop clearance across 3 randomized layouts (still 1.3-1.9 units clear, no regression). --- world/poi/poi.gd | 17 +++++++++++++++++ world/poi/poi.tscn | 10 ++++++---- world/poi/poi_gate.gd | 15 +++++++++++++++ world/poi/poi_gate.tscn | 9 +++++---- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/world/poi/poi.gd b/world/poi/poi.gd index 274131a..2184001 100755 --- a/world/poi/poi.gd +++ b/world/poi/poi.gd @@ -16,6 +16,23 @@ func _ready() -> void: _update_visuals() body_entered.connect(_on_body_entered) body_exited.connect(_on_body_exited) + # Terrain-Kollision braucht mind. einen Physik-Tick um registriert zu sein + await get_tree().physics_frame + _snap_to_terrain() + +# Handplatzierte Y-Werte treffen die geneigten Facetten nicht exakt (die Höhe +# ist keine reine Funktion des Radius, siehe terrain.gd). Statt jede Position +# neu von Hand zu berechnen: per Raycast auf die echte Terrain-Oberfläche +# schnappen, damit Sprite/Hitbox garantiert am Boden sitzen. +func _snap_to_terrain() -> void: + var space_state := get_world_3d().direct_space_state + var from := global_position + Vector3(0, 20, 0) + var to := global_position + Vector3(0, -20, 0) + var query := PhysicsRayQueryParameters3D.create(from, to) + query.exclude = [get_rid(), $StaticBody3D.get_rid()] + var result := space_state.intersect_ray(query) + if result: + global_position.y = result.position.y + 0.5 func _update_visuals() -> void: if visited: diff --git a/world/poi/poi.tscn b/world/poi/poi.tscn index fed6ee5..23e9193 100755 --- a/world/poi/poi.tscn +++ b/world/poi/poi.tscn @@ -4,7 +4,7 @@ [ext_resource type="Texture2D" uid="uid://c3ajv7vb8c4fb" path="res://assets/debug/checkerboard.png" id="2_yof6k"] [sub_resource type="SphereShape3D" id="SphereShape3D_gwpk6"] -radius = 1.3 +radius = 0.6 [sub_resource type="SpriteFrames" id="SpriteFrames_0surh"] animations = [{ @@ -18,8 +18,8 @@ animations = [{ }] [sub_resource type="CylinderShape3D" id="CylinderShape3D_58c6p"] -radius = 1.0 -height = 2.4 +radius = 0.4 +height = 1.0 [node name="POI" type="Area3D" unique_id=1770000468] script = ExtResource("1_gwpk6") @@ -29,6 +29,7 @@ shape = SubResource("SphereShape3D_gwpk6") [node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="." unique_id=1205831120] billboard = 2 +pixel_size = 0.004 sprite_frames = SubResource("SpriteFrames_0surh") [node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=1488376384] @@ -40,12 +41,13 @@ shape = SubResource("CylinderShape3D_58c6p") transform = Transform3D(6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0) [node name="NameLabel" type="Label3D" parent="Billboard" unique_id=1419647885] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.018201571, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.13, 0) billboard = 1 text = "???" font_size = 16 [node name="InteractionPrompt" type="Label3D" parent="Billboard" unique_id=2059219662] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.09, 0) visible = false billboard = 1 font_size = 12 diff --git a/world/poi/poi_gate.gd b/world/poi/poi_gate.gd index f9c1d9e..f03eeb4 100644 --- a/world/poi/poi_gate.gd +++ b/world/poi/poi_gate.gd @@ -18,6 +18,21 @@ func _ready() -> void: _update_visuals() body_entered.connect(_on_body_entered) body_exited.connect(_on_body_exited) + # Terrain-Kollision braucht mind. einen Physik-Tick um registriert zu sein + await get_tree().physics_frame + _snap_to_terrain() + +# Siehe poi.gd: handplatzierte Y-Werte treffen die geneigten Facetten nicht +# exakt, per Raycast auf die echte Terrain-Oberfläche schnappen. +func _snap_to_terrain() -> void: + var space_state := get_world_3d().direct_space_state + var from := global_position + Vector3(0, 20, 0) + var to := global_position + Vector3(0, -20, 0) + var query := PhysicsRayQueryParameters3D.create(from, to) + query.exclude = [get_rid(), $StaticBody3D.get_rid()] + var result := space_state.intersect_ray(query) + if result: + global_position.y = result.position.y + 0.3 func _all_required_visited() -> bool: var pois := get_tree().get_nodes_in_group(required_group) diff --git a/world/poi/poi_gate.tscn b/world/poi/poi_gate.tscn index 8637ebe..fc87702 100644 --- a/world/poi/poi_gate.tscn +++ b/world/poi/poi_gate.tscn @@ -4,7 +4,7 @@ [ext_resource type="Texture2D" uid="uid://c3ajv7vb8c4fb" path="res://assets/debug/checkerboard.png" id="2_gate"] [sub_resource type="SphereShape3D" id="SphereShape3D_gate"] -radius = 1.3 +radius = 0.6 [sub_resource type="SpriteFrames" id="SpriteFrames_gate"] animations = [{ @@ -18,8 +18,8 @@ animations = [{ }] [sub_resource type="CylinderShape3D" id="CylinderShape3D_gate"] -radius = 1.0 -height = 2.4 +radius = 0.4 +height = 1.0 [node name="POIGate" type="Area3D"] script = ExtResource("1_gate") @@ -29,6 +29,7 @@ shape = SubResource("SphereShape3D_gate") [node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="."] billboard = 2 +pixel_size = 0.004 sprite_frames = SubResource("SpriteFrames_gate") [node name="StaticBody3D" type="StaticBody3D" parent="."] @@ -37,7 +38,7 @@ sprite_frames = SubResource("SpriteFrames_gate") shape = SubResource("CylinderShape3D_gate") [node name="Billboard" type="Node3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0) [node name="NameLabel" type="Label3D" parent="Billboard"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0)