To practice Autonomous Agents:
Compared with the basic code in class, I have done
1. Let the sardines swim in circles in a natural state
2. Change the mouse position to an automatically swimming shark
3. Realize the lerp color gradient background
4. Change the floating speed of the bubble according to the size of the bubble
1. Sardines naturally swim in a spiral around the center
circleMovement() {
let center = createVector(width/2, height/2);
Add noise to the radius
spiral Angle determines the angle of the circle the fish is currently at. A small constant of 0.005 is added to each frame to ensure continuous rotation.
let noiseValue = noise(this.noiseOffset + frameCount * 0.005);
let radiusWithNoise = radius * (0.8 + noiseValue * 0.4);
Update angle: basic increment + noise micro-jitter
this.spiralAngle += 0.005 + noise(this.noiseOffset, frameCount * 0.01) * 0.01;
Use cos/sin + dynamic radius to calculate the target point
let targetPos = createVector(
center.x + cos(this.spiralAngle) * radiusWithNoise,
center.y + sin(this.spiralAngle) * radiusWithNoise);
⭐️ Call seek() to get closer to each other!!!
this.seek(targetPos);}
First version
2.Replace "mouse position" with "swimming shark"
wander() {
An angle determined by noise
let wanderAngle = noise(frameCount*0.01) * TWO_PI * 2;
Find the "wander center": current speed direction * wanderDistance + current position
let wanderCenter = this.vel.copy().normalize().mult(80).add(this.pos);
Offset a point with radius=25 from the center
let offset = p5.Vector.fromAngle(wanderAngle).mult(25);
wanderCenter.add(offset);
Swim towards this random target point
this.seek(wanderCenter);
3.Key changes:
Steer limit
this.maxSteerForce = random(0.02,0.06);
avoidPredator(predator) {
Detection range
let distance = p5.Vector.dist(this.pos, predator.pos);
if (distance < predator.size * 3) {
-0.001%
this.vel.mult(0.999)
Code
let fishes = [];
let shark;
let noiseScale = 0.01;
let radius = 250;
let bubbles = [];
class Fish {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(random(-1, 1), random(-1, 1));
this.acc = createVector(0, 0);
this.size = random(7, 10);
this.mass = 1;
this.angle = 0;
this.maxSpeed = random(3, 4);
this.maxSteerForce = 0.05; //!change
this.senseRad = 150;
this.brakeRad = 50;
this.noiseOffset = random(1000);
this.spiralAngle = random(TWO_PI);
this.bodyColor = color(220, 220, 230, random(180, 230));
}
applyForce(force) {
let f = p5.Vector.div(force, this.mass);
this.acc.add(f);
}
circleMovement() {
let center = createVector(width / 2, height / 2);
//noise
let noiseValue = noise(this.noiseOffset + frameCount * 0.005);
let radiusWithNoise = radius * (0.8 + noiseValue * 0.4);
// update spiral angle
this.spiralAngle +=
0.005 + noise(this.noiseOffset, frameCount * 0.01) * 0.01;
//target position in spiral
let targetPos = createVector(
center.x + cos(this.spiralAngle) * radiusWithNoise,
center.y + sin(this.spiralAngle) * radiusWithNoise
);
this.seek(targetPos);
}
seek(targetVec) {
let desireVel = p5.Vector.sub(targetVec, this.pos);
let distance = desireVel.mag();
if (distance > this.senseRad) return;
desireVel.normalize();
if (distance < this.brakeRad) {
let speed = map(distance, 0, this.brakeRad, 0, this.maxSpeed);
desireVel.mult(speed);
} else {
desireVel.mult(this.maxSpeed);
}
let steerForce = p5.Vector.sub(desireVel, this.vel);
steerForce.limit(this.maxSteerForce);
this.applyForce(steerForce);
this.vel.mult(0.999); // !change
}
avoidPredator(predator) {
//detection dia
let distance = p5.Vector.dist(this.pos, predator.pos);
if (distance < predator.size * 2.5) {
// !change
let awayDir = p5.Vector.sub(this.pos, predator.pos);
let fleeFactor = map(distance, 0, predator.size * 4, 1.5, 0.1);
awayDir.normalize();
awayDir.mult(fleeFactor * this.maxSpeed * 2);
let fleeForce = p5.Vector.sub(awayDir, this.vel);
fleeForce.limit(this.maxSteerForce * 3);
this.applyForce(fleeForce);
}
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
this.angle = this.vel.heading();
}
checkEdges() {
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.y > height) this.pos.y = 0;
if (this.pos.y < 0) this.pos.y = height;
}
display() {
push();
translate(this.pos.x, this.pos.y);
rotate(this.angle + HALF_PI);
//body
noStroke();
fill(this.bodyColor);
ellipse(0, 0, this.size * 0.7, this.size * 1.5);
//tail
fill(this.bodyColor);
triangle(
0,
this.size * 0.8,
-this.size * 0.4,
this.size * 1.2,
this.size * 0.4,
this.size * 1.2
);
//eyes
fill(0);
ellipse(
-this.size * 0.15,
-this.size * 0.4,
this.size * 0.15,
this.size * 0.15
);
ellipse(
this.size * 0.15,
-this.size * 0.4,
this.size * 0.15,
this.size * 0.15
);
//glowing
fill(255, 255, 255, 120);
ellipse(0, -this.size * 0.1, this.size * 0.3, this.size * 0.6);
pop();
}
}
class Shark {
constructor() {
this.pos = createVector(random(width), random(height));
this.vel = createVector(random(-1, 1), random(-1, 1));
this.acc = createVector(0, 0);
this.mass = 5;
this.angle = 0;
this.maxSpeed = 2.0;
this.maxSteerForce = random(0.02, 0.06); //!change
this.senseRad = 300;
this.brakeRad = 100;
this.size = 70; //shark
}
applyForce(force) {
let f = p5.Vector.div(force, this.mass);
this.acc.add(f);
}
seek(targetVec) {
let desireVel = p5.Vector.sub(targetVec, this.pos);
let distance = desireVel.mag();
if (distance > this.senseRad) return;
desireVel.normalize();
if (distance < this.brakeRad) {
let speed = map(distance, 0, this.brakeRad, 0, this.maxSpeed);
desireVel.mult(speed);
} else {
desireVel.mult(this.maxSpeed);
}
let steerForce = p5.Vector.sub(desireVel, this.vel);
steerForce.limit(this.maxSteerForce);
this.applyForce(steerForce);
this.vel.mult(0.999); //!!change
}
wander() {
let wanderRadius = 25;
let wanderDistance = 80;
let wanderAngle = noise(frameCount * 0.01) * TWO_PI * 2;
let wanderCenter = this.vel.copy();
wanderCenter.normalize();
wanderCenter.mult(wanderDistance);
wanderCenter.add(this.pos);
let offset = p5.Vector.fromAngle(wanderAngle);
offset.mult(wanderRadius);
wanderCenter.add(offset);
this.seek(wanderCenter);
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
this.angle = this.vel.heading();
}
checkEdges() {
if (this.pos.x > width) {
this.pos.x = width;
this.vel.x *= -1;
}
if (this.pos.x < 0) {
this.pos.x = 0;
this.vel.x *= -1;
}
if (this.pos.y > height) {
this.pos.y = height;
this.vel.y *= -1;
}
if (this.pos.y < 0) {
this.pos.y = 0;
this.vel.y *= -1;
}
}
display() {
push();
translate(this.pos.x, this.pos.y);
rotate(this.angle + HALF_PI);
//body
fill(70, 80, 100);
noStroke();
ellipse(0, 0, this.size * 0.6, this.size * 1.2);
//tail
fill(60, 70, 90);
beginShape();
vertex(0, this.size * 0.6);
vertex(-this.size * 0.3, this.size);
vertex(0, this.size * 0.8);
vertex(this.size * 0.3, this.size);
endShape(CLOSE);
//fins
triangle(
-this.size * 0.3,
0,
-this.size * 0.6,
this.size * 0.2,
-this.size * 0.2,
this.size * 0.3
);
triangle(
this.size * 0.3,
0,
this.size * 0.6,
this.size * 0.2,
this.size * 0.2,
this.size * 0.3
);
//eyes
fill(255);
ellipse(
-this.size * 0.15,
-this.size * 0.3,
this.size * 0.12,
this.size * 0.12
);
ellipse(
this.size * 0.15,
-this.size * 0.3,
this.size * 0.12,
this.size * 0.12
);
fill(0);
ellipse(
-this.size * 0.15,
-this.size * 0.3,
this.size * 0.06,
this.size * 0.06
);
ellipse(
this.size * 0.15,
-this.size * 0.3,
this.size * 0.06,
this.size * 0.06
);
pop();
}
}
class Bubble {
constructor() {
this.pos = createVector(random(width), height + random(20));
this.size = random(12, 32);
this.speed = map(this.size, 3, 12, 0.1, 0.3);
this.alpha = random(30, 100);
}
update() {
this.pos.y -= this.speed;
this.pos.x += sin(frameCount * 0.05 + this.pos.y * 0.01) * 0.5;
if (this.pos.y < -this.size) {
this.pos.y = height + random(20);
this.pos.x = random(width);
}
}
display() {
push();
noStroke();
fill(155, 155, 255, this.alpha);
ellipse(this.pos.x, this.pos.y, this.size, this.size);
fill(255, 255, 255, this.alpha * 0.7);
ellipse(
this.pos.x + this.size * 0.2,
this.pos.y - this.size * 0.2,
this.size * 0.3,
this.size * 0.3
);
pop();
}
}
function setup() {
createCanvas(800, 600);
for (let i = 0; i < 150; i++) {
let fish = new Fish(random(width), random(height));
fishes.push(fish);
}
shark = new Shark();
for (let i = 0; i < 10; i++) {
bubbles.push(new Bubble());
}
}
function draw() {
for (let y = 0; y <= height; y++) {
//bg
let positionRatio = y / height;
let gradientColor;
if (positionRatio < 0.5) {
//upper half
let whiteColor = color(180, 180, 200);
let lightBlueColor = color(50, 100, 200);
let upperRatio = map(positionRatio, 0, 0.5, 0, 1);
gradientColor = lerpColor(whiteColor, lightBlueColor, upperRatio);
} else {
//under half
let lightBlueColor = color(50, 100, 200);
let darkBlueColor = color(5, 10, 40);
let lowerRatio = map(positionRatio, 0.5, 1, 0, 1);
gradientColor = lerpColor(lightBlueColor, darkBlueColor, lowerRatio);
}
stroke(gradientColor);
line(0, y, width, y);
}