I have a box2d body which is simply a rectangle (the spaceship).
This ship flies around, affected by the gravity of planets (static circle bodies) just fine.
I have tried changing the ship fixture shape to both (1) a triangle fixture and also (2) two rectangular fixtures put together. Both of these body configurations are causing problems.
Given the change in volume, to make up for the difference in mass I calculated the necessary new densities so that these bodies have the same mass as the original rectangle body. However when launching the ship with the same linear impulse as before, the triangular body and multi-rectangular body both act differently than the original body and shoot forward much faster.
I have tried further tweaking the ship density as well as the linear impulse and also the planet gravity to return the ship to a normal pace of movement,
however when doing that the ship starts to act weird and will do things like suddenly switch direction in mid-air even though there's no collision. This collision-looking event does not happen when the density/gravity/linear impulse are all the same as before, however in that situation the ship is moving too fast for my needs.
Basically I don't know why these bodies are acting differently when there is no change in mass or anything besides shape and size. There's no linear damping or collisions happening. I don't know what else could possibly be affecting the ship.
Any help or ideas is greatly appreciated.
Here is the code that I believe is relevant:
public void createShipBody() {
density = 0.05f; //normal rectangle
//density = 0.06666667f; //density for the two rectangles together
restitution = 1.0f;
bodyDef.type = BodyDef.BodyType.DynamicBody;
setBodyDefPosition(bodyDef);
body = GameCore.world.createBody(bodyDef);
setShipShapeAndFixture();
}
// For with the original rectangle
private void setShipShapeAndFixture() {
PolygonShape shape = new PolygonShape();
shape.setAsBox(Utility.pixelsToMeters(bodyWidth / 2), Utility.pixelsToMeters(bodyHeight / 2));
createFixtureDef(shape);
shape.dispose();
}
// For with triangle fixture
private void setShipShapeAndFixture() {
Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(0, 0);
vertices[1] = new Vector2(Utility.pixelsToMeters(bodyWidth), 0);
vertices[2] = new Vector2(Utility.pixelsToMeters(bodyWidth/2), Utility.pixelsToMeters(bodyHeight));
shape.set(vertices);
createFixtureDef(shape);
shape.dispose();
}
// For multi-rectangle body
private void setShipShapeAndFixture() {
PolygonShape shape = new PolygonShape();
shape.setAsBox(Utility.pixelsToMeters(bodyWidth / 2), Utility.pixelsToMeters(bodyHeight / 4));
PolygonShape shape2 = new PolygonShape();
shape2.setAsBox(Utility.pixelsToMeters(bodyWidth / 4), Utility.pixelsToMeters(bodyHeight / 4), new Vector2(0, Utility.pixelsToMeters(bodyHeight/2)), 0);
createShipMultiFixtureDef(shape, shape2);
shape.dispose();
shape2.dispose();
}
private void createFixtureDef(PolygonShape shape) {
FixtureDef polygonFixtureDef = new FixtureDef();
polygonFixtureDef.shape = shape;
polygonFixtureDef.density = density;
polygonFixtureDef.restitution = restitution;
body.createFixture(polygonFixtureDef);
}
private void createShipMultiFixtureDef(PolygonShape shape, PolygonShape shape2) {
FixtureDef polygonFixtureDef = new FixtureDef();
polygonFixtureDef.shape = shape;
polygonFixtureDef.density = density;
polygonFixtureDef.restitution = restitution;
FixtureDef polygonFixtureDef2 = new FixtureDef();
polygonFixtureDef2.shape = shape2;
polygonFixtureDef2.density = density;
polygonFixtureDef2.restitution = restitution;
body.createFixture(polygonFixtureDef);
body.createFixture(polygonFixtureDef2);
}
/**
* This is inside the Ship class
* #param x From world center of release
* #param y From world center of release
* #param distance The capped distance of release from shipCenter, to determine force of impulse
*/
protected void applyLinearImpulse(float x, float y, float distance, float maxDistance) {
float deltaX = x - getXinPixels();
float deltaY = y - getYinPixels();
float scale = 0.8f; // Have tried playing around with this with different bodies
float force = scale * (distance/maxDistance);
float angle = (float) Math.atan2(deltaY, deltaX);
bodyWrapper.body.applyLinearImpulse(new Vector2((float) Math.cos(angle) * force,
(float) Math.sin(angle) * force), getWorldCenter(), true);
}
// This is called on each planet every game update
protected void applyGravity() {
for (SpaceObject spaceObject:spaceObjects) {
float scalar = 2.334f;
if(spaceObject instanceof Ship) {
if (gameCore.shipInMotion) {
scalar = 2f; //Have tried playng with this for different bodies
}
else {
continue; //No gravity to ship until it's launched
}
}
Vector2 objectWorldCenter = spaceObject.getWorldCenter();
Vector2 planetWorldCenter = getWorldCenter();
float planetDiameter = Utility.pixelsToMeters(getWidth());
float distance = Utility.distance(planetWorldCenter.x, planetWorldCenter.y, objectWorldCenter.x, objectWorldCenter.y);
float xDistance = planetWorldCenter.x - objectWorldCenter.x;
float yDistance = planetWorldCenter.y - objectWorldCenter.y;
float x = (float) ((xDistance * planetDiameter * scalar) / (distance*distance));
float y = (float) ((yDistance * planetDiameter * scalar) / (distance*distance));
Vector2 gravity = new Vector2(x, y);
spaceObject.bodyWrapper.body.applyForceToCenter(gravity, true);
}
}
Related
I'm working with ARCore in Android Studio using java and am trying to implement ray intersection with an object.
I started with Google's provided sample (as found here: https://developers.google.com/ar/develop/java/getting-started).
Upon touching the screen, a ray gets projected and when this ray touches a Plane, a PlaneAttachment (with an Anchor/a Pose) is created in the intersection point.
I would then like to put a 3D triangle in the world attached to this Pose.
At the moment I create my Triangle based on the Pose's translation, like this:
In HelloArActivity, during onDrawFrame(...)
//Code from sample, determining the hits on planes
MotionEvent tap = mQueuedSingleTaps.poll();
if (tap != null && frame.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon.
if (hit instanceof PlaneHitResult && ((PlaneHitResult) hit).isHitInPolygon()) {
mTouches.add(new PlaneAttachment(
((PlaneHitResult) hit).getPlane(),
mSession.addAnchor(hit.getHitPose())));
//creating a triangle in the world
Pose hitPose = hit.getHitPose();
float[] poseCoords = new float[3];
hitPose.getTranslation(poseCoords, 0);
mTriangle = new Triangle(poseCoords);
}
}
}
Note: I am aware that the triangle's coordinates should be updated every time the Pose's coordinates get updated. I left this out as it is not part of my issue.
Triangle class
public class Triangle {
public float[] v0;
public float[] v1;
public float[] v2;
//create triangle around a given coordinate
public Triangle(float[] poseCoords){
float x = poseCoords[0], y = poseCoords[1], z = poseCoords[2];
this.v0 = new float[]{x+0.0001f, y-0.0001f, z};
this.v1 = new float[]{x, y+ 0.0001f, z-0.0001f};
this.v2 = new float[]{x-0.0001f, y, z+ 0.0001f};
}
After this, upon tapping the screen again I create a ray projected from the tapped (x,y) part of the screen, using Ian M his code sample provided in the answer to this question: how to check ray intersection with object in ARCore
Ray Creation, in HelloArActivity
/**
* Returns a world coordinate frame ray for a screen point. The ray is
* defined using a 6-element float array containing the head location
* followed by a normalized direction vector.
*/
float[] screenPointToWorldRay(float xPx, float yPx, Frame frame) {
float[] points = new float[12]; // {clip query, camera query, camera origin}
// Set up the clip-space coordinates of our query point
// +x is right:
points[0] = 2.0f * xPx / mSurfaceView.getMeasuredWidth() - 1.0f;
// +y is up (android UI Y is down):
points[1] = 1.0f - 2.0f * yPx / mSurfaceView.getMeasuredHeight();
points[2] = 1.0f; // +z is forwards (remember clip, not camera)
points[3] = 1.0f; // w (homogenous coordinates)
float[] matrices = new float[32]; // {proj, inverse proj}
// If you'll be calling this several times per frame factor out
// the next two lines to run when Frame.isDisplayRotationChanged().
mSession.getProjectionMatrix(matrices, 0, 1.0f, 100.0f);
Matrix.invertM(matrices, 16, matrices, 0);
// Transform clip-space point to camera-space.
Matrix.multiplyMV(points, 4, matrices, 16, points, 0);
// points[4,5,6] is now a camera-space vector. Transform to world space to get a point
// along the ray.
float[] out = new float[6];
frame.getPose().transformPoint(points, 4, out, 3);
// use points[8,9,10] as a zero vector to get the ray head position in world space.
frame.getPose().transformPoint(points, 8, out, 0);
// normalize the direction vector:
float dx = out[3] - out[0];
float dy = out[4] - out[1];
float dz = out[5] - out[2];
float scale = 1.0f / (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
out[3] = dx * scale;
out[4] = dy * scale;
out[5] = dz * scale;
return out;
}
The result of this however is that, no matter where I tap the screen, it always counts as a hit (regardless of how much distance I add between the points, in Triangle's constructor).
I suspect this has to do with how a Pose is located in the world, and using the Pose's translation coordinates as a reference point for my triangle is not the way to go, so I'm looking for the correct way to do this, but any remarks regarding other parts of my method are welcome!
Also I have tested my method for ray-triangle intersection and I don't think it is the problem, but I'll include it here for completeness:
public Point3f intersectRayTriangle(CustomRay R, Triangle T) {
Point3f I = new Point3f();
Vector3f u, v, n;
Vector3f dir, w0, w;
float r, a, b;
u = new Vector3f(T.V1);
u.sub(new Point3f(T.V0));
v = new Vector3f(T.V2);
v.sub(new Point3f(T.V0));
n = new Vector3f(); // cross product
n.cross(u, v);
if (n.length() == 0) {
return null;
}
dir = new Vector3f(R.direction);
w0 = new Vector3f(R.origin);
w0.sub(new Point3f(T.V0));
a = -(new Vector3f(n).dot(w0));
b = new Vector3f(n).dot(dir);
if ((float)Math.abs(b) < SMALL_NUM) {
return null;
}
r = a / b;
if (r < 0.0) {
return null;
}
I = new Point3f(R.origin);
I.x += r * dir.x;
I.y += r * dir.y;
I.z += r * dir.z;
return I;
}
Thanks in advance!
There was a problem with box2d. I wanted to add to the already existing game physics , but ran into problems. First, the game world I draw with calculation
public Hero(float x, float y, int width, int height) {
this.width = width;
this.height = height;
position = new Vector2(x, y);
velocity = new Vector2(0, 0);
acceleration = new Vector2(0, -420);
}
public void update(float delta){
velocity.mulAdd(acceleration, delta);
if(velocity.y < -200){
velocity.y = -200;
}
position.mulAdd(velocity,delta);
}
public void onTap(){
velocity.y = 140;
}
the body of the hero falls from the standard setting, but the body box that I have added to the test behaves very strangely.
Question number 1. Why box2d box more than a hero with this setting, but when I divide by two, it becomes similar to the size of the texture of the hero? May be linked such effekts so that the body is drawn based on the center in all directions 2. Why is the body in the world with gravity -420 falls continuously at the same speed, but not as much as my hero. How to achieve a similar effect it?
hero = new Hero(30, midPointY, 18, 21);
hero1 = new Box2Dhero(world, 90, midPointY, 18, 21);
Its box2d hero constructor
public Box2Dhero(World world, float x, float y, int width, int height ) {
bodyDef = new BodyDef();
bodyDef.position.set(x,y);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
box = new PolygonShape();
box.setAsBox(width,height);
fixtureDef = new FixtureDef();
fixtureDef.shape = box;
body.createFixture(fixtureDef);
}
My GameWorld size
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
float gameHeight = 385;
float gameWidth = screenWidth / (screenHeight / gameHeight);
Your observation about box being based on center with unit length in all directions is correct.
It should affect your comparison in the sense that origins of box2d's box and your box do not match. This effect should be relatively small.
You are clamping your velocity in y direction to -200. Making such an assumption may not be a good idea for comparison. Finding a resource regarding what does box2d do would be a good idea (source perhaps).
Most physics engines perform uniform time stepping to be deterministic. Box2D is one of them. You can read about uniform time stepping here.
There might be many more differences/optimizations, looking in source and comparing would be most efficient solution for you in my opinion.
Good luck.
I created a rope in box2d with RevoluteJoint and a RopeJoint but I have several problems with it:
From time to time, the last segment rotates and it looks like it was disconnected from the rope.
How to draw this thing? I create a texture with the size of each link. If the rope isn't moving it looks good, but as soon as the rope moves and the different links start to rotate slightly, you see gaps between the links.
Code:
private void createChain(World world, Body anchorBody) {
Body previousBody = anchorBody;
FixtureDef fixtureDef = new FixtureDef();
PolygonShape robeLinkShape = new PolygonShape();
robeLinkShape.setAsBox(4 / PPM, 8 / PPM);
fixtureDef.shape = robeLinkShape;
fixtureDef.density = 0.1f;
// fixtureDef.friction = 1.0f;
fixtureDef.restitution = 0.1f;
fixtureDef.filter.maskBits = Box2DConst.BIT_PLAYER;
fixtureDef.filter.categoryBits = Box2DConst.BIT_GROUND;
float mapX = anchorBody.getPosition().x * PPM;
float mapY = anchorBody.getPosition().y * PPM;
BodyDef bodyDef = new BodyDef();
bodyDef.angularDamping = 1.0f;
bodyDef.linearDamping = 1.0f;
//create rope
for (int i = 0; i < 10; i++) {
Float robeX = mapX / PPM;
Float robeY = (mapY - (i * 16)) / PPM;
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(robeX, robeY);
final Body link = world.createBody(bodyDef);
link.createFixture(fixtureDef);
RevoluteJointDef jointDef = new RevoluteJointDef();
jointDef.initialize(previousBody, link, new Vector2(robeX, robeY));
//don't need the rope to collide itself
jointDef.collideConnected = false;
jointDef.enableLimit = false;
// because we don't collide with other bodies in the rope, limit rotation to keep the rope bodies from rotating too much.
jointDef.lowerAngle = -5.0f * MathUtils.degreesToRadians;
jointDef.upperAngle = 5.0f * MathUtils.degreesToRadians;
world.createJoint(jointDef);
links.add(link);
previousBody = link;
}
RopeJointDef ropeJointDef = new RopeJointDef();
ropeJointDef.localAnchorB.set(0, 0);
ropeJointDef.maxLength = 90.0f;
ropeJointDef.bodyB = previousBody;
ropeJointDef.bodyA = links.get(0);
ropeJointDef.collideConnected = false;
world.createJoint(ropeJointDef);
}
public void draw(final SpriteBatch batch) {
Texture texture = FipiGame.res.get("rope");
batch.begin();
for (Body link : links) {
float x = (link.getPosition().x * PPM) - 4;
float y = (link.getPosition().y * PPM) - 8;
float angleDeg = MathUtils.radiansToDegrees * link.getAngle();
batch.draw(texture, x, y, 0, 0, texture.getWidth(), texture.getHeight(), 1f, 1f, angleDeg, 0, 0,
texture.getWidth(), texture.getHeight(), false, false);
}
batch.end();
}
Instead of drawing based on body positions and rotations: Create a set of points by looping through the revolute joint positions (midpoint of anchorA and anchorB in world space). Then draw your sprites so they connect those positions. This will be somewhat inaccurate in that it won't perfectly line up with the positions of the rigid bodies, but it should look all right.
I have array of sprites forming T shape, and I want to ratate them around the same origin, in my case box2D body origin, like this:
my shape is defined in matrix like this:
int array[][]= {{0,1,1,1,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0},
{0,0,0,0,0}};
this is how I create the body:
public void setBody(int[][] blocks){
BodyDef def = new BodyDef();
def.type = BodyType.DynamicBody;
def.position.set(new Vector2(0 * WORLD_TO_BOX, 0 * WORLD_TO_BOX));
Body body = world.createBody(def);
body.setTransform(130*WORLD_TO_BOX, 200*WORLD_TO_BOX, -90*MathUtils.degreesToRadians);
for (int x = 0; x < 5; x++) { // HARDCODED 5
for (int y = 0; y < 5; y++) { // HARDCODED 5
if(blocks[x][y] == 1){
PolygonShape poly = new PolygonShape();
Vector2 v = new Vector2((x*size/BOX_TO_WORLD),(y*size/BOX_TO_WORLD));
poly.setAsBox(size/2 * WORLD_TO_BOX, size/2 * WORLD_TO_BOX, v, 0);
Sprite sprite = new Sprite(new Texture(Gdx.files.internal("data/block.png")));
sprite.setSize(size, size);
sprites.add(sprite);
orig.add(v);
body.createFixture(poly, 1);
poly.dispose();
}
}
}
this.body = body;
}
this is my render method:
public void draw(SpriteBatch batch){
for (int i = 0;i< this.body.getFixtureList().size;i++) {
Vector2 pos = this.body.getFixtureList().get(i).getBody().getPosition();
Sprite sprite = sprites.get(i);
sprite.setOrigin(this.body.getWorldCenter().x,this.body.getWorldCenter().y);
sprite.setPosition(pos.x*BOX_TO_WORLD+orig.get(i).x*32-16, pos.y*BOX_TO_WORLD+orig.get(i).y*32-16);
sprite.setRotation(this.body.getAngle()*MathUtils.radiansToDegrees);
sprite.draw(batch);
}
}
As you can see in your 'right' example in the diagram, the position of the sprites depends on the angle of the body, which you are not accounting for.
b2Vec2 pos = ...; // center of the sprite relative to body (local coords)
float ang = ...; // angle of the sprite relative to body (probably zero)
//need to rotate image local center by body angle
b2Rot rot( body->GetAngle() );
pos = b2Mul(rot, pos) + body->GetPosition();
ang += -body->GetAngle();
sprite.setRotation( ang * RADTODEG ); // RADTODEG = 57.295779513082320876f
sprite.setPosition( PTM * pos.x, PTM * -pos.y);
Prior to that, I have also done:
sf::FloatRect rect = sprite.getLocalBounds();
sprite.setOrigin( 0.5 * rect.width, 0.5 * rect.height );
The easiest way to do this is to create all the fixtures, remembering what their relative position is to the body. Create a sprite for each fixture and update them using the body->GetWorldPosition(fixture center) each time the physics updates. Finally, the rotation of the sprites is the same as the rotation of the body (excepting it is the negative of the angle).
For example, to create the body:
void MainScene::CreateBody()
{
Vec2 position(0,0);
// Create the body.
b2BodyDef bodyDef;
bodyDef.position = position;
bodyDef.type = b2_dynamicBody;
_body = _world->CreateBody(&bodyDef);
assert(_body != NULL);
// Now attach fixtures to the body.
FixtureDef fixtureDef;
PolygonShape polyShape;
vector<Vec2> vertices;
const float32 VERT_SCALE = .5;
fixtureDef.shape = &polyShape;
fixtureDef.density = 1.0;
fixtureDef.friction = 1.0;
fixtureDef.isSensor = false;
// Main Box
vertices.clear();
vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,-1*VERT_SCALE));
vertices.push_back(Vec2(1*VERT_SCALE,-1*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
_body->CreateFixture(&fixtureDef);
_fixturePositions.push_back(CalculateAverage(vertices));
// Down one
vertices.clear();
vertices.push_back(Vec2(1*VERT_SCALE,-1*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,-1*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,-3*VERT_SCALE));
vertices.push_back(Vec2(1*VERT_SCALE,-3*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
_body->CreateFixture(&fixtureDef);
_fixturePositions.push_back(CalculateAverage(vertices));
// Up One
vertices.clear();
vertices.push_back(Vec2(1*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
_body->CreateFixture(&fixtureDef);
_fixturePositions.push_back(CalculateAverage(vertices));
// T Left Top
vertices.clear();
vertices.push_back(Vec2(-1*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(-3*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(-3*VERT_SCALE,1*VERT_SCALE));
vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
_body->CreateFixture(&fixtureDef);
_fixturePositions.push_back(CalculateAverage(vertices));
// T Right Top
vertices.clear();
vertices.push_back(Vec2(3*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(1*VERT_SCALE,3*VERT_SCALE));
vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
vertices.push_back(Vec2(3*VERT_SCALE,1*VERT_SCALE));
polyShape.Set(&vertices[0],vertices.size());
_body->CreateFixture(&fixtureDef);
_fixturePositions.push_back(CalculateAverage(vertices));
_body->SetAngularVelocity(M_PI/8);
}
NOTE The CalculateAverage(...) function just finds the average of the vertices. For squares, it will be the center. I could manually set the centers, but I didn't want to make a simple math oopsie so I wrote a quick function to handle it.
Then create the sprites:
void MainScene::CreateSprites()
{
Viewport& vp = Viewport::Instance();
for(int idx = 0; idx < _fixturePositions.size(); idx++)
{
CCSprite* sprite = CCSprite::create("arrow.png");
sprite->setScale(1.0*vp.GetPTMRatio()/128);
_fixtureSprites.push_back(sprite);
addChild(sprite);
}
}
Then update the sprites after each physics update:
void MainScene::UpdateSprites()
{
for(int idx = 0; idx < _fixturePositions.size(); idx++)
{
CCPoint spritePosition = Viewport::Instance().Convert(_body->GetWorldPoint(_fixturePositions[idx]));
_fixtureSprites[idx]->setPosition(spritePosition);
float32 bodyAngle = _body->GetAngle();
bodyAngle = MathUtilities::AdjustAngle(bodyAngle);
_fixtureSprites[idx]->setRotation(-CC_RADIANS_TO_DEGREES(bodyAngle));
}
}
NOTE The viewport has a function Convert(..) that takes a Vec2 and converts it to a pixel position on the screen. AdjustAngle just puts the angle back in the range of [-pi,pi). All the rest should be fairly straightforward, but feel free to ask.
I have posted a solution on git hub here for Cocos2d-x (C++). Check out the code in MainScene.cpp.
And this is what it looks like on my simulator:
(Hopefully) Final Note: The implementation here uses a "Viewport" class to map between the Box2d world (meters) and the screen coordinates (pixels). One very useful consequence of this is that you can automatically adjust the sizes of sprites based on the size you want the bodies to be in meters and the size of the graphic in pixels. This is part of a larger set of components that I often use. You can find more information about them in this post.
Was this helpful?
So I am making a game in box 2d. In one part I need a ball which has a constant bounce height. I set the ball's restitution to 1 and am applying no force on it whatsoever (except gravity of course). Now this ball, on every bounce, bounces a bit higher everytime till it goes out of the top edge of the screen. What is wrong with this code?
public class Ball implements ApplicationListener {
World world ;
Box2DDebugRenderer debugRenderer;
OrthographicCamera camera;
static final float BOX_STEP=1/60f;
static final int BOX_VELOCITY_ITERATIONS=8;
static final int BOX_POSITION_ITERATIONS=3;
static final float WORLD_TO_BOX=0.01f;
static final float BOX_WORLD_TO=100f;
Rectangle ball;
Body body;
/* (non-Javadoc)
* #see com.badlogic.gdx.ApplicationListener#create()
*/
#Override
public void create() {
world = new World(new Vector2(0, -10), true);
camera = new OrthographicCamera();
camera.viewportHeight = 480;
camera.viewportWidth = 800;
camera.position.set(camera.viewportWidth * .5f, camera.viewportHeight * .5f, 0f);
camera.update();
//Ground body
BodyDef groundBodyDef =new BodyDef();
groundBodyDef.position.set(new Vector2(0, 10 * WORLD_TO_BOX));
Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
float w = (camera.viewportWidth * 2) * WORLD_TO_BOX;
float h = 10.0f * WORLD_TO_BOX;
groundBox.setAsBox(w,h);
groundBody.createFixture(groundBox, 0.0f);
String a="gb";
groundBody.setUserData(a);
//Dynamic Body
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
float posX = (camera.viewportWidth / 8) * WORLD_TO_BOX;
float posY = (camera.viewportHeight / 2) * WORLD_TO_BOX;
bodyDef.position.set(posX, posY);
body = world.createBody(bodyDef);
// create a Rectangle to logically represent the ball
CircleShape dynamicCircle = new CircleShape();
dynamicCircle.setRadius(1f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = dynamicCircle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.0f;
fixtureDef.restitution =1f;
body.createFixture(fixtureDef);
debugRenderer = new Box2DDebugRenderer();
}
#Override
public void dispose() {
}
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
debugProjection.set(camera.combined);
debugProjection.scl(BOX_WORLD_TO);
debugRenderer.render(world, debugProjection);
world.step(BOX_STEP, BOX_VELOCITY_ITERATIONS, BOX_POSITION_ITERATIONS);
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
This may be caused by numerical instability. Repeatedly calculating positions of your ball inevitably introduces tiny numerical errors (because a float can only hold a limited number of digits). These may grow and add up from iteration to iteration, bounce to bounce.
You can try using restitution 0.9999999 or something. But chances are that there is no restitution value which gives desired results.
Try to actively compensate for the numerical instability e.g. by controlling the ball's position and/or velocity in situations in which you can calculate values non-iteratively. (I do not know Box2D. Maybe when the ball hits the ground you can reset its speed or something.)
Ok I solved it by setting the restitution to 0 and applying an upward force of 5N on each contact. As halfbit said, the problem is that because box2d uses float, the position calculations are not precise which causes minor changes in height on every bounce.
If I have done the math right for the calculations, your sphere has a radius of 1m, and your box as dimensions of 16m x 0.1m.
In Box2d, everything needs to be on the scale of 0.1m to 10m. The reason your bodies may be numerically unstable is because the ratio of the width to height is greater than the normal sizes of objects. While you may get some stability by changing the restitution, I think you will always be on the "hairy edge" with these dimensions.
Try changing the dimensions of your objects so the width is 8 and the height is 0.1.