Assignment Week #8 – Predator Prey Simulation

Predator-Prey simulation

For this assignment, I wanted to recreate an underwater chase scene. My goal was to show simulation through autonomous agents, like a single predatory shark chasing a school of fish. The fish reacts to the shark’s actions by fleeing. Each agent has its own set of rules and behaviors that it follows in their 2D ocean.

Implementation:

  • The Shark’s Path: To control the shark’s movement, I used a Perlin noise algorithm. This created a smooth, random path that mimics how unpredictable a real shark’s patrol through the water is.
  • Fish Fleeing:  In this state, their behavior changes dramatically. They prioritize moving away from the shark over any other actions they might be taking, such as wandering 
  • Boundary Constraints: I coded the fish to wrap around the edges of the canvas so they stay in the canvas
  • Shark Movement: Instead of being directly controlled, the shark follows a path made by perlin noise. I’ve tuned this function to keep the shark from moving randomly and give its hunt a bit of randomness. Also, the shark slows down when it gets closer to its target.

Sketch:

Code: 

The following is the function for sharks seeking their target which is calculated by Perlin noise.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Shark {
seek(target) {
let desired = p5.Vector.sub(target, this.position);
let distance = desired.mag();
desired.normalize();
if (distance < 100) {
let m = map(distance, 0, 100, 0, this.maxSpeed);
desired.mult(m); // Slow down as it gets closer to the target
} else {
desired.mult(this.maxSpeed);
}
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxForce);
this.applyForce(steer);
}
}
class Shark { seek(target) { let desired = p5.Vector.sub(target, this.position); let distance = desired.mag(); desired.normalize(); if (distance < 100) { let m = map(distance, 0, 100, 0, this.maxSpeed); desired.mult(m); // Slow down as it gets closer to the target } else { desired.mult(this.maxSpeed); } let steer = p5.Vector.sub(desired, this.velocity); steer.limit(this.maxForce); this.applyForce(steer); } }
class Shark {
  
  seek(target) {
    let desired = p5.Vector.sub(target, this.position);
    let distance = desired.mag();
    desired.normalize();
    if (distance < 100) {
      let m = map(distance, 0, 100, 0, this.maxSpeed);
      desired.mult(m); // Slow down as it gets closer to the target
    } else {
      desired.mult(this.maxSpeed);
    }
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxForce);
    this.applyForce(steer);
  }
  
}
  • The seek method in the Shark class steers the shark towards a target.
  • It calculates the direction to the target and if the shark is within 100 pixels, it slows down proportionally to the distance to avoid overshooting.
  • The map function adjusts the shark’s speed dynamically, allowing for a gradual stop as it reaches the target.
  • This prevents the shark from ‘spazzing’ or jittering when it gets close to where it wants to go.

The following is the function for fish fleeing the shark

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Fish {
flee(predator) {
let desired = p5.Vector.sub(this.position, predator.position);
let d = desired.mag();
if (d < 100) { // If the shark is within 100 pixels
desired.setMag(this.maxSpeed); // Set maximum speed
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxForce); // Limit the force to be applied
this.applyForce(steer);
}
}
}
class Fish { flee(predator) { let desired = p5.Vector.sub(this.position, predator.position); let d = desired.mag(); if (d < 100) { // If the shark is within 100 pixels desired.setMag(this.maxSpeed); // Set maximum speed let steer = p5.Vector.sub(desired, this.velocity); steer.limit(this.maxForce); // Limit the force to be applied this.applyForce(steer); } } }
class Fish {  
  flee(predator) {
    let desired = p5.Vector.sub(this.position, predator.position);
    let d = desired.mag();
    if (d < 100) { // If the shark is within 100 pixels
      desired.setMag(this.maxSpeed); // Set maximum speed
      let steer = p5.Vector.sub(desired, this.velocity);
      steer.limit(this.maxForce); // Limit the force to be applied
      this.applyForce(steer);
    }
  }
  
}
  • The flee function in the Fish class calculates the escape direction from the shark.
  • If the shark is within a certain range, the fish accelerates to its top speed away from the shark.
  • This quick response mimics a fish’s instinct to escape predators.

Challenges: 

  • Precision in Shark Movement: Improving the shark’s movement to stop it from “spazzing” as it gets to its target was a difficult job that required slowing the velocity as it got closer to the target.
  • Fish Behavior Realism: Making flocking behavior was hard to implement because you had to find a fine balance between random movement and organized group dynamics. After a shark chase, there were difficulties getting the fish to gather back into a proper formation. I think this aspect still needs some work to make it more realistic.

Future Improvements: 

  • User Interaction Features: I want to look into ways for users to connect more directly with the simulation, such as using different inputs to change the path of the shark or the fish.
  • Ecosystem Development: I like the idea of adding more species and environmental features, like currents or obstacles, to make the experience more interesting.
  • Develop Flocking Behavior: To make the fish’s reaction seem real, the flocking and evasion algorithms have to be tweaked to match how fish move naturally.

Leave a Reply

Your email address will not be published. Required fields are marked *