refactor(gootower): draw strands as pinched goo shapes instead of lines

Replace simple line-based strand rendering with filled polygon shapes that
are thick at the ends and pinched thin in the middle, with curved edges
and perpendicular sag for a drooping arc effect. Strands now connect
smoothly to balls with tangent-aligned edges.
This commit is contained in:
Brian Fertig 2026-07-26 19:26:25 -06:00
parent b6e7d932cd
commit 59a5d0606e
1 changed files with 56 additions and 4 deletions

View File

@ -654,7 +654,8 @@ export default class GooTowerGame extends Phaser.Scene {
this.drawGears(g); this.drawGears(g);
// Strands first, under the balls. // Strands first, under the balls — drawn as pinched goo shapes (thick
// at the ends, thin in the middle with curved edges).
for (const s of st.strands) { for (const s of st.strands) {
if (s.broken) continue; if (s.broken) continue;
const a = st.balls[s.a]; const a = st.balls[s.a];
@ -662,9 +663,60 @@ export default class GooTowerGame extends Phaser.Scene {
if (!a || !b || a.dead || b.dead) continue; if (!a || !b || a.dead || b.dead) continue;
const stress = strandStress(s); const stress = strandStress(s);
const color = lerpColor(STRAND, STRAND_HOT, stress); const color = lerpColor(STRAND, STRAND_HOT, stress);
const width = 7 - 3 * stress; // strands thin as they are pulled taut
g.lineStyle(width, color, 1); const dx = b.x - a.x;
g.lineBetween(a.x, a.y, b.x, b.y); const dy = b.y - a.y;
const len = Math.hypot(dx, dy);
if (len < 1) continue;
// Unit direction along the strand and perpendicular to it.
const ux = dx / len, uy = dy / len;
const nx = -uy, ny = ux;
const baseW = 7 - 3 * stress;
const endW = baseW;
const midW = Math.max(1, baseW * 0.2);
// Perpendicular sag of the centerline at the midpoint — gives the strand
// a gentle drooping arc.
const sagAmt = baseW * 0.5;
// Width profile: endW at both endpoints, pinched to midW at the center.
// Zero derivative at t=0 and t=1 so the strand meets each ball tangentially.
// w(t) = midW + (endW - midW) * (1 - 2·t·(1-t))
// Sag profile: zero at endpoints, sagAmt at center.
// s(t) = 4·sagAmt·t·(1-t)
// Both are quadratics with zero derivative at the endpoints, giving
// perfectly smooth (tangent-aligned) connections into the balls.
// The second derivative of the edge offset is negative everywhere,
// so the edges curve inward — a concave "pinched" look.
const samples = Math.max(8, Math.min(20, Math.round(len / 15)));
const verts = [];
for (let i = 0; i <= samples; i++) {
const t = i / samples;
const tp = t * (1 - t); // peaks at 0.25 when t = 0.5
const w = midW + (endW - midW) * (1 - 2 * tp);
const sg = 4 * sagAmt * tp;
// Center point (straight line + perpendicular sag)
const cx = a.x + t * dx + nx * sg;
const cy = a.y + t * dy + ny * sg;
// Upper edge
verts.push({ x: cx + nx * (w / 2), y: cy + ny * (w / 2) });
}
// Bottom edge, walking back from end → start.
for (let i = samples; i >= 0; i--) {
const t = i / samples;
const tp = t * (1 - t);
const w = midW + (endW - midW) * (1 - 2 * tp);
const sg = 4 * sagAmt * tp;
const cx = a.x + t * dx + nx * sg;
const cy = a.y + t * dy + ny * sg;
verts.push({ x: cx - nx * (w / 2), y: cy - ny * (w / 2) });
}
g.fillStyle(color, 1);
g.fillPoints(verts, true);
} }
// Balls. // Balls.