Concept
The idea for this assignment is to simulate the behavior of different living organisms that follow a certain leader. I just thought the idea of following something was interesting. In this case, the lead insect is wandering; however, the rest of the colony is seeking him.
Code
let leader; let followers = []; let numFollowers = 100; let desiredSeparation = 50; // Minimum distance between flies function setup() { createCanvas(windowWidth, windowHeight); // Create leader fly with random starting position leader = new Vehicle(random(width), random(height), true); // Create follower flies for (let i = 0; i < numFollowers; i++) { followers.push(new Vehicle(random(width), random(height), false)); } } function draw() { background(220, 30); // Transparent background for trail effect // Leader wanders around leader.wander(); leader.update(); leader.checkEdges(); // Ensure leader stays in bounds leader.show(); // Followers seek the leader and avoid overlapping for (let follower of followers) { follower.separate(followers); // Avoid overlapping with other followers follower.seek(leader.position); // Seek the leader's current position follower.update(); follower.checkEdges(); // Ensure followers stay in bounds follower.show(); } } // Vehicle class definition class Vehicle { constructor(x, y, isLeader = false) { this.position = createVector(x, y); this.velocity = createVector(random(-1, 1), random(-1, 1)); this.acceleration = createVector(0, 0); this.r = 6; this.maxspeed = isLeader ? 2 : 2.5; // Leader moves slightly faster this.maxforce = 0.05; // Reduce max force for smoother movements this.isLeader = isLeader; this.wanderTheta = 0; // Used for wander behavior for leader } // Wander behavior for the leader wander() { let wanderR = 25; // Radius for our "wander circle" let wanderD = 80; // Distance for the wander circle to be ahead of the vehicle let change = 0.3; this.wanderTheta += random(-change, change); // Randomly change wanderTheta // Now we have to calculate the new target let circleLoc = this.velocity.copy(); circleLoc.setMag(wanderD); // Move circle ahead of vehicle circleLoc.add(this.position); // Make it relative to the vehicle's position let h = this.velocity.heading(); // Heading angle of the vehicle let circleOffset = createVector(wanderR * cos(this.wanderTheta + h), wanderR * sin(this.wanderTheta + h)); let target = p5.Vector.add(circleLoc, circleOffset); this.seek(target); // Seek the wandering target } // Method to update location update() { this.velocity.add(this.acceleration); this.velocity.limit(this.maxspeed); // Limit speed this.position.add(this.velocity); this.acceleration.mult(0); // Reset acceleration to 0 after each update } // A method that calculates a steering force towards a target seek(target) { let desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target desired.setMag(this.maxspeed); // Scale to maximum speed let steer = p5.Vector.sub(desired, this.velocity); // Steering = Desired minus velocity steer.limit(this.maxforce); // Limit to maximum steering force this.applyForce(steer); } // Separation behavior: Avoid overlapping separate(vehicles) { let sum = createVector(0, 0); let count = 0; // Check all other vehicles for (let other of vehicles) { let d = p5.Vector.dist(this.position, other.position); if ((d > 0) && (d < desiredSeparation)) { // Calculate vector pointing away from neighbor let diff = p5.Vector.sub(this.position, other.position); diff.normalize(); diff.div(d); // Weight by distance sum.add(diff); count++; // Keep track of how many are too close } } // Average out the forces if (count > 0) { sum.div(count); sum.setMag(this.maxspeed); // Implement Reynolds: Steering = Desired - Velocity let steer = p5.Vector.sub(sum, this.velocity); steer.limit(this.maxforce); this.applyForce(steer); } } // Apply force to the vehicle applyForce(force) { this.acceleration.add(force); } // Check edges to ensure vehicle stays within the canvas checkEdges() { if (this.position.x > width) { this.position.x = width; this.velocity.x *= -1; } else if (this.position.x < 0) { this.position.x = 0; this.velocity.x *= -1; } if (this.position.y > height) { this.position.y = height; this.velocity.y *= -1; } else if (this.position.y < 0) { this.position.y = 0; this.velocity.y *= -1; } } // Display the vehicle as a triangle pointing in the direction of velocity show() { let angle = this.velocity.heading() + PI / 2; fill(this.isLeader ? color(255, 0, 0) : color(0, 255, 0)); // Leader is red, followers are green stroke(0); push(); translate(this.position.x, this.position.y); rotate(angle); beginShape(); vertex(0, -this.r * 2); vertex(-this.r, this.r * 2); vertex(this.r, this.r * 2); endShape(CLOSE); pop(); } }
The simulation uses a vehicle class that has different attributes including and is leader attribute which becomes true when the vehicle is a leader. The class also has a seek-and-wander method. The wander method makes the vehicle wander around by creating a circle around the head of the vehicle and making the vehicle seek that circle. The seek method takes a target and makes the vehicle seek the target object.
Reflection and Future Improvements
The mechanics of the motion look good however the sketch might need more styling and polishing. Another thing would be making the leader controllable using a mouse.