xxxxxxxxxx
269
let birds = [];
const tileCount = 100;
const noiseScale = 0.03;
let noiseVector, noiseVelocity, noiseAcceleration;
let startScreen = true;
let startButton;
let windSlider;
let dayButton, nightButton;
let birdButton;
let isDay = true;
let skyColor;
let cloudColor;
function setup() {
createCanvas(windowWidth, windowHeight);
initializeFlock(5);
noiseVector = createVector(0, 0);
noiseVelocity = createVector(0.03, 0);
noiseAcceleration = createVector(
random(-0.005, 0.005),
random(-0.005, 0.005)
);
// Create start button
startButton = createButton("bird watching");
startButton.position(width / 2 - 75, height / 2 - 20);
startButton.size(150, 50);
startButton.style("color", "black");
startButton.style("font-size", "15px");
startButton.mousePressed(startSimulation);
// Create wind slider
windSlider = createSlider(0.5, 2, 1.2, 0.05);
windSlider.position(width - 350, 25);
windSlider.style("width", "100px");
windSlider.hide();
// Create day button
dayButton = createButton("Day");
dayButton.position(width - 160, 20);
dayButton.size(60, 30);
dayButton.mousePressed(setDay);
dayButton.hide();
// Create night button
nightButton = createButton("Night");
nightButton.position(width - 90, 20);
nightButton.size(60, 30);
nightButton.mousePressed(setNight);
nightButton.hide();
// Create birds button
birdButton = createButton("Birds");
birdButton.position(width - 230, 20);
birdButton.size(60, 30);
birdButton.mousePressed(addBirds);
birdButton.hide();
// Set initial colors
skyColor = color(150, 180, 255);
cloudColor = color(252, 230, 252);
}
function startSimulation() {
startScreen = false;
startButton.hide();
windSlider.show();
dayButton.show();
nightButton.show();
birdButton.show();
// Set the initial state to "Day" and grey out the Day button
isDay = true;
dayButton.style("background-color", "grey");
}
function setDay() {
isDay = true;
dayButton.style("background-color", "grey");
nightButton.style("background-color", "");
}
function setNight() {
isDay = false;
nightButton.style("background-color", "grey");
dayButton.style("background-color", "");
}
function draw() {
if (startScreen) {
drawStartScreen();
} else {
drawSimulation();
}
}
function mouseReleased() {
birdButton.style("background-color", "white");
}
function drawStartScreen() {
background(252, 230, 252);
}
function drawSimulation() {
let windFactor = windSlider.value();
// Transition background colors
let targetSkyColor = isDay ? color(150, 180, 255) : color(0, 50, 150);
let targetCloudColor = isDay ? color(252, 230, 252) : color(115, 61, 96);
skyColor = lerpColor(skyColor, targetSkyColor, 0.01);
cloudColor = lerpColor(cloudColor, targetCloudColor, 0.01);
// Draw the Perlin noise-based background
background(skyColor);
noiseVelocity.add(noiseAcceleration);
noiseVector.add(noiseVelocity.copy().mult(-windFactor));
noiseAcceleration.x = random(-0.0005, 0.0005);
noiseAcceleration.y = random(-0.0005, 0.0005);
let tileSize = width / tileCount;
for (let row = 0; row < tileCount; row++) {
for (let col = 0; col < tileCount; col++) {
let x = col * tileSize;
let y = row * tileSize;
let xnoise = noiseVector.x + col * noiseScale;
let ynoise = noiseVector.y + row * noiseScale;
let noiseValue = noise(xnoise, ynoise);
let a = map(noiseValue, 0, 0.5, 0, 210);
fill(cloudColor.levels[0], cloudColor.levels[1], cloudColor.levels[2], a);
noStroke();
rect(x, y, tileSize, tileSize);
}
}
// Update and display all birds
for (let bird of birds) {
bird.update(windFactor, windSlider.value());
bird.show();
}
}
class Bird {
constructor(x, y, birdSize, birdSpeed) {
this.x = x;
this.y = y;
this.birdSize = birdSize;
this.baseSpeed = birdSpeed;
this.flapAngle = 0;
this.flapSpeed = random(0.20, 0.22);
this.stopped = false; // Indicates if the bird is sitting
this.flewAway = false; // Tracks if the bird has flown off-screen
this.flockingRadius = 100;
this.wingOffset = random(0, TWO_PI); // Add an offset to stagger the wing flap
}
update(windFactor, sliderValue) {
// Check if the bird should be flying based on the slider value
if (!this.stopped || sliderValue > 0.5) {
// If the bird was stopped and the slider value is now above 0.5, change to flying
if (this.stopped && sliderValue > 0.5) {
this.stopped = false; // Change the bird's state to flying
}
// Move the bird forward at a constant speed
if (!this.flewAway) {
this.x -= this.baseSpeed / windFactor;
}
// If the bird reaches the left edge of the screen, mark it as having flown away
if (this.x < -50) {
this.flewAway = true;
}
// Stop the bird when it reaches the tree, with a slight random offset, if the slider is at 0.5
if (!this.flewAway && this.x <= 100 && sliderValue === 0.5) {
this.stopped = true;
this.x = 50 + random(-100, 75);
}
// Update the flap angle for wing movement
this.flapAngle += this.flapSpeed * windFactor;
// Apply flocking behavior if there are other birds
if (birds.length > 1) {
let cohesionForce = createVector(0, 0);
let count = 0;
// Calculate cohesion force based on nearby birds within the flocking radius
for (let otherBird of birds) {
if (otherBird !== this && !this.flewAway) {
let d = dist(this.x, this.y, otherBird.x, otherBird.y);
if (d < this.flockingRadius) {
cohesionForce.add(
createVector(otherBird.x - this.x, otherBird.y - this.y)
);
count++;
}
}
}
if (count > 0) {
// Average the cohesion force and apply a subtle force to maintain flocking
cohesionForce.div(count);
cohesionForce.setMag(0.1);
this.x += cohesionForce.x;
this.y += cohesionForce.y;
}
}
}
}
show() {
push();
translate(this.x, this.y);
scale(this.birdSize);
fill(0, 0, 0, 220);
this.drawFlyingBird(sin(this.flapAngle + this.wingOffset));
pop();
}
drawFlyingBird(curveFactor) {
let wingY = map(curveFactor, -1, 1, 0, -35);
let wingCurve = map(curveFactor, -1, 1, -25, -50);
// Left wing
beginShape();
vertex(0, 0);
quadraticVertex(-25, -10, -35, wingY);
quadraticVertex(-25, wingCurve, 0, 0);
endShape(CLOSE);
// Right wing
beginShape();
vertex(0, 0);
quadraticVertex(40, -15, 70, wingY);
quadraticVertex(40, wingCurve, 0, 0);
endShape(CLOSE);
}
}
// create flock of birds
function initializeFlock(count) {
let startX = windowWidth;
let startY = height / 2 + random(-75, 200);
let birdSize = 0.5;
let birdSpeed = 2;
for (let i = 0; i < 7; i++) {
let x = startX + i * 60;
let y = startY + i * 20 * (i % 2 === 0 ? 1 : -1);
birds.push(new Bird(x, y, birdSize, birdSpeed));
birdSize *= 0.95;
}
}
// add flock of birds when button is pressed
function addBirds() {
// Change the button color to grey when pressed
birdButton.style("background-color", "grey");
initializeFlock(birds.length + 5);
}