xxxxxxxxxx
132
let tadpole;
function setup() {
createCanvas(windowWidth, windowHeight);
tadpole = new Tadpole(width / 2, height / 2); // Initialize tadpole at the center
}
function draw() {
background(145, 224, 255);
tadpole.wander(); // Call wander to add randomness to movement
tadpole.update(); // Update position and apply accumulated forces
tadpole.show(); // Display the tadpole on the canvas
tadpole.edges(); // Keep the tadpole within canvas boundaries
}
class Tadpole {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
this.maxSpeed = 4; // Maximum movement speed
this.maxForce = 0.15; // Maximum steering force
this.r = 10; // Radius of the tadpole head
this.wanderTheta = PI / 2; // Initial wander angle
this.xoff = 0.5; // Noise offset for smoother wandering
this.currentPath = []; // Store tadpole path for tail effect
this.paths = [this.currentPath];
}
wander() {
let angle = noise(this.xoff) * TWO_PI * 1.5;
let steer = p5.Vector.fromAngle(angle);
steer.setMag(this.maxForce * 0.8);
// Apply the steering force to acceleration and update noise offset for gradual change
this.applyForce(steer);
this.xoff += 0.005; // Smaller increment to make motion changes more gradual
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.set(0, 0);
// Track position history for tail effect
this.currentPath.push(this.pos.copy());
// Manage path length for tail effect
let total = 0;
for (let path of this.paths) {
total += path.length;
}
if (total > 200 || (total > 10 && millis() > 3000)) {
this.paths[0].shift(); // Remove oldest position if path is too long
if (this.paths[0].length === 0) {
this.paths.shift();
}
}
}
// Display the tadpole
show() {
// Draw the head with eyes
fill(72, 73, 74);
noStroke();
push();
translate(this.pos.x, this.pos.y);
rotate(this.vel.heading());
ellipse(0, 0, this.r * 2, this.r * 2); // Draw head as a circle
// Draw eyes on the head
fill(255);
ellipse(this.r / 2, this.r / 4, this.r / 4, this.r / 4); // Left eye
ellipse(this.r / 2, -this.r / 4, this.r / 4, this.r / 4); // Right eye
pop();
// Draw a transparent tail that fades along the path
let tailLength = 50;
for (let i = 0; i < tailLength; i++) {
// Interpolate between current position and earlier path for smooth tail
let pos = p5.Vector.lerp(
this.pos,
this.currentPath[max(0, this.currentPath.length - 1 - i)],
i / tailLength
);
fill(44, 45, 46, map(i, 0, tailLength, 50, 0)); // Gradually fade tail opacity
noStroke();
ellipse(pos.x, pos.y, this.r - i * 0.3, this.r - i * 0.3); // Smaller ellipses for the tail
}
}
edges() {
let pushed = false;
// Check each boundary, reverse direction, and add a force toward the center if needed
if (this.pos.x > width - this.r) {
this.pos.x = width - this.r;
this.vel.x *= -1;
pushed = true;
} else if (this.pos.x < this.r) {
this.pos.x = this.r;
this.vel.x *= -1;
pushed = true;
}
if (this.pos.y > height - this.r) {
this.pos.y = height - this.r;
this.vel.y *= -1;
pushed = true;
} else if (this.pos.y < this.r) {
this.pos.y = this.r;
this.vel.y *= -1;
pushed = true;
}
// If a boundary was hit, apply a slight push toward the canvas center
if (pushed) {
let center = createVector(width / 2, height / 2);
let directionToCenter = p5.Vector.sub(center, this.pos);
directionToCenter.setMag(0.5); // Set the magnitude of the push
this.applyForce(directionToCenter);
}
}
}