How to deduct 1 health point if a moving triangle collides with a moving point?

I want to figure out how to deduct 1 health point if a moving triangle collides with any moving point, and make that one particular point that has the triangle collided with disappear (if that’s possible). Just to note that the moving circles are not ellipses, they’re points with various different stroke weights to them. Once the health score hits -1, my plan is to try and program another screen to pop up and display the amount of time you lasted for.

flake[] flake = new flake[30];
float speed = 10;
int b;
color b1 = color(0, 0, 0);
color b2 = color(141, 84, 216);
int Y_AXIS = 1;
int s = 0;
int m = 0;
int health = 3;

void setup(){
 size(1000,800);
 for(int i = 0; i < flake.length; i++){
  flake[i] = new flake(); 
 }
}

void draw(){
  setGradient(0, 0, width, height, b1, b2, Y_AXIS);
  b = mouseY;  
  
  for(int i = 0; i < flake.length; i++){
   flake[i].show();
   flake[i].update();
  }
  
  display();  
  timer();
  healthScore();
}

void display() {
 fill(243, 142, 255);
 noStroke();
 triangle(917, b, 950, b-15, 950, b+15);
 
}

void setGradient(int x, int y, float w, float h, color c1, color c2, int axis ) {
  float deltaR = red(c2)-red(c1);
  float deltaG = green(c2)-green(c1);
  float deltaB = blue(c2)-blue(c1);

  if (axis == Y_AXIS) {

    for (int i=x; i<=(x+w); i++) {
      // row
      for (int j = y; j<=(y+h); j++) {
        color c = color(
          (red(c1)+(j-y)*(deltaR/h)), 
          (green(c1)+(j-y)*(deltaG/h)), 
          (blue(c1)+(j-y)*(deltaB/h)));
        set(i, j, c);
      }
    }
  }
}

void timer(){
  textAlign(100,100);
  textSize(30);
  
  if(s<=59){
   text(m+":"+s,100,100);
   s=s+1;
  }
  else{
   m=m+1;
   s=0;
   text(m+":"+s,100,100);
  }
}

void healthScore(){
  textSize(32);
  text("Health: "+health, 100, 50);  
}

Moving circles class:

class flake{
  float y=random(height);
  float x=random(-width);
  
  float z = random(20, 100);
  float size=map(z, 1, 30, 3, 20);
  float speed=map(z, 1, 30, 0.1, 1);
  
  void show(){
    stroke(248, 188, 255);
    strokeWeight(size);
    point(x,y);
  }
  
  void update() {
    x+=speed;
    
    if(x>width) reset();
  }
  
  void reset(){
   y = random(height);
   x = random(-width,0);
   z = random(20,100);
   size=map(z,1,29,3,30);
   speed=map(z,1,29,0.1,1);
  }
  
}
1 Like

You’ll first need to start with detecting collisions between the circles and the triangles. If you want it to work really nice I would use a polygon / circle collision method, which I got from this link (which also has a lot of other nice solutions for collision)

I copied the relevant bit of code here:

// POLYGON/CIRCLE
boolean polyCircle(PVector[] vertices, float cx, float cy, float r) {

  // go through each of the vertices, plus
  // the next vertex in the list
  int next = 0;
  for (int current=0; current<vertices.length; current++) {

    // get next vertex in list
    // if we've hit the end, wrap around to 0
    next = current+1;
    if (next == vertices.length) next = 0;

    // get the PVectors at our current position
    // this makes our if statement a little cleaner
    PVector vc = vertices[current];    // c for "current"
    PVector vn = vertices[next];       // n for "next"

    // check for collision between the circle and
    // a line formed between the two vertices
    boolean collision = lineCircle(vc.x,vc.y, vn.x,vn.y, cx,cy,r);
    if (collision) return true;
  }

  // the above algorithm only checks if the circle
  // is touching the edges of the polygon – in most
  // cases this is enough, but you can un-comment the
  // following code to also test if the center of the
  // circle is inside the polygon

  // boolean centerInside = polygonPoint(vertices, cx,cy);
  // if (centerInside) return true;

  // otherwise, after all that, return false
  return false;
}


// LINE/CIRCLE
boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) {

  // is either end INSIDE the circle?
  // if so, return true immediately
  boolean inside1 = pointCircle(x1,y1, cx,cy,r);
  boolean inside2 = pointCircle(x2,y2, cx,cy,r);
  if (inside1 || inside2) return true;

  // get length of the line
  float distX = x1 - x2;
  float distY = y1 - y2;
  float len = sqrt( (distX*distX) + (distY*distY) );

  // get dot product of the line and circle
  float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2);

  // find the closest point on the line
  float closestX = x1 + (dot * (x2-x1));
  float closestY = y1 + (dot * (y2-y1));

  // is this point actually on the line segment?
  // if so keep going, but if not, return false
  boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY);
  if (!onSegment) return false;

  // optionally, draw a circle at the closest point
  // on the line
  fill(255,0,0);
  noStroke();
  ellipse(closestX, closestY, 20, 20);

  // get distance to closest point
  distX = closestX - cx;
  distY = closestY - cy;
  float distance = sqrt( (distX*distX) + (distY*distY) );

  // is the circle on the line?
  if (distance <= r) {
    return true;
  }
  return false;
}


// LINE/POINT
boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) {

  // get distance from the point to the two ends of the line
  float d1 = dist(px,py, x1,y1);
  float d2 = dist(px,py, x2,y2);

  // get the length of the line
  float lineLen = dist(x1,y1, x2,y2);

  // since floats are so minutely accurate, add
  // a little buffer zone that will give collision
  float buffer = 0.1;    // higher # = less accurate

  // if the two distances are equal to the line's
  // length, the point is on the line!
  // note we use the buffer here to give a range, rather
  // than one #
  if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) {
    return true;
  }
  return false;
}


// POINT/CIRCLE
boolean pointCircle(float px, float py, float cx, float cy, float r) {
  
  // get distance between the point and circle's center
  // using the Pythagorean Theorem
  float distX = px - cx;
  float distY = py - cy;
  float distance = sqrt( (distX*distX) + (distY*distY) );

  // if the distance is less than the circle's 
  // radius the point is inside!
  if (distance <= r) {
    return true;
  }
  return false;
}

Now all that needs to be done is checking the collisions between the circles and triangle and reduce health.

Edit: I just noticed you were specifying point, so in that case this code will do what you want

3 Likes

Thanks for the help. I attempted to implement it into my own code, but I don’t seem to be getting anywhere. The health score randomly deducts by a few points at a time, and I’m not sure why. I’m very new to programming in general, and it all seems so confusing.

class flake{
  float y=random(height);
  float x=random(-width);
  
  float z = random(20, 100);
  float size=map(z, 1, 30, 3, 20);
  float speed=map(z, 1, 30, 0.1, 1);
  
  float x1 = 917;     
  float y1 = b;
  float x2 = 950;
  float y2 = b-15;
  float x3 = 950;
  float y3 = b+15;
  
  void show(){
    stroke(248, 188, 255);
    strokeWeight(size);
    point(x,y);
  }
  
  void update() {
    x+=speed;
    
    boolean hit = triPoint(x1,y1, x2,y2, x3,y3, x,y);
  if (hit) health--;
    
    if(x>width) reset();
  }
  
  void reset(){
   y = random(height);
   x = random(-width,0);
   z = random(20,100);
   size=map(z,1,29,3,30);
   speed=map(z,1,29,0.1,1);
  }
  
  
    boolean triPoint(float x1, float y1, float x2, float y2, float x3, float y3, float x, float y) {

  float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) );

  float area1 =    abs( (x1-x)*(y2-y) - (x2-x)*(y1-y) );
  float area2 =    abs( (x2-x)*(y3-y) - (x3-x)*(y2-y) );
  float area3 =    abs( (x3-x)*(y1-y) - (x1-x)*(y3-y) );

  if (area1 + area2 + area3 == areaOrig) {
    return true;
  }
  return false;
  }
  
  
  

  

}

  
  


sure thing! It seems you have the right idea so I tidied up the code. Also to prevent copious amounts of health being lost I would add a boolean (let’s call it active) to your flake class and set it to true. Then only run the health-- code when both triPoint() and active are true, and set active equal to false. Set it back to true when you run reset()

Also you should move the x1, y1… declarations out of the flake constructor and into update

class flake {
  float y=random(height);
  float x=random(-width);

  float z = random(20, 100);
  float size=map(z, 1, 30, 3, 20);
  float speed=map(z, 1, 30, 0.1, 1);

  boolean active = true;

  void show() {
    if (active) {
      stroke(248, 188, 255);
    } else {
      stroke(7, 255-188, 0);
    }
    strokeWeight(size);
    point(x, y);
  }

  void update() {
    x+=speed;

    if (x>width) reset();
if (active) {    
float x1 = 917;     
    float y1 = b;
    float x2 = 950;
    float y2 = b-15;
    float x3 = 950;
    float y3 = b+15;
    
      if (triPoint(x1, y1, x2, y2, x3, y3, x, y)) {
        println("hit");
        health--;
        active=false;
      }
    }
  }

  void reset() {
    y = random(height);
    x = random(-width, 0);
    z = random(20, 100);
    size=map(z, 1, 29, 3, 30);
    speed=map(z, 1, 29, 0.1, 1);
    active=true;
  }
}
boolean triPoint(float x1, float y1, float x2, float y2, float x3, float y3, float x, float y) {

  float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) );

  float area1 =    abs( (x1-x)*(y2-y) - (x2-x)*(y1-y) );
  float area2 =    abs( (x2-x)*(y3-y) - (x3-x)*(y2-y) );
  float area3 =    abs( (x3-x)*(y1-y) - (x1-x)*(y3-y) );

  if (area1 + area2 + area3 == areaOrig) {
    return true;
  }
  return false;
}

I also made it so the circles turn an ugly green color when they hit :man_shrugging:

1 Like

Thank you!! very helpful.

1 Like