(14) Astroid generation with collisions

So if we combine the info in (12) and (13) we should be able to get astroid generation where the astroids can collide. We will approximate the astroids with circles and ignore that they actually have a slightly different shape. If an astroid collides, we will split it into two and make them smaller, until we reach a minimum size.

While I was doing this, many of my attempts just stopped (hung, crashed, etc). This was because when they collided the new generated astroids immediately also crashed into eachother. This was solved by giving each astroid an "ID" number (like a social-security/personnummer). When two astroids collide, all "offspring" will have the same "ID" and no collisions happen between the same "ID".

If you take the code below and run it, it will look like this (but random):


The code is:

ArrayList<astroid> astroids = new ArrayList<astroid>();

// Settings - how many seconds between each new astroid (1 seconds = 1 * 60)
int astroid_rate = 1 * 60;
int astroid_count = 60;
// Size in pixel of nominal astroid
float ast_size = 10;
float ast_min_size = 0.25;
int ast_id = 1;

// Run once
void setup () {
  frameRate(60);
  size(500, 500);
}

// Called 60 times per second
void draw()
{
  int i;

  // 1 new astroid every 5 seconds (60 fps * 4 sec)
  if (astroid_count--==0) {
    astroids.add(new astroid(random(0, TWO_PI), random(0.1, 2.0), random(0.5, 4), random(-0.1, 0.1), 
      random(-150, 150), random(-150, 150), ast_id++));
    // Increase rate just a little
    astroid_count = astroid_rate--;
    if (astroid_rate<30) astroid_rate=30;
  }

  // Clear screen, black
  background(0);
  // Go through all astroids (if any) and update their position
  for (i = 0; i<astroids.size(); i++) {
    astroid a = astroids.get(i);
    if (a.update()) {
      // Remove bullet, if outside screen
      astroids.remove(i);
    }
  }
}


// Class definition for the shot
class astroid {
  // An astroid angle, speed, size, rotation
  float angle, speed, size, rotSpeed;
  float position;
  float rotation;
  float xoff, yoff;
  float x, y;
  PShape s;  // The PShape object - Keeps the astroid shape
  float i;
  int id;


  // Constructor  
  astroid(float _angle, float _speed, float _size, float _rotSpeed, float _xoff, float _yoff, int _id) {
    angle = _angle;
    speed = _speed;
    size = _size;
    rotSpeed = _rotSpeed;
    xoff = _xoff;
    yoff = _yoff;
    id = _id;
    if (xoff<1000) {
      x = 250+500*cos(angle)+xoff;
      y = 250+500*sin(angle)+yoff;
    } else {
      x = _xoff-2000;
      y = _yoff-2000;
    }
    rotation = 0; 
    // Generate the shape of the astroid - Some variations for all
    s = createShape();
    s.beginShape();
    s.fill(255, 255, 100);
    s.noStroke();
    for (i=0; i<TWO_PI; i=i+PI/(random(4, 11))) {
      s.vertex(random(ast_size*0.8, ast_size*1.2)*cos(i), random(ast_size*0.8, ast_size*1.2)*sin(i));
    }
    s.endShape(CLOSE);
  }

  // Update position, return true when out of screen
  boolean update() {
    int i;
    x = x - cos(angle)*speed;
    y = y - sin(angle)*speed;
    rotation = rotation + rotSpeed; 

    // Check for astroid vs astroid collision
    for (i = 0; i<astroids.size(); i++) {
      astroid a = astroids.get(i);
      // If we are not our own astroid, check collision
      if ((a != this) && (a.coll(x, y, ast_size*size, id))) {
        // if size is too small, do not generate new astroid
        if (size > ast_min_size) {
          astroids.add(new astroid(angle-random(PI/5, PI/7), speed+random(0, speed/2), size/2, rotSpeed, 2000+x, 2000+y, id));
          astroids.add(new astroid(angle+random(PI/5, PI/7), speed+random(0, speed/2), size/2, rotSpeed, 2000+x, 2000+y, id));
        }
        astroids.remove(i);
      }
    }

    pushMatrix();
    // Set position as the new 0,0 
    translate(x, y);
    // Rotate screen "angle" 
    rotate(rotation);
    // Draw astroid
    scale(size);
    shape(s, 0, 0);
    // Bring back normal perspektive
    popMatrix();

    if (x<-300 || x>800 || y<-300 || y>800) {
      return true;
    } else {
      return false;
    }
  }

  // Detect collision between this astroid and the one passed in
  boolean coll(float _x, float _y, float _size, int _id) {
    float dist;

    dist = sqrt ((x-_x)*(x-_x) + (y-_y)*(y-_y));

    // Check if distance is shorter than astroid size and other objects size
    // No collision if the ID is same for both astroids
    if ((dist<(_size+ast_size*size)) && (id!=_id)) {
      // Collision, set the ID to the same so we do not call over and over again
      if (_id>0) id = _id;
      if (size > 1) {
        // If the astroid was "large" generate two new fragments
        astroids.add(new astroid(angle-random(PI/5, PI/7), speed+random(0, speed/2), size/2, rotSpeed, 2000+x, 2000+y, id));
        astroids.add(new astroid(angle+random(PI/5, PI/7), speed+random(0, speed/2), size/2, rotSpeed, 2000+x, 2000+y, id));
      }
      return true;
    } else { 
      return false;
    }
  }
}

See what happens if you change these variables. Like setting this:

// Size in pixel of nominal astroid
float ast_size = 20;
float ast_min_size = 0.1;

In the actual game, we probably will use "ast_min_size=1" or so to not make it impossible :)

No comments:

Post a Comment