2d arrow in 3d with glow

I created a script for Node2d in a 3D scene to draw a line with an arrow between two Sprite3ds, with the option to select a custom arrow as a texture.

extends Node2D


@export var line_thickness: float = 4.0
@export var arch_height: float = 80.0
@export var line_color: Color = Color.YELLOW


@export var sprite1: Node3D
@export var sprite2: Node3D
@export var camera: Camera3D


var arrow_length = 15.0
var arrow_angle_rad = deg_to_rad(35.0)


func _process(delta):
	queue_redraw()


func _draw():
	if not is_instance_valid(sprite1) or not is_instance_valid(sprite2) or not is_instance_valid(camera):
		return

	var pos1_3d = sprite1.global_position
	var pos2_3d = sprite2.global_position

	if camera.is_position_behind(pos1_3d) or camera.is_position_behind(pos2_3d):
		return

	var start_point = camera.unproject_position(pos1_3d)
	var end_point = camera.unproject_position(pos2_3d)
	

	var arch_direction = Vector2.UP
	

	var midpoint = start_point.lerp(end_point, 0.5)
	

	var control_point = midpoint + arch_direction * arch_height


	var points = PackedVector2Array()
	var num_segments = 30 
	for i in range(num_segments + 1):
		var t = i / float(num_segments)
		var point_on_curve = pow(1 - t, 2) * start_point + 2 * (1 - t) * t * control_point + pow(t, 2) * end_point
		points.append(point_on_curve)
	

	draw_polyline(points, line_color, line_thickness, true)

	var t_peak = 0.5
	var peak_point = pow(1 - t_peak, 2) * start_point + 2 * (1 - t_peak) * t_peak * control_point + pow(t_peak, 2) * end_point
	
	var tangent = (end_point - start_point).normalized()
	
	if tangent.is_zero_approx():
		return 

	var dir_to_end = tangent
	var dir_to_start = -tangent

	var arrow1_p1 = peak_point + dir_to_start.rotated(arrow_angle_rad) * arrow_length
	var arrow1_p2 = peak_point + dir_to_start.rotated(-arrow_angle_rad) * arrow_length
	draw_line(peak_point, arrow1_p1, line_color, line_thickness, true)
	draw_line(peak_point, arrow1_p2, line_color, line_thickness, true)
	
	var arrow2_p1 = peak_point + dir_to_end.rotated(arrow_angle_rad) * arrow_length
	var arrow2_p2 = peak_point + dir_to_end.rotated(-arrow_angle_rad) * arrow_length
	draw_line(peak_point, arrow2_p1, line_color, line_thickness, true)
	draw_line(peak_point, arrow2_p2, line_color, line_thickness, true)

As a final touch, I wanted to add a glowing effect to the arrowhead and the line itself, but I can’t figure out how to do it and don’t know what to do. I think shaders might be able to handle this, but I don’t understand them.

Shaders would work, and they’re useful to learn, but in this case you don’t need them. When you’re generating your arrow, make a second arrow that’s scaled up maybe 10%, color it with the glow color, but set the alpha to probably somewhere between 25% and 66% (you’ll want to tweak it to make it look right), and draw it at the same coordinates but slightly (like, 0.01) further away.

Edit: ah, polyline(), that’ll make it slightly more complex, and you might need shaders. Personally, I’d suggest doing the arrow as a triangle mesh.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.