Collision detection of object works, but not always.

edited February 2016 in Questions about Code

I made an array with 10 to 20 objects flying around on the screen. The objects bounce of the wall. I tried including "collision-detection", which works but not all the time. Why is this? (PS: you can change the number of arms of the wheels with "a" en "q")

float r = 40, radhoek; 
int arm=4;
float[] xpos = new float[20];
float[] ypos = new float[20];
int numberwheels = 16;
TurningWheel[] turningwheel = new TurningWheel[numberwheels];

void setup() {
  size(800, 800);
  for (int count=0; count <numberwheels; count=count+1) {
    turningwheel[count] = new TurningWheel(random(60, 800), random(60, 800));
  }
}

void draw() {
  background(230, 180, 160);
  for (int count=0; count <numberwheels; count=count+1) {
    turningwheel[count].display();
    turningwheel[count].move();
    turningwheel[count].detect();
  }
}

void keyPressed() {
  if (key == 'a') {
    arm++;
  } else if (key == 'q') {
    arm--;
  }
  arm = constrain(arm, 1, 16);
}

class TurningWheel {
  float xpos, ypos, hoek = random(0, 360);
  float speedx = random(-4, 4);
  float speedy = random(-4, 4);
  color kolor = color(random(150, 255), random(150, 255), random(150, 255));

  TurningWheel (float tempxpos, float tempypos) {
    xpos = tempxpos;
    ypos = tempypos;
  }

  void display() {
    radhoek=radians(hoek);
    for (int i = 0; i < arm; i++) {
      float[] xarmen = new float[arm];
      float[] yarmen = new float[arm];  
      xarmen[i] = xpos + r * cos(radhoek+radians(i*360/arm));
      yarmen[i] = ypos + r * sin(radhoek+radians(i*360/arm));
      line(xpos, ypos, xarmen[i], yarmen[i]);
      fill(kolor);
      ellipse(xarmen[i], yarmen[i], 6, 6);
    }
    ellipse(xpos, ypos, 20, 20);
  }

  void move() {
    if (hoek>360) {
      hoek=1;
    }
    hoek+=2;

    if (xpos > (width-42) || xpos < 42) {
      speedx=speedx * -0.95;
    }
    if (ypos > (height-42) || ypos < 42) {
      speedy=speedy * -0.95;
    }

    xpos+=speedx;
    ypos+=speedy;
  }

  void detect() {
    for (int i = 0; i<numberwheels; i++) {
      for (int j = 0; j<numberwheels; j++) {  

        if (dist(turningwheel[i].xpos, turningwheel[i].ypos, turningwheel[j].xpos, turningwheel[j].ypos) < 20 && i != j) {
          println("crash!", "wheel", i, "and wheel", j);
          turningwheel[i].speedx=-turningwheel[i].speedx;
          turningwheel[i].speedy=-turningwheel[i].speedy;
          //     turningwheel[j].speedx=-turningwheel[j].speedx;
          //     turningwheel[j].speedy=-turningwheel[j].speedy;
        }
      }
    }
  }
}

As you can see, I commented the move change for the second objects out, because then it doesn't work. I guess because the collision is detected by BOTH wheels and the change in speed would be cancelled out.

          turningwheel[i].speedx=-turningwheel[i].speedx;
          turningwheel[i].speedy=-turningwheel[i].speedy;
          //     turningwheel[j].speedx=-turningwheel[j].speedx;
          //     turningwheel[j].speedy=-turningwheel[j].speedy;

I don't understand why some collisions still happen.

Answers

  • edited February 2016 Answer ✓

    I suspect the detect() function detects too much.
    With:

    crash++;
    println(crash);
    

    you can see that collisions are too many.
    You don't need a double loop to check the collisions.
    See: https://processing.org/examples/bouncybubbles.html

  • edited February 2016

    Thanks a lot!

    I used part of that code, but with the dist() function. I understand, by counting with I and J, I had a lot of double counts. So by start counting from for (int i = id + 1; i < numBalls; i++) you avoid all counts you already had from the previous objects. Now I also pass the count of the current wheel in the class like in the example (int idin in the example, dcount in my sketch).

    It works, but it's still not correct. There are less collisions going unnoticed, but there are more collisions ending in both objects "vibrating" or sticking together. I suspect some kind of unwanted loop, but I don't understand where and why...

    Is it OK to refer to the open.processing.org sketch? TurningWheels

    Or better to paste the code here?

  • edited February 2016 Answer ✓

    this is causing the stutter

    if (xpos > (width-42) || xpos < 42) {
          speedx=speedx * -0.95;
        }
    

    here a ball is in the return zone where xpos > (width-42) applies and after the change of speed he is still in the return zone and so changes speed again... never ending... stutter...

    instead say

    if (xpos > (width-42)) { // right border!!!!
         speedx=abs(speedx) * -0.95; // always a negative result, no stutter 
    }
    
    
    if (xpos < 42) { // LEFT border!!!!
         speedx=abs(speedx) * 0.95; // always a POSITIVE result, no stutter 
    }
    

    so even when he is still in the return zone, speedx gets the same correct value and thus the ball can safely leave the return zone

  • Answer ✓

    repeat for upper and lower border and between circles

  • here is in the return zone where xpos > (width-42) applies and after the change of speed he is still in the return zone and so changes speed again... never ending

    xpos > (width-42) = xpos > 758 (so xpos has to be 759, or could that be more? as soon its 759, this condition is met). speedx=-speedx, since the wheel was floating to the right, speedx HAD to be positive. So the new speedx HAS to be negative. And xpos+=speedx, so xpos gets smaller, l should think like from 759 to 758. Which isn't greater then 758, so the condition is not met and the wheel can happily float to the left?

    I understand, since I used -0.95 it's a little different. First the wheel goes to the right by +1, then it's bounced back by -0.95. So you have a positive 758,05. Then the problem would also be solved by letting the wheel bouncing back by -1. Right?

    I did the changes you recommended though (in Processing, at home, now I'm at work). I'll post them later this evening. But I'm struggling with applying you tip to the collision of wheels.

    repeat for upper and lower border and between circles

    (Since the openprocessing version isn't the latest, I can't continu now... Should have updated last night :-))

  • ??

    well your math is faulty.

    e.g. speed is random up to 4, so xpos can be 762 almost

    so of course it can stay in the reverse zone

    trust me, I did this so many times

    just try it

    with the wheels you could add a value to move out of the danger zone

  • Right! Of course! And I see I even stated "random(-4, 4)" which is stupid because a negative speed is like an other direction, while I already use a random direction. That can only confuse things. I make it zero to 4.

    I trust you ;-) when I ask those questions, it's not to question YOU, but to understand it MYSELF (or to check if I understood right).

    Like I said, at home I have my sketch already changed following your directions, only the statement of the collision-condition is a problem for now.

    I try working on it later, but since today I worked from 9:30 till 22:30, it's possible I'll have to postpone it until tomorrow...

    Thank for your input already!!

  • thanks

    I don't understand what is bad about a negative speed

    it brings in more diversity and balls can go left as well...?

    but I haven't tried your sketch yet

  • Since hoek = random(0, 360), balls go left when "hoek" is 180°.

  • Answer ✓

    I see

  • I don't understand... I used the abs() for the collision as well. I tried different combination, but now the wheels all float in a certain direction in stead of random. Direction (hoek) stays random, speed is reversed, how can they all float together???

    float r = 40, radhoek; 
    int arm=4;
    float[] xpos = new float[20];
    float[] ypos = new float[20];
    int numberwheels = 16;
    TurningWheel[] turningwheel = new TurningWheel[numberwheels];
    
    void setup() {
      size(800, 800);
      for (int count=0; count <numberwheels; count++) {
        turningwheel[count] = new TurningWheel(random(42, 758), random(42, 758), count);
      }
    }
    
    void draw() {
      background(230, 180, 160);
      for (int count=0; count <numberwheels; count=count+1) {
        turningwheel[count].display();
        turningwheel[count].move();
       turningwheel[count].detect();
      }
    }
    
    void keyPressed() {
      if (key == 'a') {
        arm++;
      } else if (key == 'q') {
        arm--;
      }
      arm = constrain(arm, 1, 16);
    }
    
    class TurningWheel {
      float xpos, ypos, hoek = random(0, 360);
      float speedx = random(0, 4);
      float speedy = random(0, 4);
      int dcount;
      color kolor = color(random(150, 255), random(150, 255), random(150, 255));
    
      TurningWheel (float tempxpos, float tempypos, int tempcount) {
        xpos = tempxpos;
        ypos = tempypos;
        dcount = tempcount;
      }
    
      void display() {
        radhoek=radians(hoek);
        for (int i = 0; i < arm; i++) {
          float[] xarmen = new float[arm];
          float[] yarmen = new float[arm];  
          xarmen[i] = xpos + r * cos(radhoek+radians(i*360/arm));
          yarmen[i] = ypos + r * sin(radhoek+radians(i*360/arm));
          line(xpos, ypos, xarmen[i], yarmen[i]);
          fill(kolor);
          ellipse(xarmen[i], yarmen[i], 6, 6);
        }
        ellipse(xpos, ypos, 20, 20);
      }
    
      void move() {
        if (hoek>360) {
          hoek=1;
        }
        hoek+=2;
    
        if (xpos > (width-42)) {
          speedx=abs(speedx) * -0.95;
        }
        if (xpos < 42) {
          speedx=abs(speedx) * 0.95;
        }
        if (ypos > (height-42)) {
          speedy=abs(speedy) * -0.95;
        }
        if (ypos < 42) {
          speedy=abs(speedy) * 0.95;
        }
    
        xpos+=speedx;
        ypos+=speedy;
      }
    
      void detect() {
        for (int i = dcount + 1; i<numberwheels; i++) {
    
          if ((dist(xpos, ypos, turningwheel[i].xpos, turningwheel[i].ypos)) < 84) {
            speedx=abs(speedx) * -1;
            speedy=abs(speedy) * -1;
            turningwheel[i].speedx=abs(turningwheel[i].speedx) * -1;
            turningwheel[i].speedy=abs(turningwheel[i].speedy) * -1;
          }
        }
      }
    }
    
  • Answer ✓

    Abs(x) * -1 is always negative...

  • Answer ✓

    That said, you can't treat collision between circles the same way you handle collision with a wall, it just doesn't work that way. The wall collisions are as simple as they are because the walls are perfectly vertical or horizontal, a collision between two balls can happen at any angle. You also have to take into account the different speeds of the incoming balls (and their masses, but I assume all yours are the same weight).

    I'll try and google up an example later (am on my phone)

  • edited February 2016 Answer ✓

    Just to check: this part here

        if (xpos > (width-42)) {
          speedx=abs(speedx) * -0.95;
        }
        if (xpos < 42) {
          speedx=abs(speedx) * 0.95;
        }
        if (ypos > (height-42)) {
          speedy=abs(speedy) * -0.95;
        }
        if (ypos < 42) {
          speedy=abs(speedy) * 0.95;
        }
    

    gave a huge improvement with the wall collisions, right?

    same without the inner ( ) btw

        if (xpos > width-42) {
          speedx=abs(speedx) * -0.95;
        }
        if (xpos < 42) {
          speedx=abs(speedx) * 0.95;
        }
        if (ypos > height-42) {
          speedy=abs(speedy) * -0.95;
        }
        if (ypos < 42) {
          speedy=abs(speedy) * 0.95;
        }
    
  • Answer ✓

    this looks like a decent runthrough of the maths:

    https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional

    given that m1 = m2 the equation simplifies down a lot. but does involve vector maths...

    (you should also consider just WHEN the balls collide but can probably get away with not doing this if you're updating fast enough)

  • edited February 2016 Answer ✓

    There is an example in section movement or so for this

    iirc on website

  • edited February 2016

    Indeed @Chrisir, I didn't mention it yet, because I was so busy getting everything to work, that I forget what was already going better (talking about the improved wall-bouncing)!
    I look up the example you mention, maybe it's the one @cameyo posted also: BouncyBubbles
    I don't know why the dist() function is not used, maybe it's made with/for an earlier version of Processing?

    float dx = others[i].x - x;
    float dy = others[i].y - y;
    float distance = sqrt(dx*dx + dy*dy);
    float minDist = others[i].diameter/2 + diameter/2;
    

    I'll try this collision-movement first, since it's already worked out (I mean in stead of trying to apply https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional)

  • Dist () isn't used because it calls sqrt() which is expensive. If you don't need to know the exact distance, just that it is smaller than a given value, then you can square both sides and skip the sqrt.

  • What they use in stead of dist() uses sqrt() as well. float distance = sqrt(dx*dx + dy*dy);

    I'm using the animation of two bouncing balls in your wikipedia reference to find out the calculus myself. Still puzzling :-) I let you know what I found!

    (the bouncing balls have gravity and different parameters then I use).

  • Answer ✓
    boolean isColliding(TurningWheel tw) {
      return sq(x - tw.x) + sq(y - tw.y) < sq(r + tw.r);
    }
    
  • Answer ✓

    (oops, i missed the dist(), was reading on the phone again.)

    Yes, if you don't need the value of distance then that is better. They've reimplemented dist() themselves which is pointless and inefficient here.

  • I thought of an easier solution. I don't need the collisions to be scientific correct. The reason I made this sketch, is just to practice Processing. I prefer to switch to a new sketch and learn different things. There is so much more to discover :-).

    This is what I came up with: Processing

    If Wheel A is above_ Wheel B_, the value of x keeps going up or down, so if x is negative it stays negative, if it's positive it stays positive. speedx stays equal. Y changes: in stead of going up it goes down, or the other way around. speedy reverses polarity: speedy = -speedy. Something similar goes for when A is left of B.

    I don't care the angle of the rebound movement is right or wrong. There's some kind of bounce, which for the simplicity of this "project" is enough.

    Curious about your thoughts about this work-a-round!

  • edited February 2016 Answer ✓

    absolutely not the quality of experience we expect from a skilled coder like you are

    ;-)

    honestly, look at the bouncy bubbles examples and apply it to your code. The layout of the variables is not so different from yours.

    it's a good learning steps to grasp the angle stuff.

  • a skilled coder like me :-D thank you...

    Okay... I'll do the extra mile, I'll try to use the Bouncing Ball method...

  • edited February 2016

    Voilà: I did it :-)

    Turning Wheels

    When I heighten the speed to max 6, the wheels start shaking, I guess because that's to much calculations.

    @koogs said : > (you should also consider just WHEN the balls collide but can probably get away with not doing this if you're updating fast enough)

    I understand why this could be necessary, but on the other hand: when your in the detect-loop, [i] is always the same, so the x an y positions are from the same moment when you do the calculations, or not? Or could xpos and ypos be from a later frame, then turningwheel[i].xpos and turningwheel[i].ypos?

  • @koogs : could you explain? So I learn for future projects... (I'm talking about the timeline thing).

    Would be appreciated!

Sign In or Register to comment.