One commited on
Commit
16ad637
Β·
verified Β·
1 Parent(s): 7fe103c

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +55 -5
index.html CHANGED
@@ -167,6 +167,30 @@ class Fish {
167
  desired = lerpAngle(desired, Math.atan2(H * 0.45 - this.y, W / 2 - this.x), fp * 1.6);
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  // Limit turn rate so direction changes are smooth and fish-like
171
  let d = desired - this.angle;
172
  while (d > Math.PI) d -= 2 * Math.PI;
@@ -220,14 +244,15 @@ class Fish {
220
  // tail fin follows the body wave at its base, amplified (same phase β†’ no fighting)
221
  const sway = Math.sin(-s * waveK + this.phase) * s * 0.34;
222
 
223
- // tail fin
 
224
  c.fillStyle = `hsla(${this.hue},75%,52%,0.92)`;
225
  c.beginPath();
226
  c.moveTo(-s * 0.5, 0);
227
- c.quadraticCurveTo(-s * 1.0, -s * 0.35 + sway, -s * 1.28, -s * 0.48 + sway);
228
- c.lineTo(-s * 0.82, sway * 0.5);
229
- c.lineTo(-s * 1.28, s * 0.48 + sway);
230
- c.quadraticCurveTo(-s * 1.0, s * 0.35 + sway, -s * 0.5, 0);
231
  c.fill();
232
 
233
  // body β€” undulates while swimming: the ellipse flexes along a traveling wave (v3)
@@ -865,6 +890,31 @@ function frame(now) {
865
  updateGrass(t, dt);
866
 
867
  for (const f of fishes) f.update(dt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  fishes.sort((a, b) => a.size - b.size); // small fish swim behind
869
  for (const f of fishes) f.draw(ctx);
870
 
 
167
  desired = lerpAngle(desired, Math.atan2(H * 0.45 - this.y, W / 2 - this.x), fp * 1.6);
168
  }
169
 
170
+ // v7: separation steering β€” bend around nearby fish instead of ploughing into them.
171
+ // Only kicks in while actually swimming, so resting fish don't twitch at each other;
172
+ // the faster we swim, the earlier and harder we steer away.
173
+ const mySpeed = this.speed * this.speedFactor;
174
+ if (mySpeed > 0.3) {
175
+ let ax = 0, ay = 0, wsum = 0;
176
+ for (const o of fishes) {
177
+ if (o === this) continue;
178
+ const dx = o.x - this.x, dy = o.y - this.y;
179
+ const d2 = dx * dx + dy * dy;
180
+ const zone = (this.size + o.size) * 1.7; // notice radius scales with both fish
181
+ if (d2 < zone * zone && d2 > 0.01) {
182
+ const dd = Math.sqrt(d2);
183
+ const w = (1 - dd / zone) * Math.min(1, mySpeed); // close & fast β†’ strong
184
+ ax -= (dx / dd) * w;
185
+ ay -= (dy / dd) * w;
186
+ wsum += w;
187
+ }
188
+ }
189
+ if (wsum > 0) {
190
+ desired = lerpAngle(desired, Math.atan2(ay, ax), Math.min(0.85, wsum * 0.45));
191
+ }
192
+ }
193
+
194
  // Limit turn rate so direction changes are smooth and fish-like
195
  let d = desired - this.angle;
196
  while (d > Math.PI) d -= 2 * Math.PI;
 
244
  // tail fin follows the body wave at its base, amplified (same phase β†’ no fighting)
245
  const sway = Math.sin(-s * waveK + this.phase) * s * 0.34;
246
 
247
+ // tail fin (v7): rounded lobes instead of two pointed spikes β€” each tip is swept
248
+ // around by a curve whose control point sits just past where the old spike was
249
  c.fillStyle = `hsla(${this.hue},75%,52%,0.92)`;
250
  c.beginPath();
251
  c.moveTo(-s * 0.5, 0);
252
+ c.quadraticCurveTo(-s * 1.04, -s * 0.36 + sway, -s * 1.22, -s * 0.44 + sway); // upper edge out toward the tip
253
+ c.quadraticCurveTo(-s * 1.34, -s * 0.52 + sway, -s * 0.86, sway * 0.5); // rounded upper lobe β†’ notch
254
+ c.quadraticCurveTo(-s * 1.34, s * 0.52 + sway, -s * 1.22, s * 0.44 + sway); // notch β†’ rounded lower lobe
255
+ c.quadraticCurveTo(-s * 1.04, s * 0.36 + sway, -s * 0.5, 0); // lower edge back to the base
256
  c.fill();
257
 
258
  // body β€” undulates while swimming: the ellipse flexes along a traveling wave (v3)
 
890
  updateGrass(t, dt);
891
 
892
  for (const f of fishes) f.update(dt);
893
+
894
+ // v7: fish gently bump each other β€” overlapping pairs are eased apart along their
895
+ // center line (weighted by size), so they nudge instead of stacking on top of each other
896
+ for (let i = 0; i < fishes.length; i++) {
897
+ const a = fishes[i];
898
+ for (let j = i + 1; j < fishes.length; j++) {
899
+ const b = fishes[j];
900
+ let dx = b.x - a.x, dy = b.y - a.y;
901
+ const minD = (a.size + b.size) * 0.62; // gentle: smaller than their visual length
902
+ const d2 = dx * dx + dy * dy;
903
+ if (d2 <= 0.01) { // exactly overlapping β€” nudge apart randomly
904
+ a.x -= rand(0.5, 1.5); a.y -= rand(0.5, 1.5);
905
+ b.x += rand(0.5, 1.5); b.y += rand(0.5, 1.5);
906
+ } else if (d2 < minD * minD) {
907
+ const dd = Math.sqrt(d2);
908
+ const overlap = (minD - dd) / minD; // 0..1 how deep they are in each other
909
+ const push = overlap * 24 * dt; // soft: only a few px/s even at full overlap
910
+ dx /= dd; dy /= dd;
911
+ const ma = b.size / (a.size + b.size), mb = a.size / (a.size + b.size);
912
+ a.x -= dx * push * 2 * ma; a.y -= dy * push * 2 * ma;
913
+ b.x += dx * push * 2 * mb; b.y += dy * push * 2 * mb;
914
+ }
915
+ }
916
+ }
917
+
918
  fishes.sort((a, b) => a.size - b.size); // small fish swim behind
919
  for (const f of fishes) f.draw(ctx);
920