Box2D Forums

It is currently Fri Nov 28, 2014 12:01 pm

All times are UTC - 8 hours [ DST ]




Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Sun Dec 25, 2011 3:42 pm 
Offline

Joined: Sun Dec 25, 2011 3:41 pm
Posts: 18
I'm trying to make sticky physics objects, so that when they are thrown at a wall (staticbody), they will stay on that wall and start to slide down along it.

First, I need to know from which direction the wall is touching the physics object. Then I will apply a force (towards the wall) to the object so that it will stay against the wall and won't bounce off.

To get the direction, I need to know where the contact point or touch point is.
Also, I need the normal of the contact surface so that with enough force, and with a correct direction of the pull, the object can be pulled from the wall.


Top
 Profile  
 
PostPosted: Sun Dec 25, 2011 11:07 pm 
Offline

Joined: Tue Jun 24, 2008 8:25 pm
Posts: 1970
Location: Tokyo
You can get the contact points and the normal from the 'world manifold'.
In your contact listener you can get this like (C++):
Code:
b2WorldManifold worldManifold;
contact->GetWorldManifold(&worldManifold);
Also check section "4.11 Contact Manifolds" in the manual.


Top
 Profile  
 
PostPosted: Mon Dec 26, 2011 4:03 am 
Offline

Joined: Sun Dec 25, 2011 3:41 pm
Posts: 18
Could you give me a more complete example code?
I posted this question into the Java subforum so that I could get the answer in Java, but I'm fine with C++ as long as it's a more complete example tutorial.

The Java version of Box2D doesn't have any API from which I could read where the 'contact' and 'worldManifold' are taken from.


Top
 Profile  
 
PostPosted: Mon Dec 26, 2011 5:09 am 
Offline

Joined: Tue Jun 24, 2008 8:25 pm
Posts: 1970
Location: Tokyo
'contact' is available in all of the contact listener functions: BeginContact, EndContact, PreSolve, PostSolve.
'worldManifold' is just a temporary local variable.

Code:
class MyContactListener : public b2ContactListenervoid
{

  BeginContact(b2Contact* contact)
  {
    b2WorldManifold worldManifold;
    contact->GetWorldManifold(&worldManifold);

    //now you can use these properties for whatever you need
    worldManifold.normal //b2Vec2
    worldManifold.points[i] //b2Vec2[2]
 
  }

}


Check out the definition of b2WorldManfold too:
http://code.google.com/p/box2d/source/b ... ollision.h


Top
 Profile  
 
PostPosted: Mon Dec 26, 2011 10:42 am 
Offline

Joined: Sun Dec 25, 2011 3:41 pm
Posts: 18
Thanks!
I think I got it working.

So, as the world is being initialized, it gets a ContactListener.

Code:
PlayerContactListener p = new PlayerContactListener();
jboxWorld.setContactListener(p);


and the ContactListener looks like this:
Code:
public class PlayerContactListener implements ContactListener {

   @Override
   public void beginContact(Contact contact) {
      WorldManifold worldManifold = new WorldManifold();
       contact.getWorldManifold(worldManifold);
      
      //print the contact point and the normal
      System.out.println(worldManifold.points[0].x + "," + worldManifold.points[0].y);
      System.out.println(worldManifold.normal.x + "," + worldManifold.normal.y);
   }
...
...


I noticed that I don't have to enable the ContactListener anywhere. I only had to set the listener for the world and it started to check for contacts in the game.

-

Now I need to limit the loop so that only certain bodies are being checked. I need to know this information (points and normals) only from one physics object, the "player" object.

Also, I saw that the normal is only one Vector2. So it holds only 1 x and 1 y value. The problem is that the listener doesn't tell from which object those values are measured, the A object or the B object.

Also, I need to do massive checks for the player object (as I need to apply forces and limit its movement as the object is against a wall), so is it wise to do those checks inside the ContactListener? Can it even be done anywhere else?

If you have any tips on those issues please reply here. I will update my progress here and paste some code, so that this thread will be more helpful for other people in the future.


Top
 Profile  
 
PostPosted: Mon Dec 26, 2011 12:49 pm 
Offline

Joined: Tue Jun 24, 2008 8:25 pm
Posts: 1970
Location: Tokyo
'Limiting the loop' is done by you in the contact listener functions - just check if the things in the contact are something you want to deal with (eg. check if they are the right fixture, body, user data etc, whatever it is you need) and exit as early as possible if they are not. You can get fixtures from the contact, and then bodies from the fixtures.

Check the link I gave above and look for the definition of b2WorldManifold, you'll see that the normal points from A to B.

It sounds like you will need to do the checks in the contact listener, specifically in PreSolve if you want to adjust your 'pressing into wall' force continuously while the body is stuck on the wall.

I think you are well on the way to getting this down already, but fwiw you might possibly find it useful to keep an eye on this page in the next few days too: http://www.iforce2d.net/b2dtut/collision-anatomy


Top
 Profile  
 
PostPosted: Tue Dec 27, 2011 12:26 pm 
Offline

Joined: Sun Dec 25, 2011 3:41 pm
Posts: 18
I can't get the userdata from the bodies when I call them inside the ContactListener.

I created a physics object and gave it some userData:
Quote:
Body<PhysObjectData> player = new DynamicBody(new Circle(0.1f , 0.1f , 0.2f , 1.0f), 4, 1);
PhysObjectData playerData = new PhysObjectData(1);
player.setUserData(playerData);
world.add(player);


And inside the ContactListener I have:
Quote:
if (contact.getFixtureA().m_body.getUserData() == null) {
System.out.println("m_body from FixtureA has no userdata");
}

if (contact.getFixtureA().getUserData() == null) {
System.out.println("FixtureA has no userdata");
}
if (contact.getFixtureA().getBody().getUserData() == null) {
System.out.println("Body from FixtureA has no userdata");
}


As you can see I found three different ways I can call the body from the fixture, which is quite weird.
All of those return null. No userdata found.

I debugged it a little so that I know that the userdata really went into one of the physics objects, but for some reason the ContactListener can't find it.

I'm a bit sleepy. Did I miss something?


Top
 Profile  
 
PostPosted: Tue Dec 27, 2011 1:30 pm 
Offline

Joined: Tue Jun 24, 2008 8:25 pm
Posts: 1970
Location: Tokyo
Fixtures and bodies can each have user data, so only the first and third of those methods are the same.
You should also check fixtureB for user data, as well as fixtureA.

I have not used the Java port but it seems very odd that you can just create a body as you please like: new DynamicBody(...)
That implies that you can make a body without even having a world at all, which is not how it usually works.
The world is supposed to be in charge of the creation of bodies so (at least in C++) it is world->CreateBody(...) then the world takes care of managing all the setup that needs to be done.
Then again, maybe that's how the Java version works - I'm just mentioning it in case it helps.


Top
 Profile  
 
PostPosted: Wed Dec 28, 2011 5:38 am 
Offline

Joined: Sun Dec 25, 2011 3:41 pm
Posts: 18
Ok, the problem appeared to be in Fizzy (a wrapper around JBox2D).
It creates the bodies differently than JBox2D originally would, and didn't give the userData properly to the jboxBody.
So, I made small changes to Fizzy's code.
Code:
public void setUserData(T object) {
   this.jboxBodyDef.userData = (object); //new line
   //this.jboxBody.setUserData(object); //new line
   this.userData = object; //userData goes to the Fizzy body
   }

For some reason I can't give the userData directly to the jboxBody, but rather the jboxBodyDef. Maybe I should read the manual to see what the differences are. :oops:

Now, back to coding... ->

EDIT:
Got it working, I reached my goal Image
Now my player object gets stuck to walls.

Thanks for your help!

Here's the applyForce part inside the preSolve method. It's not the best implementation but the idea is simple.
Code:
Vec2 fv1 = new Vec2(-worldManifold.normal.x*0.01f,-worldManifold.normal.y*0.01f);
contact.getFixtureB().getBody().applyForce( fv1, contact.getFixtureB().getBody().getWorldCenter());


And ofc before that we check that it's the right object.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 8 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group