Interactive Simulation: Urban System

Brief

Create a sketch that would simulate an existing natural system - look at physics, biology, and other natural sciences for good examples. Start with the environment - where is the system situated? What are the forces the environment might exert on the system? Examine the agents in the system, their relationships to each other and the environment they are in. The look at how this system would develop over time. What are the rules that you are going to come up with, what are the parameters you are going to feed into them and what effect will the changes have on the development of the system.

  • create classes of entities to represent the components of the system

  • use vectors to represent forces existing in the system

  • use randomness or noise to generate at least one

  • add direct or indirect mouse or keyboard controls

Ideation

First Phase (In-Class)

When I was first introduced to this brief, my idea was to create a eco system in rainforest. Then I changed my plan because eco system in rainforest was too complicated for me.

Next idea was a very simple sketch where cows are eating grass, and when they eat 70% of the grass on the canvas, cows would leave (no food for them). Although it was a very simple idea, I was having trouble making path that follows cows to indicate that the area was eaten already. It wasn’t for me.

Here are the ideation sketches that I designed during the workshop:

After the first phase at the workshop, I went home very, very sad.

Second Phase (Revenge)

After the class, right outside of my apartment I almost got hit by a car. With a lot of frustration, I decided to just change everything that I brainstormed during the class, and make a road full of cars and a poor pedestrian who increases their anxiety level whenever getting hit by cars.

Here are some of the ideas I wanted to include:

  • The system is situated in an urban city environment with cars, traffic lights, and a pedestrian.

  • The environment exerts forces on the cars in the form of traffic lights that control when the cars can move. Additionally, there's the presence of a pedestrian who interacts with the cars.

  • The agents in the system

    • cars (the cars interact with traffic lights and potentially with the pedestrian)

    • traffic lights

    • a pedestrian

  • The system develops over time as cars move, obey traffic lights, and potentially interact with the pedestrian. Traffic lights change colors over time.

After creating this first design, I realized that there were some issues:

  • The cars were too big considering that this is a small 800x400 canvas

  • As you see the number “1012”, whenever a car touch a pedestrian the number goes up way too much, even if it’s just a one second interaction

  • This design didn’t include function triggered by mouse or keyboard, so there was no interaction

Final Design: Urban System

After the second phase, I started implementing what I thought would be able to do to make the design better. This includes adjusting the size of the cars and adding mouse and/or keyboard triggers to the design.

Here’s the video of the final design:

  • The rules of the system include traffic light behavior, car movement, car interaction with traffic lights, and interaction with the pedestrian.

  • The code uses vectors for positions and speeds of entities, such as the positions of cars and the pedestrian.

  • Randomness is used in the initial speed and color of the cars, as well as in the traffic light change intervals.

  • There's direct keyboard control to pause the cars (with the 'P' key), accelerate the cars (with the right arrow key), and change lanes (with the left arrow key).

Overall, the code simulates a natural system, resembling aspects of traffic flow in a city. It incorporates various elements, forces, and interactions to create a dynamic simulation.

Takeaway

This coding assignment enhanced my skill of turning my creative idea into reality by coding, which I thought I could never do. This helped me understand how different parts of a system interact and how to solve problems, all while having fun, and I learned from various fields to make my simulation realistic.

Adding interactivity turned this design into an engaging game, and it was an opportunity to me to make my project visually appealing and practice troubleshooting.

Overall, even with the struggles, it was a fun way to be imaginative and improve my coding skills.


Code

Here’ s my final code, or you can check it at my OpenProcessing page!

🚨 The keyboard triggers are not working on OpenProcessing for some reason, so I also attached the zip file here as well.

int canvasWidth = 800;
int canvasHeight = 400;
boolean pauseCars = false;
boolean accelerateCars = false;

TrafficCitySimulation trafficSimulation;

void setup() {
  size(600, 400);
  trafficSimulation = new TrafficCitySimulation();
}

void keyPressed() {
  if (key == 'p' || key == 'P') {
    pauseCars = !pauseCars;
  }
  if (keyCode == RIGHT) {
    accelerateCars = true;
  }
}

void keyReleased() {
  if (keyCode == RIGHT) {
    accelerateCars = false;
  }
}

void draw() {
  trafficSimulation.updateAndDisplay();
}

class Car {
  float x;
  float y;  // Add y variable
  float radius = 15; // Car radius
  float speed;
  color col;
  boolean stopped = false;

  Car() {
    x = random(canvasWidth);
    y = canvasHeight / 2 + 10;  // Set initial y position

    speed = random(1, 3);
    col = color(random(255), random(255), random(255));
    stopped = false;
  }

  void update() {
    if (!pauseCars && !stopped) {
      if (accelerateCars) {
        speed = random(3, 6); // Increase speed when the right arrow key is pressed
      } else {
        speed = random(1, 3);
      }

      x += speed;
      if (x > canvasWidth) {
        x = 0;
      }
      if (x < 0) {
        x = canvasWidth;
      }
      if (y > canvasHeight / 2 && y < canvasHeight / 2 + 20) {
        float distance = dist(x, y, trafficSimulation.pedestrianPosition.x, trafficSimulation.pedestrianPosition.y);
        if (distance < radius + trafficSimulation.pedestrianRadius) {
          // Car touched the pedestrian, increment count
          trafficSimulation.pedestrianCount++;
        }
      }
    }
  }

  void display() {
    fill(col);
    noStroke();
    rect(x, canvasHeight / 2 + 10, 40, 20);
  }

  void checkTrafficLight(ArrayList<TrafficLight> lights) {
    boolean anyRedLightInFront = false;
    for (TrafficLight light : lights) {
      if (light.isRed() && light.isCarInFront(this)) {
        // Adjust the stopping distance to create a safety buffer
        float stoppingDistance = random(20, 50);
        if (light.distanceToCar(this) < stoppingDistance) {
          anyRedLightInFront = true;
          break;
        }
      }
    }
    stopped = anyRedLightInFront;
    if (!stopped) {
      speed = random(1, 3);
    }
  }
}

class TrafficLight {
  float x;
  float y;
  boolean red;
  int changeInterval;

  TrafficLight(float x, float y, int changeInterval) {
    this.x = x;
    this.y = y;
    this.changeInterval = changeInterval;
    red = true;
  }

  int getChangeInterval() {
    return changeInterval;
  }

  void update() {
    // Implement traffic light behavior here
  }

  void display() {
    fill(100);
    rect(x, y, 20, 60);

    if (red) {
      fill(255, 0, 0);
      ellipse(x + 10, y + 20, 15, 15);
      fill(0);
      ellipse(x + 10, y + 40, 15, 15);
    } else {
      fill(0);
      ellipse(x + 10, y + 20, 15, 15);
      fill(0, 255, 0);
      ellipse(x + 10, y + 40, 15, 15);
    }
  }

  void toggleLight() {
    red = !red;
  }

  boolean isRed() {
    return red;
  }

  boolean isCarInFront(Car car) {
    return dist(x + 10, y + 20, car.x, canvasHeight / 2 + 10) < 50;
  }
  
   float distanceToCar(Car car) {
    return dist(x + 10, y + 20, car.x, canvasHeight / 2 + 10);
  }
}

class TrafficCitySimulation {
  ArrayList<Car> cars;
  ArrayList<TrafficLight> lights;
  PVector pedestrianPosition; // Position of the pedestrian
  float pedestrianRadius = 10; // Pedestrian radius
  boolean pedestrianActive; // Flag to activate the pedestrian
  float pedestrianSpeed; // Speed of the pedestrian
  int pedestrianCount;

  TrafficCitySimulation() {
    cars = new ArrayList<Car>();
    lights = new ArrayList<TrafficLight>();

    // Create traffic lights with different intervals
    lights.add(new TrafficLight(canvasWidth / 2 - 120, canvasHeight / 2, 120)); // Change every 2 seconds
    lights.add(new TrafficLight(canvasWidth / 2 + 120, canvasHeight / 2, 90));  // Change every 1.5 seconds

    // Create cars
    for (int i = 0; i < 10; i++) {
      cars.add(new Car());
    }

    // Initialize pedestrian properties
    pedestrianPosition = new PVector(canvasWidth / 2, -20); // Start from the top edge
    pedestrianActive = true; // Activate the pedestrian
    pedestrianSpeed = 2; // Set pedestrian speed
    pedestrianCount = 0;
  }

  void updateAndDisplay() {
    background(155, 153, 153);
    fill(0);
    ellipse(pedestrianPosition.x, pedestrianPosition.y, 20, 20);

    // Update and display traffic lights
    for (TrafficLight light : lights) {
      light.update();
      light.display();
    }

    // Update and display cars
    for (Car car : cars) {
      car.update();
      car.display();
      car.checkTrafficLight(lights);
    }

    // Update and display pedestrian
    updatePedestrian();
    displayPedestrian();

    // Display pedestrian count
    fill(232, 232, 232);
    textSize(24);
    text("Pedestrian's Anxiety Level: " + pedestrianCount, 30, 60);
    textSize(18);
    text("- Press 'P' to stop the cars", 30, 320);
    text("- Press & hold right arrow key to spped up", 30, 340);
    text("- Hitting the pedestrian makes them anxious :(", 30, 360);



    // Check if it's time to change the traffic lights
    for (TrafficLight light : lights) {
      if (frameCount % light.getChangeInterval() == 0) {
        light.toggleLight();
      }
    }
  }

  void updatePedestrian() {
    if (pedestrianActive) {
      pedestrianPosition.y += pedestrianSpeed;
      if (pedestrianPosition.y > canvasHeight) {
        // Reset the pedestrian when it goes off the canvas
        pedestrianPosition.y = -20;
        pedestrianPosition.x = canvasWidth / 2;
      }
    }
  }

  void displayPedestrian() {
    fill(0); // Black color
    noStroke();
    ellipse(pedestrianPosition.x, pedestrianPosition.y, 20, 20);
  }
}
Previous
Previous

Music Machine

Next
Next

Generative Animation: A Clock for Aliens