How to achieve gradient animation in Line2D to resemble progress bar animation with clear-cut edges?

Godot Version

4.3
I’m working on a skill tree for my game, where each skill is represented by a square panel containing an icon. The skill panels are connected by lines to indicate progression between them. What I want to achieve is an animation that plays when the player unlocks a new skill, where the line connecting the newly learned skill and the previous one animates from gray to orange, similar to a progress bar with a sharp distinction between the two colors.

Here’s the code I’ve written to test this idea:

extends Line2D

func animate():
    var tween = get_tree().create_tween()
    tween.tween_method(tween_gradient_offset.bind(0), 0.0, 1.0, 3.0)
    tween.parallel().tween_method(tween_gradient_offset.bind(1), 0.01, 1.01, 3.0)

func tween_gradient_offset(value: float, offset_index: int):
    gradient.set_offset(offset_index, value)

func _on_button_pressed() -> void:
    animate()

I was hoping that by adjusting the gradient’s offsets, I could animate the color transition smoothly, making it look like a progress bar with a clear, moving boundary between the two colors. However, the result I’m seeing isn’t what I expected. You can check out this video to see what’s happening.

I’ve tried setting the gradient mode to both Linear and Cubic, but neither seems to help. I’m guessing this might be a limitation of how gradients work in Godot’s Line2D.

Since I’m working with Line2D, which is important because the connecting line could have multiple segments (e.g., an L-shape), I tried creating a shader using the step() function to get the desired sharp edge, but that didn’t work as expected either.

Does anyone have suggestions on how I can achieve this effect? Any alternative approaches for animating a gradient-like transition along a Line2D?

Solved it! The key was setting the Line2D’s fill/texture_mode to “stretch.” Here’s the solution, including the shader and script.

Shader:

shader_type canvas_item;

uniform float progress = 0.0;

void fragment() {
	vec4 final_color = step(vec4(progress), vec4(UV.x));
	final_color = 1.0 - final_color;
	COLOR = final_color;
}

Line2D script:

extends Line2D

func animate():
	var tween = create_tween()
	tween.tween_method(tween_shader_parameter.bind("progress"), 0.0, 1.0, 2.0).set_trans(Tween.TRANS_CUBIC)
	
func tween_shader_parameter(value: Variant, param: String):
	material.set_shader_parameter(param, value)


func _on_button_pressed() -> void:
	animate()