You pick another value for nextFrameChange only after frameCount reached current nextFrameChange:
if (frameCount >= nextFrameChange) swapDirection();
It’s inside funtion swapDirection() that you change nextFrameChange:
void swapDirection() {
speed *= -1;
nextFrameChange = frameCount + 60 * (int) random(5, 11); // 5s to 10s range
}
Anyways, I did a demo sketch that has a class Fish w/ methods such as display(), update(), bounce(), touchedBoundary(), etc.:
Swimming_Fish.pde
/**
* Swimming Fish (v1.0.3)
* GoToLoop (2023/Jan/23)
*
* Discourse.Processing.org/t/help-with-simple-game-moving-fish/40679/9
*
* OpenProcessing.org/sketch/1805772
*/
static final boolean REMOTE = true;
static final String
FISH_SITE = "http://" + "www.Clker.com" + "/cliparts/" + "1/f/a/i/g/d/",
FISH_FILE = "rainbow-fish-th.png",
FISH_PATH = REMOTE? FISH_SITE + FISH_FILE : FISH_FILE;
static final color BG = #0080FF; // azure
static final int FISHES = 4, FPS = 60;
final Fish[] fishes = new Fish[FISHES];
PImage fishLeft, fishRight;
boolean paused;
void setup() {
size(1000, 500);
frameRate(FPS);
populateLake();
println("File: \"" + FISH_PATH + "\"\n");
for (final Fish fish : fishes) println(fish);
fishLeft = loadImage(FISH_PATH);
fishLeft.resize(Fish.W, Fish.H);
fishRight = createMirrorImage(fishLeft);
}
void draw() {
background(BG);
for (final Fish fish : fishes) fish.display().update();
}
void mousePressed() {
if (paused ^= true) noLoop();
else loop();
}
Fish.pde
class Fish {
static final int W = 100, H = 80, VEL = 3, MIN_SEC = 3, MAX_SEC = 6;
int x, y, v, nextFrameBounce;
Fish(final int depth) {
this((int) random(W, width - W * 2), height / FISHES * depth + H / 4 | 0);
}
Fish(final int posX, final int posY) {
x = posX;
y = posY;
v = random(1) < .5? -VEL : VEL;
nextFrameBounce = getNextFrameBounce();
}
Fish display() {
image(v < 0? fishLeft : fishRight, x, y);
return this;
}
Fish update() {
if (frameCount >= nextFrameBounce || touchedBoundary()) bounce();
x += v;
return this;
}
Fish bounce() {
v *= -1;
nextFrameBounce = getNextFrameBounce();
return this;
}
int getNextFrameBounce() {
return frameCount + FPS * (int) random(MIN_SEC, MAX_SEC + 1);
}
boolean touchedBoundary() {
final int nextX = x + v;
return nextX < 0 || nextX > width - W;
}
String toString() {
return "Fish: { x: " + x + ", y: " + y + ", v: " + v + " }";
}
}
Functions.pde
void populateLake() {
for (int i = 0; i < FISHES; fishes[i] = new Fish(i++));
}
PImage createMirrorImage(final PImage img) {
final PGraphics pg = createGraphics(img.width, img.height);
pg.beginDraw();
pg.scale(-1, 1);
pg.image(img, -img.width, 0);
pg.endDraw();
return pg.get();
}
Preload.pde
/* @pjs preload = "
rainbow-fish-th.png,
https://OpenProcessing-UserContent.s3.AmazonAWS.com/files/user22279/visual1805772/h836d69cccc2d9f0e47fad8e146c132d0/rainbow-fish-th.png
"
*/
index.html
<link rel=icon href=rainbow-fish-th.png>
<script defer src=https://Unpkg.com/processing-js></script>
<canvas data-processing-sources=
"Swimming_Fish.pde Fish.pde Functions.pde Preload.pde">
</canvas>