Wrong ShapeCast2D Collision Normal

Godot Version

Godot 3.6

Question

Hi everyone!

I’m working on a ball type of game. I’m trying to implement an aiming system that shows the first bounce using ShapeCast2D and the bounce() function. However, I noticed that in some cases (apparently related to the angle of reflection), ShapeCast2D.normal() returns incorrect values.

To illustrate the issue, I recorded a video where the red line represents the normal returned by ShapeCast2D:

You can see that in certain situations, the normal doesn’t match the expected reflection angle. Below is the relevant code:

			$BouncedShapeCast2D.clear_exceptions()
			get_tree().call_group("DirectionLines", "queue_free")
			var mouse_pos = get_global_mouse_position()
			if mouse_pos.y > 920:
				mouse_pos.y = 920
			mouse_pos.x = clamp(mouse_pos.x,0, 580)
			dl = dir_line_prefab.instance()
			$ShapeCast2D.target_position = $ShapeCast2D.global_position.direction_to(mouse_pos) * 1024
			$ShapeCast2D.force_shapecast_update()
			add_child(dl)
			dl.points[0] = $Player.position - ball_spawnpoint_margin
#			dl.points[1] = $Player.position - ball_spawnpoint_margin
#			dl.points[1] = $ShapeCast2D.get_collision_point(0)
			dl.points[1] = ($ShapeCast2D.global_position - ($ShapeCast2D.target_position*-1 * $ShapeCast2D.get_closest_collision_unsafe_fraction()))
			if $ShapeCast2D.get_collider(0) and ($ShapeCast2D.get_collider(0).is_in_group("Borders") or $ShapeCast2D.get_collider(0).is_in_group("Bricks")):
				
				$BouncedShapeCast2D.add_exception($ShapeCast2D.get_collider(0))
				$BouncedShapeCast2D.global_position = dl.points[1]
#				var bcastdir = dl.points[0].direction_to(dl.points[1]).bounce($ShapeCast2D.get_collision_normal(0))

				var bcastdir = $ShapeCast2D.get_collision_normal(0)
				
				$BouncedShapeCast2D.target_position = bcastdir*1024
				$BouncedShapeCast2D.force_shapecast_update()

				bdl = dir_line_prefab.instance()
				
				bdl.default_color = Color(1,0,0,1)
				
				add_child(bdl)
				bdl.global_position = Vector2.ZERO
				bdl.points[0] = $BouncedShapeCast2D.position
#				bdl.points[1] = $BouncedShapeCast2D.position
				bdl.points[1] = ($BouncedShapeCast2D.position - ($BouncedShapeCast2D.target_position*-1 * $BouncedShapeCast2D.get_closest_collision_safe_fraction()))

Has anyone encountered a similar issue? Could this be a bug, or am I missing something in my implementation? Any help would be greatly appreciated!

Thanks in advance!