Files
Kalulu/sources/look_and_learn/tracing_path.gd
T
2025-09-05 15:55:48 +02:00

176 lines
4.7 KiB
GDScript

class_name TracingPath
extends Path2D
signal finished()
signal demo_finished()
@export var points_per_curve: int = 25
@export var hand_min_travel_time: float = 0.5
@export var hand_max_travel_time: float = 2.0
@export var distance: float = 100.0
@export var color_gradient: Gradient
var curve_points: PackedVector2Array
var is_playing: bool = false
var is_in_demo: bool = false
var touch_positions: Array[Vector2] = []
var should_play_effects: bool = false
@onready var line: Line2D = $Line2D
@onready var guide: PathFollow2D = $GuidePathFollow
@onready var hand: PathFollow2D = $HandPathFollow2D
@onready var guide_sprite: Sprite2D = $GuidePathFollow/Guide
@onready var hand_sprite: Sprite2D = $HandPathFollow2D/Hand
@onready var remaining_curve: Curve2D = Curve2D.new()
func _ready() -> void:
guide_sprite.visible = false
hand_sprite.visible = false
func _exit_tree() -> void:
pass
#if is_instance_valid(remaining_curve):
#remaining_curve.free()
func _process(delta: float) -> void:
should_play_effects = false
if is_playing or is_in_demo:
var points: PackedVector2Array = []
for index: int in range(0, int(guide.progress), 10):
var i_f: float = float(index)
var point: Vector2 = curve.sample_baked(i_f)
points.append(point)
line.points = points
if is_playing:
hand_sprite.visible = false
if Input.is_action_pressed("left_click"):
touch_positions.append(get_global_mouse_position())
if guide.progress_ratio >= 0.99:
stop()
elif not touch_positions.is_empty():
_tracing_process()
_reset_hand_offset()
elif not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
hand_sprite.visible = true
_hand_process(delta)
func _tracing_process() -> void:
while touch_positions.size() > 0:
var touch_position: Vector2 = touch_positions.pop_front()
var touch_vector: Vector2 = touch_position - guide.global_position
var current_offset: float = 0.0
if touch_vector.length() < distance:
var target_offset: float = remaining_curve.get_closest_offset(touch_position - global_position)
if target_offset > current_offset and target_offset - current_offset <= distance:
guide.progress = curve.get_closest_offset(remaining_curve.sample_baked(target_offset))
line.default_color = color_gradient.sample(guide.progress_ratio)
should_play_effects = true
var remove_up_to: int = 0
for index: int in range(curve_points.size()):
var offset: float = curve.get_closest_offset(curve_points[index])
if offset > guide.progress:
remove_up_to = index
break
for _index: int in range(mini(remove_up_to, curve_points.size() - 2)):
curve_points.remove_at(0)
remaining_curve.remove_point(0)
func _hand_process(delta: float) -> void:
var current_progression: float = guide.progress_ratio
var current_hand_travel_time: float = current_progression * hand_min_travel_time + (1.0 - current_progression) * hand_max_travel_time
var hand_speed: float = (1.0 - current_progression) / current_hand_travel_time
hand.progress_ratio += delta * hand_speed
if hand.progress_ratio >= 1.0:
_reset_hand_offset()
func _reset_hand_offset() -> void:
hand.progress_ratio = guide.progress_ratio
func setup(points: Array) -> void:
curve = Curve2D.new()
curve.bake_interval = 1
curve.resource_local_to_scene = true
line.width = 50.0
var smooth_points: Array[Vector2] = _smooth_points(points)
for point: Vector2 in smooth_points:
curve.add_point(point)
guide.progress = 0.0
hand.progress = 0.0
curve_points = curve.get_baked_points()
remaining_curve.clear_points()
for point: Vector2 in curve_points:
remaining_curve.add_point(point)
func _smooth_points(points: Array) -> Array:
return Bezier.bezier_sampling(points, maxi(points_per_curve, points.size()))
func set_points(points: Array) -> void:
curve.clear_points()
for point: Vector2 in points:
curve.add_point(point)
func set_guide_progress(progress: float) -> void:
guide.progress = progress
func get_guide_progress() -> float:
return guide.progress
func get_guide_progress_ratio() -> float:
return guide.progress_ratio
func set_hand_progress(progress: float) -> void:
hand.progress = progress
func start() -> void:
is_playing = true
guide_sprite.visible = true
guide.progress = 0.0
hand.progress = 0.0
func stop() -> void:
is_playing = false
guide_sprite.visible = false
finished.emit()
func demo() -> void:
is_in_demo = true
hand_sprite.visible = true
hand.progress_ratio = 0.0
var tween: Tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)
tween.tween_property(hand, "progress_ratio", 1.0, hand_max_travel_time)
tween.tween_callback(demo_end)
func demo_end() -> void:
hand_sprite.visible = false
is_in_demo = false
demo_finished.emit()