extends Area2D ## Una singola pallina nella sua corsia. Partendo da destra, se lanciata ## scivola verso il bordo sinistro e poi torna alla posizione di partenza. ## Il rilevamento del tocco/click è gestito centralmente da main.gd tramite ## distanza: qui NON ci sono handler di input dedicati. signal returned # emesso quando la pallina torna a casa var color_name := "" var ball_color := Color.WHITE var radius := 40.0 var home_pos := Vector2.ZERO var left_pos := Vector2.ZERO var in_motion := false var roll_angle := 0.0: set(v): roll_angle = v queue_redraw() # aggiorna il disegno a ogni step di rotazione var _tween: Tween func _draw() -> void: # ombra a terra draw_circle(Vector2(0, 6), radius, Color(0, 0, 0, 0.25)) # corpo base draw_circle(Vector2.ZERO, radius, ball_color) # ombreggiatura inferiore-destra (profondità 3D) draw_circle(Vector2(radius * 0.18, radius * 0.22), radius * 0.92, Color(0, 0, 0, 0.16)) # luce superiore-sinistra draw_circle(Vector2(-radius * 0.18, -radius * 0.22), radius * 0.92, Color(1, 1, 1, 0.2)) # marmorizzazione a vortici che ruota col rotolamento (design realistico) _draw_marbling() # bordo nero spesso (stile china) draw_arc(Vector2.ZERO, radius, 0.0, TAU, 48, Color(0.1, 0.1, 0.1, 0.9), 4.0) # riflesso speculare (fisso: la luce non ruota con la superficie) draw_circle(Vector2(-radius * 0.3, -radius * 0.34), radius * 0.16, Color(1, 1, 1, 0.85)) func _draw_marbling() -> void: # Tre venature curve, irregolari e non simmetriche. Tutta la geometria # superficiale ruota; illuminazione e riflesso vengono invece disegnati dopo. var vein_color: Color = ball_color.darkened(0.42) vein_color.a = 0.20 var soft_color: Color = ball_color.darkened(0.28) soft_color.a = 0.12 var veins: Array[PackedVector2Array] = [ _make_vein(-0.72, -0.22, 0.68, -0.06, 0.15, 0.09), _make_vein(-0.55, 0.34, 0.34, 0.60, -0.13, 0.07), _make_vein(-0.18, -0.64, 0.58, -0.40, -0.10, 0.06), ] for i: int in range(veins.size()): var rotated_points := PackedVector2Array() for point: Vector2 in veins[i]: rotated_points.append(point.rotated(roll_angle)) var width: float = radius * (0.075 if i == 0 else 0.045) draw_polyline(rotated_points, soft_color, width * 1.9, true) draw_polyline(rotated_points, vein_color, width, true) func _make_vein(x0: float, y0: float, x1: float, y1: float, bend: float, ripple: float) -> PackedVector2Array: var points := PackedVector2Array() var direction := Vector2(x1 - x0, y1 - y0) var normal := direction.normalized().orthogonal() for step: int in range(17): var t: float = float(step) / 16.0 var base := Vector2(lerpf(x0, x1, t), lerpf(y0, y1, t)) var curve: float = sin(t * PI) * bend var wave: float = sin(t * TAU * 1.35 + x0 * 4.0) * ripple points.append((base + normal * (curve + wave)) * radius) return points func setup(cn: String, c: Color, home: Vector2, left: Vector2, r: float) -> void: color_name = cn ball_color = c radius = r home_pos = home left_pos = left position = home var shape := CollisionShape2D.new() var cs := CircleShape2D.new() cs.radius = r * 1.35 # area di tocco ampliata (ma l'input è via distanza in main) shape.shape = cs add_child(shape) queue_redraw() func is_home() -> bool: return not in_motion func reset() -> void: if _tween and _tween.is_valid(): _tween.kill() in_motion = false position = home_pos roll_angle = 0.0 ## Lancia la pallina verso sinistra e la fa tornare a casa. `mt` è il tempo ## totale (andata + ritorno) in secondi. func launch(mt: float) -> void: in_motion = true if _tween and _tween.is_valid(): _tween.kill() var half: float = maxf(mt * 0.5, 0.01) # rotazione proporzionale alla distanza percorsa (rotolamento) var dist: float = home_pos.x - left_pos.x var total_angle: float = dist / radius _tween = create_tween() _tween.tween_property(self, "position", left_pos, half) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT) _tween.parallel().tween_property(self, "roll_angle", total_angle, half) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT) _tween.tween_property(self, "position", home_pos, half) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT) _tween.parallel().tween_property(self, "roll_angle", 0.0, half) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT) _tween.finished.connect(_on_motion_done) func _on_motion_done() -> void: in_motion = false returned.emit()