box2d - why does this static body misbehave?

Hi,
I create letters inside circles in box2d and then I create a rectangle around the mouse and I am pushing the objects out of the screen (eventually this will be controlled with the outline of the actor). However, when I push the dynamic objects they don’t bounce on the mouse as they do when I am not moving the mouse. Their movement looks forced and not realistic.
Take a look at this 20 second screencast that demonstrates the bug.

Notice how when I push the item it does not roll/bounce away. I have to shove it. After a few seconds though, when an “ε” letter comes along and bounces off me it behaves nicely.
Any ideas?
This is how I create the box around the mouse:

ofPtr<ofxBox2dRect> rect = ofPtr<ofxBox2dRect>(new ofxBox2dRect);
rect.get()->setPhysics(0,0.2,0.2);
rect.get()->setup(box2d.getWorld(), center.x, center.y, 100, 100);
rects.push_back(rect);
rect.get()->body->SetType(b2_staticBody);

In other words, if I assume that the box is a racket and the letter is a ball. The ball bounces naturally only when the racket (box) is immobile. As soon as I move to meet the ball I drag it rather than make it bounce on the box like in a moving racket.

thanks marinero

Hi. Just to check. How do you move the box around? Are you directly assigning your mouse position to the box position?

yes. Is that wrong?
I do it in this line:

rect.get()->setup(box2d.getWorld(), center.x, center.y, 100, 100);

Is this not correct? The box is destroyed in the end of each update() and recreated, that’s why I need to call the “setup” function again and again.
(the reason I have to do it this way is because I have an arbitrary number of rects which I need to insert in the beginning of each update and then once they interact with my dynamic bodies they can disappear, I don’t need them any more).
marinero

Yes that is probably your problem. You have to think of the box2d world as a closed system where everything is working together. Now if you come from the outside and force a block to not behave the way the system expects it to, then stuff can break.

A better way to this, is using a joint. On one side you connect your mouse and on the other your object. That way the object is attached to your mouse but still behaves according to the system’s rules. See this page for a rudimentary example: http://www.binarytides.com/mouse-joint-box2d-javascript/

And I think shiffman talks about this here: https://vimeo.com/61114510

thanks @underdoeg for replying so fast and with great references.

You are probably right that I am messing up with the box2d world by forcing the objects to move to a specified direction. The reason I am doing it is because I am using the outline of an actor (see this image) to interact with objects. The problem is that I don’t want to use the outline because objects pass right through it. I then tried to use the points on the outline to create triangles which I will insert as static bodies in my box2dworld but something goes wrong there (as I posted in the forum about a month ago. see here). So I thought of cheating by creating very thin and long rectangles that connect one point to the other, effectively creating a thicker edge so that objects don’t pass through it if the actor moves to fast.

And this is where our paths crossed :).
See this image below. The red dots indicate the points on he outline and I connect them by creating very thin rectangles.

I know it’s a bit of a hack but it’s because I could not get the triangulation working (see link above). The problem is now that the points on the triangle are not always the same they vary (50-80 points) and as a result I need to destroy the rects and recreate them each time. In order to figure out where the rects above should be placed I go through the points in the outline, calculate the middle point between them and their relative angle and apply both to the corresponding rect.

I tried the solution that you suggested with the anchor, but I had no luck. I created a point (anchor) and then I attached another “ball” to it (using the joints example of box2d), but it is not ON the mouse. It is hanging by an inelastic joint string which even though I specified should be of length zero it is forcing the ball to be at least a radius away.

At this point, the problem is simple:
If I wanted to play a game of computer tennis how would I implement the racket-ball interaction in box2d?

Sorry for the long email. Really looking forward to your answer. Got a show on Monday (rehearsals tomorrow) and really desparate to get the interaction between user controlled & box2d objects working properly.

marinero

Hi @marinero , did you resolve?
I think i have similar problem. I have a shape of an actor and he continue to move. I need to use some physic interaction with others object. The problem is that i don’t know how to get the force of the actor’s movement. For example, if the actor want to hit with his hand some object, how get the force of his movement? As you wrote, I re-draw my actor in box2D world for each frame, so, i really don’t know how get force of movement. I want to try to use optical flow and doing something like this:

  • Actor moves;
  • Actor’s hand (or other body part) hits with some objects;
  • Get the value of optical flow on hit position;
  • Calculate the force and add it to my object in box2D;

Do you think the optical flow way could work or you have found something better?
I’m little bit afraid about get optical flow of many collision.

1 Like

@Mauro,
sorry for late reply. Hope I can still help you.
This has been one of the most tricky bits i have encountered. I haven’t as yet found something that solves the problem completely. That is, a solution that makes the object behave like a real life object hiting a moving target. What I did in order to improve the situation is this:

A. I resampled the outline of the actor (so that the points are equally spaced an the smaller objects don’t manage to pass between outline points). I did this by running the getResampledBtCount() command

resampledOutline = actorStruct[0].outline.getResampledByCount(80);

B. For every point in the outline I created a ofxBox2dCircle object. This ensures that other objects don’t pass through. If it is left as an outline, as you might have noticed things move easily through it. I did this by runnig this code:

for (int i=0; i<resampledOutline.size(); i++)
{
     ofPtr<ofxBox2dCircle> circle = ofPtr<ofxBox2dCircle>(new ofxBox2dCircle);
     circle.get()->setPhysics(0, 0.7, 0.2);
     circle.get()->setup(box2d.getWorld(), resampledOutline.getVertices()[i].x, resampledOutline.getVertices()[i].y, 1);
     circles.push_back(circle);
}

All of this will work beautifully as you can see in this video of a past work of ours. There is however one caviat. You can’t really have realistic bouncing when the actor is moving. The bouncing works correctly when the actor is static but when he moves the object is dragged and does not bounce. What we did is that we instructed our dancer to stop moving when she saw the object coming so that it can bounce correctly on her. For the other stuff we did not mind the dragging. But in order for the dragging to work correctly you need to do the stuff I suggested.

Hope it helps. Did you find another/better solution? Let me know and less discuss.

marinero

Ehy @marinero thanks for the tips! I seen your video and it’s great! It seems to work without problem.

I found just now the time for try your code and i try to write a fast implementation for add some more realistic force when actor punch an object.

Starting from your code, i have found this solution that seems not too bad. I tested just using some puppets i made with paper, but in the next days i hope i can try with a real actor or anyway, with a more realistic setup.

Anyway, the logic i implemented is simple. I create a vector of ofVec2f and i call it “forces”.
Inside every frame i calculate the force of movement of the 80 circles that i create from the resampled outline.
I added also a collision listener to box2d, so, when one of the circle of the resampled outline hit with some others circle, i take the right force from “forces” vector and i apply to the circle. That’s it. I tried and seems not bad… but i have a big doubt that everytime i update the position of resampled circles, the order how they are created it changed and this create a wrong calculation of force. I’m not sure and i have to test this better… but this could be a problem.

If this way work, i just need to know how take the collision position, because now i’m applyng the force to the object, but always at the center of the object that collide. Do you know how to take the right position of collision?

When i found time i want to try with optical flow. I just want to try to get the optical flow data of the point of resampled circle that collide with another circle. Take this info and add the force… it could works.

I still have some doubt about the object that pass through my character’s shape. If i draw a big circles from resampled outline, of course it’s more hard that object pass through my character, but they bounce far from the right shape. One solution could be draw the circles a little bit inside of my character’s shape.

Anyway… there are all ideas in my mind. I think i test better in the next days.

Here some most important parts of the code:

void ofApp::initBox2D()
{
  box2d.init();
  box2d.setGravity(0, 30);
  box2d.createGround();
  box2d.setFPS(30.0);
  box2d.enableEvents();
  ofAddListener(box2d.contactStartEvents, this, &ofApp::contactStart);
  
  for(int a = 0; a < 80; a++)
  {
    ofPtr<ofxBox2dCircle> c = ofPtr<ofxBox2dCircle>(new ofxBox2dCircle);
    c.get()->setPhysics(0, 0.7, 0.2);
    c.get()->setup(box2d.getWorld(), 0, 0, 5);
    characterCircles.push_back(c);
    forces.push_back(ofVec2f(0,0));
    c.get()->setData(new CustomData());
    CustomData * sd = (CustomData*)c.get()->getData();
    sd->speedId = a;
    sd->type = 1;
  }
  yellowCharacter = ofPtr<ofxBox2dEdge>(new ofxBox2dEdge);
}

void ofApp::contactStart(ofxBox2dContactArgs &e)
{
  if(e.a != NULL && e.b != NULL)
  {
     CustomData * data1 = (CustomData*)e.a->GetBody()->GetUserData();
     CustomData * data2 = (CustomData*)e.b->GetBody()->GetUserData();
    
    if((e.a->GetType() == b2Shape::e_circle && e.b->GetType() == b2Shape::e_circle))
      {
        if(data1->type == 1)
        {
          e.b->GetBody()->ApplyForceToCenter(b2Vec2(forces[data1->speedId].x * 10, forces[data1->speedId].y * 10), true);
        }
        if(data2->type == 1)
        {
          e.a->GetBody()->ApplyForceToCenter(b2Vec2(forces[data2->speedId].x * 10, forces[data2->speedId].y * 10), true);
        }
    }
  }
}

void ofApp::updateYellowCharacter(){
  if(blobTracker1.contourFinder.size() > 0)
  {
    ofPolyline polyLine = blobTracker1.contourFinder.getPolylines()[0];
    yellowCharacter.get()->clear();
    yellowCharacter.get()->addVertexes(polyLine);
    yellowCharacter.get()->create(box2d.getWorld());
    ofPolyline resempled;
    resempled = yellowCharacter.get()->getResampledByCount(80);
    for(int a = 0; a < resempled.size(); a++)
    {
      ofVec2f pos = characterCircles[a].get()->getPosition();
      forces[a] = ofVec2f(pos-resempled.getVertices()[a]);
      characterCircles[a].get()->setPosition(resempled.getVertices()[a]);
    }
  }
}


void ofApp::createCircle()
{
  ofPtr<ofxBox2dCircle> c = ofPtr<ofxBox2dCircle>(new ofxBox2dCircle);
  c.get()->setPhysics(1, 0.5, 0.5);
  c.get()->setup(box2d.getWorld(), mouseX, mouseY, 10);
  
  c.get()->setData(new CustomData());
  CustomData * sd = (CustomData*)c.get()->getData();
  sd->speedId = 666;
  sd->type = 0;
  circles.push_back(c);
}

Sorry for my english, but i hope you could understand. :slight_smile: