I'm using libgdx and its physic engine Box2d. My question is : how could I make my box2d body go through a block , ignore the first collison than make the block active so the ball can fall onto it. A good example is doodle jump.
4 Answers
Set a flag whenever collisions are detected.
Something like this:
method Ball.onCollisionWith(block) {
if (block == self.dontCollide) {
self.dontCollide = block
-- Do nothing else, passing through collision
} else {
-- Reset flag so next collision will pass again
self.dontCollide = nil
resolveCollision(self, block)
}
}
The Ball
will then ignore every second collision due to its dontCollide
flag being set.
-
\$\begingroup\$ If I'm not wrong this is C or C++, programming languages that I don't know and I don't have any chance to translate it into java/libgdx code :( could you please translate it ? \$\endgroup\$ Commented Mar 12, 2013 at 14:19
-
1\$\begingroup\$ It was only really pseudocode to begin with -- really just meant to illustrate a solution. I've converted it to look more like C though. Programming is a prerequisite for game programming. You should read an an introductory programming book. \$\endgroup\$– AnkoCommented Mar 12, 2013 at 17:30
I've had a similar problem in the past, have a look at this Tutorial. I found it answered my question. Its not for java but the code was very easy to copy over
I finally made it :)
Here's my code :
public void preSolve(Contact contact, Manifold oldManifold) {
fixtureA = contact.getFixtureA();
fixtureB = contact.getFixtureB();
if(fixtureA.getBody().getUserData() == "platform" && fixtureB.getBody().getUserData() == "ball"|| fixtureA.getBody().getUserData() == "ball" && fixtureB.getBody().getUserData() == "platform"){
if (fixtureA.getBody().getUserData() == "platform") {
platform_y = fixtureA.getBody().getPosition().y;
ball_y = fixtureB.getBody().getPosition().y;
} else if(fixtureA.getBody().getUserData() == "ball") {
ball_y = fixtureA.getBody().getPosition().y;
platform_y = fixtureB.getBody().getPosition().y;
}
if(ball_y < platform_y + 1.5F) { //the ball is below
contact.setEnabled(false);
}
}
}
-
\$\begingroup\$ I've added my solution below, based on yours but using Category bits to define the objects which can collide etc. Hope it helps \$\endgroup\$ Commented Aug 20, 2016 at 7:55
I used your solution but instead of using getUserData I set up category bits on my platforms and player. The ground has different category bits to platforms so that only the platforms can be passed through. You can define category bits in your main game class as follows:
public static final short GROUND_BIT = 1;
public static final short PLATFORM_BIT = 2;
public static final short PLAYER_BIT = 4;
Next, in your Presolve we only want to pass through platforms, so we add this code to check if Fixture A and B are either the Player or Platforms:
@Override
public void preSolve(Contact contact, Manifold oldManifold) {
//THIS SHOULD ALLOW PLAYER TO PASS THROUGH PLATFORMS AND COLLIDE ON THE WAY DOWN
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
float platform_y;
float player_y;
platform_y = fixtureA.getBody().getPosition().y;
player_y = fixtureB.getBody().getPosition().y;
if(fixtureA.getFilterData().categoryBits == Engine.PLAYER_BIT
&& fixtureB.getFilterData().categoryBits == Engine.PLATFORM_BIT ||
fixtureA.getFilterData().categoryBits == Engine.PLATFORM_BIT
&& fixtureB.getFilterData().categoryBits == Engine.PLAYER_BIT ) {
//Gdx.app.log("Player Y ", "" + player_y);
if (fixtureA.getBody().getUserData() == "platform") {
platform_y = fixtureA.getBody().getPosition().y;
player_y = fixtureB.getBody().getPosition().y;
} else if(fixtureA.getBody().getUserData() == "player") {
player_y = fixtureA.getBody().getPosition().y;
platform_y = fixtureB.getBody().getPosition().y;
}
if(player_y < platform_y + 0.22f) { //the player is below platform
contact.setEnabled(false);
} else {
contact.setEnabled(true);
}
}
}
When I build my level the Platforms use this loop to build them from a Tiled map, and set the Category bits to the fixtures:
//////////////////////////////////////////////////////////////////////////
//Platform Layers - with category bit PLATFORM_BIT
for(MapObject object : map.getLayers().get(2).getObjects().getByType(RectangleMapObject.class)) {
Rectangle rect = ((RectangleMapObject) object).getRectangle();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set((rect.getX() + rect.getWidth() / 2) / Engine.PPM, (rect.getY() + rect.getHeight() / 2) / Engine.PPM);
body = world.createBody(bdef);
shape.setAsBox(rect.getWidth()/2 / Engine.PPM, rect.getHeight()/2 / Engine.PPM);
fdef.shape = shape;
//Set the category Bits to Platform
fdef.filter.categoryBits = Engine.PLATFORM_BIT;
body.createFixture(fdef);
}
My Player also has category bits set and the maskBits define what he can collide with:
fdef.filter.categoryBits = Engine.PLAYER_BIT;
fdef.filter.maskBits = Engine.GROUND_BIT | Engine.PLATFORM_BIT;
This should be all you need to get it working.
The only issue is trying to work out what the value here should be:
if(player_y < platform_y + 0.22f) { //the player below
The 0.22f in my case makes sure tht the player gets a certain height above the platform before checking. If you set this too high or too low, the player may not land on the platform at all. I have not worked out what the .22f is in relation to my world coordinates or sprite size yet, but will update if I can replace this with a value derived from the Player's height for example.
Hope this helps a little.
GIT