Box2D: Side by side ground tiles, flat ground, yet blocking at small speed.

Forums Programming physics: Box2d, Chipmunk et al Box2D: Side by side ground tiles, flat ground, yet blocking at small speed.

This topic contains 17 replies, has 7 voices, and was last updated by  dercetech 1 year, 11 months ago.

Viewing 18 posts - 1 through 18 (of 18 total)
Author Posts
Author Posts
April 29, 2012 at 11:52 am #241070

dercetech
Participant
@dercetech

Hi!

I’m facing this issue with Box2D; It can be easily reproduced and downloaded here:

http://www.dercetech.com/data/bulk/boxd-issue.zip

See the picture below:

A ground is made of blocks placed side-by-side. These are at the exact same level, forming a perfectly flat surface. Let’s call them left block and right block.

The tall grey rectangle is the player’s physics shape. Its rightmost boundary is very near the beginning of the right block.

Use the right key to make the “player” move rightwards. Every other time, the player will simple stop as it encounters the leftmost edge of the second block.

Now, restart the game. Get a running start by first moving leftwards before attempting this again. Enough momentum never causes this glitch to happen.

This bug is quite unlikely to happen, however, in a platform game with level made of hundreds of small tiles, this bug is likely to affect the gameplay.

How could we solve this?

Thank you very much for your time!

April 29, 2012 at 12:12 pm #374868

Eko Mirhard
Participant
@whateva

I haven’t really test the code, but I managed to read several important parts. I saw that you build the ground as 2 separate bodies for each block. Can you try to actually use only one body, with both left and right blocks as its fixtures, and see if it still happen?

April 29, 2012 at 12:58 pm #374869

Jeff
Participant
@jeff

The body getting stuck happens all the time and is not just your code. I would do as @Eko said and make it one block. It will not get stuck this way. I think why it gets stuck this way, is actually because of how it detects physics. It has a skin type of thing for each body:

http://www.box2d.org/manual_files/image007.png

So I think it sticks out a little. So, you can either make the body bigger or make the two bodies closer together(in that they are overlapping)

April 29, 2012 at 1:13 pm #374870

abitofcode
Moderator
@abitofcode

An alternative approach is to try cutting the two corners off the bottom of your top block so the edges don’t catch as easily.

April 29, 2012 at 1:27 pm #374871

dercetech
Participant
@dercetech

@Eko hey thanks for the suggestion. Indeed, separate bodies they are – My actual implementation creates one body per tile it loads from a level file.

-> Is there some sort of built-in method to “merge” bodies?

@Jeff Hmm interesting. That’s more constructive than my calling it a bug ;) Overlapping doesn’t help :(

@abitofcode Hmm like rounded rectangles? How would one accomplish that?

Thanks :-)

April 29, 2012 at 1:38 pm #374872

itlgames
Moderator
@itlgames

This is a well know Bod2d issue, the best thing is to create your ground boxes as edges instead, that will solve the problem. I tried the rounding corners and whilst it works it gave me some collateral issues.

April 29, 2012 at 1:42 pm #374873

itlgames
Moderator
@itlgames
// Define the box shape as edges to avoid player to get stuck
b2Vec2 lowerLeft = b2Vec2(0 - (size.width/PTM_RATIO), 0 - (size.height/PTM_RATIO));
b2Vec2 lowerRight = b2Vec2(size.width/PTM_RATIO, 0 - (size.height/PTM_RATIO));
b2Vec2 upperRight = b2Vec2(size.width/PTM_RATIO, size.height/PTM_RATIO);
b2Vec2 upperLeft = b2Vec2(0 - (size.width/PTM_RATIO), size.height/PTM_RATIO);

b2EdgeShape groundBox;

// bottom
groundBox.Set(lowerLeft, lowerRight);
body->CreateFixture(&groundBox,0);

// top
groundBox.Set(upperRight, upperLeft);
body->CreateFixture(&groundBox,0);

// left
groundBox.Set(upperLeft, lowerLeft);
body->CreateFixture(&groundBox,0);

// right
groundBox.Set(lowerRight, upperRight);
body->CreateFixture(&groundBox,0);

April 29, 2012 at 1:42 pm #374874

dercetech
Participant
@dercetech

@itigames Hey thanks for that input. Not a Box2D expert, I’ll go start learning about edges. Any good tutorial you would suggest? Thanks!

edit: Oh, just refreshed the page and see you posted a ready answer! Thanks again! Let me try this out :D

April 29, 2012 at 1:45 pm #374875

itlgames
Moderator
@itlgames

The only downside of the edges is that you will get two contacts when on top of the sides, the top and the left or right side, that may or may not affect your code, but if it does, what I did was to reduce slightly the left and right sides so they don’t touch the top edge.

April 29, 2012 at 1:48 pm #374876

itlgames
Moderator
@itlgames
// Define the box shape as edges to avoid player to get stuck
b2Vec2 lowerLeft = b2Vec2(0 - (size.width/PTM_RATIO), 0 - (size.height/PTM_RATIO));
b2Vec2 lowerRight = b2Vec2(size.width/PTM_RATIO, 0 - (size.height/PTM_RATIO));
b2Vec2 upperRight = b2Vec2(size.width/PTM_RATIO, size.height/PTM_RATIO);
b2Vec2 upperLeft = b2Vec2(0 - (size.width/PTM_RATIO), size.height/PTM_RATIO);

// To avoid platforms to trigger an end contact when on top (with sides)
// we create left and right edges a bit smaller so they don't touch the top edge
b2Vec2 nearUpperRight = b2Vec2(size.width/PTM_RATIO, (size.height/PTM_RATIO) - 0.1f);
b2Vec2 nearUpperLeft = b2Vec2(0 - (size.width/PTM_RATIO), (size.height/PTM_RATIO) - 0.1f);

b2EdgeShape groundBox;

// bottom
groundBox.Set(lowerLeft, lowerRight);
body->CreateFixture(&groundBox,0);

// top
groundBox.Set(upperRight, upperLeft);
body->CreateFixture(&groundBox,0);

// left
groundBox.Set(nearUpperLeft, lowerLeft);
body->CreateFixture(&groundBox,0);

// right
groundBox.Set(lowerRight, nearUpperRight);
body->CreateFixture(&groundBox,0);

April 29, 2012 at 7:12 pm #374877

Jeff
Participant
@jeff

@dercetech

Just in case you wanted to merge bodies as you said, you could make each body for the tile just a little wider than the tile. Then, you check if they are colliding. If there is a collision, you replace them with a body twice the size. After you do this checking, you can get all the bodies and shrink them back to scale. This method may be a little overkill though when you could just use @itlgames code. It depends on your preference.

April 30, 2012 at 2:04 am #374878

bryce
Participant
@bryce

Like @abitofcode suggested I was able to fix this problem by cutting the corners ever so slightly. It just slides over them now. Here is what I use to add the bottom edges to my player:

float32 edgeWidth = 0.05;  // Add chamfered corners to allow the body to slide over edges
float32 edgeHeight = 0.01;
b2Vec2 vertices[6];
vertices[0].Set(-width + edgeWidth, -height); // bottom
vertices[1].Set(width - edgeWidth, -height); // bottom-right edge start
vertices[2].Set(width, -height + edgeHeight); // bottom-right edge end
vertices[3].Set(width, height); // top-right
vertices[4].Set(-width, height); // top-left
vertices[5].Set(-width, -height + edgeHeight); // bottom-left edge
shape.Set(vertices, 6);

April 30, 2012 at 2:27 am #374879

iforce2d
Participant
@iforce2d

Placing simple b2EdgeShape fixtures next to each other will have the same problem.

Looks like you have it solved now, but for anyone who does try edge shapes, the important thing is to set up their ‘ghost vertices’ properly.

April 30, 2012 at 6:41 am #374880

itlgames
Moderator
@itlgames

@iforce2d using edges instead did solved the problem to me. Using rounded corners had the downside that sometimes the character makes a “small” jump (at least on my case)

May 1, 2012 at 5:43 pm #374881

dercetech
Participant
@dercetech

@jeff

yep that sounds like a good idea. Like, when the level is loaded, check for contiguous blocks of a certain category. Would be a clean implementation, but a bit tricky since I’ve not yet handled all the basics ;-)

@itigames

That looks great, I’ve modified my code to work like you suggested. One issue however, I can’t seem to affect the friction anymore using that technique. Maybe am I missing something

// your code end
groundBox.Set(lowerRight, nearUpperRight);
body->CreateFixture(&groundBox,0);

// Stuff I add
b2FixtureDef fixtureDef;

fixtureDef.shape = &groundBox;

fixtureDef.density = 1.0f;
fixtureDef.friction = 200.0f;

b2Fixture *bdy = body->CreateFixture(&fixtureDef);

See, I put a goofy “200″ density value. Still is as slippery as ice. I don’t think modifying the player’s friction is a clean way to go when walking on various surfaces ;-)

Am I missing something?

Thanks for all inputs :-)

May 1, 2012 at 6:29 pm #374882

Jeff
Participant
@jeff

For my implementation:

for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
CCSprite *nwactor = (CCSprite*)mag->GetUserData();
if (nwactor.tag == GROUND) {
for (b2ContactEdge* bc = b->GetContactList(); bc; bc = bc->next)
{
b2Contact *contact = bc->contact;
if (contact->IsTouching())
{
CCSprite *spriteA = (CCSprite*)contact->GetFixtureA()->GetUserData();
CCSprite *spriteB = (CCSprite*)contact->GetFixtureB()->GetUserData();
if (spriteB.tag == GOUND && spriteA.tag == GROUND) {
(Remove bc and replace with new one in between the two bodies with double the size)
}
}
}
}
}

May 1, 2012 at 7:08 pm #374883

dercetech
Participant
@dercetech

@Jeff

Woa interresting, thanks for sharing this. I’ll try this out right now! Thanks again

-J.

May 2, 2012 at 9:46 am #374884

dercetech
Participant
@dercetech

Ok, got the solution for Jayther (box2D) as per how to define vectrices friction:

Do the b2FixtureDef before all of that, like so:

Code:

b2EdgeShape groundBox;

b2FixtureDef fixtureDef;

fixtureDef.density = 1.0f;

fixtureDef.friction = 1.0f;

fixtureDef.shape = &groundBox;

// bottom

groundBox.Set(lowerLeft, lowerRight);

body->CreateFixture(&fixtureDef);

// top

groundBox.Set(upperRight, upperLeft);

body->CreateFixture(&fixtureDef);

//etc.

Creating a fixture without using a fixture definition (b2FixtureDef) and just using a shape is a shortcut to making a fixture (which is why you need to pass in a second parameter for the density). Using a fixture definition allows you to specify the properties for that fixture.

Also CreateFixture copies the information and does not store the pointer anywhere, so you can just keep using the b2FixtureDef instance multiple times to create multiple fixtures of the same properties.

And be sure the friction is no less than 0.0 or more than 1.0. That’s the coefficient of friction, which is between 0.0 and 1.0. 1.0 will be quite obvious.

Thanks all for your support! Got one nice step further, cheers!

Viewing 18 posts - 1 through 18 (of 18 total)

You must be logged in to reply to this topic.