city

actually animated and can do way more stuff, I’ll post some gifs later

// made by alex
// iamalexbaker@gmail.com

class unit{
  PVector pos;
  color col;
  PVector size;
  float mass;
  unit base;
  float roof;
  ArrayList<unit> children = new ArrayList<unit>();
  boolean dead = false;
  ArrayList<unit> parentArray;
  boolean newBase = false;
  
  unit(PVector p, color c, PVector s, float m, unit b, ArrayList<unit> pa){
    pos = p; col = c; size = s; mass = m; base = b; parentArray = pa;
    if(b == null){
      roof = -size.y;
      if(mass < 0) newBase = true;
    }
    else{
      roof = b.roof;
    }
  }
  
  void update(float xp, float zp){
    //println(pos);
    pushMatrix();
    translate(pos.x + xp,pos.y,pos.z + zp);
    if(!dead){
      fill(col, 100);
      box(size.x, size.y, size.z);
    }
    popMatrix();
    if(mass > 0){
      pos.y += mass;
      if(pos.y >= base.roof){
        pos.y = base.roof;
        base.roof -= size.y;
        base.children.add(this);
        mass = 0;
        if(base.children.size() >= maxChildren) base.release();
      }
    if(base != null){
      if(base.mass < 0){
        mass = -gravity;
      }
    }
    }
    if(mass < 0){
        pos.y += mass;
        if(newBase && pos.y <= 0){
          newBase = false;
          pos.y = 0;
          mass = 0;
        }
        if(pos.y < deathCeiling){
          dead = true;
          //parentArray.remove(this);
        }
      }
  }
  
  void release(){
    //println("release "+children.size());
    //println("contains "+bases.contains(this));
    for(int i = children.size() - 1; i >= 0; i--){
      children.get(i).mass = -((i+2)*0.8)*gravity;
      //println("child "+base.children.get(i).mass);
    }
    mass = -gravity;
  }
}

// -----------
// PARAMETERS
// -----------
float uw = 50;
float ul = 40;
int rows = 20;
float xpan = -200;
float zpan = -500;
float gravity = 2;
float dropsPerSecond = 128;
float spawnHeight = -500;
int maxChildren = 7;
float deathCeiling = -700;
float panRate = 1.5;
float fullness = 12;
float colourVariance = 4;
float colourDepth = 0.2;
color bgc = color(230);
// -----------

ArrayList<unit> bases = new ArrayList<unit>();
ArrayList<unit> units = new ArrayList<unit>();
PVector shape;
float timer = 0;
float dropTime = 100000;
ArrayList<unit> toRemove = new ArrayList<unit>();
float columnX;
color[] col = new color[2];
int maxUnits = 1;

void setup(){
  size(800,800, P3D);
  
  shape = new PVector(random(80,120),random(2,10),random(80,120));
  timer = 0;
  dropTime = frameRate/dropsPerSecond;
  stroke(0);
  strokeWeight(3);
  //noStroke();
  //noFill();
  units.clear();
  bases.clear();
  maxUnits = 1;
  for(int cc = 0; cc < col.length; cc++) col[cc] = dullColour(200,0,255);
  float xp = 0;
  float zp = 0;
  
  for(int i = 0; i < 200; i++){
    //println(xp+" "+zp);
    bases.add(new unit(new PVector(xp, -spawnHeight, zp), lerpColor(col[0],col[1],random(1)), shape, -random(gravity,gravity*2), null, bases));
    zp += shape.z;
    if(i%rows == rows-1){
      zp -= shape.z * rows;
      xp += shape.x;
    }
  }
  columnX = xp;
}

void draw(){
  background(bgc);
  do{
    timer -= dropTime;
    newdrop();
  }while(timer > dropTime);
  translate(width/2,width/2,100);
  rotateY(0.3);
  rotateX(-PI/4);
  rotateZ(PI/16);
  toRemove.clear();
  for(unit u: bases){
    u.update(xpan, zpan);
    if(u.dead || u.pos.x<xpan-width) toRemove.add(u);
  }
  bases.removeAll(toRemove);
  toRemove.clear();
  for(unit u: units){
    u.update(xpan, zpan);
    if(u.dead || u.pos.x<xpan-width) toRemove.add(u);
  }
  units.removeAll(toRemove);
  int n = bases.size();
  float rateMult = 1 / (n / (rows*fullness));
  float rm2 = pow((columnX - (-xpan))/width, 2);
  if(units.size()>maxUnits) maxUnits = units.size();
  if(float(units.size())/maxUnits<0.5) maxUnits--;
  if(maxUnits==0) maxUnits = 1;
  println(bases.size()+" "+units.size()+" "+rateMult+" "+rm2+" "+(float(units.size())/maxUnits));
  xpan -= panRate * rm2;
  timer += 1;
  if(random(1000)>=(999-(rateMult*10))){
    newColumn(pow(float(units.size())/maxUnits,colourVariance));
  }
}

void newColumn(float cLevel){
  for(int i = 0; i < rows; i++){
    float colour = constrain(cLevel + (float(i)/rows)*colourDepth - colourDepth/2, 0.0, 1.0);;
    bases.add(new unit(new PVector(columnX, -spawnHeight, shape.z * i), lerpColor(col[0],col[1],colour), shape, -random(gravity,gravity*2), null, bases));
  }
  columnX += shape.x;
}

void newdrop(){
  unit b = bases.get(int(random(bases.size())));
  if(b.children.size()>=maxChildren) return;
  PVector spawnPos = new PVector(b.pos.x, spawnHeight, b.pos.z);
  unit u = new unit(spawnPos, b.col, shape, gravity, b, units);
  units.add(u);
}

color dullColour(float spread, float min, float max)
{
  color col;
  float x = random(min, max);
  col = color(constrain(x + random(spread) - spread/2, 0, 255), constrain(x + random(spread) - spread/2, 0, 255), constrain(x + random(spread) - spread/2, 0, 255));
  return col;
}

void keyPressed()
{
  if (keyCode==32) {
    saveFrame("city-"+hour()+"-"+minute()+"-"+second()+".png");
  }
  if (keyCode==10) {
    setup();
  }
}

Leave a comment