0

I'm trying to create a simple test for Box2d, Cocos2d, and Android. All I need is to put one body on the screen, and have it respond to gravity. I looked everywhere and there are good tutorials for non-Android applications but none of them has gravity on Android working. Can anyone help?

This is the code I'm using. I took it, and modified lightly from here: http://www.expobrain.net/2012/05/12/box2d-physics-simulation-on-android/

For creating the world:

World world = new World(new Vec2(2.0f, 8.0f), false);

And this is how I create the body:

public void setupBallBody() {
    CGSize size = CCDirector.sharedDirector().winSize();
    CGPoint pos = CGPoint.make(size.width / 1.2f, size.height / 1.2f);

    // Create Dynamic Body
    BodyDef bodyDef = new BodyDef();

    bodyDef.type = BodyType.DYNAMIC;
    bodyDef.position.set(screenToWorld(pos));

    ballBody = world.createBody(bodyDef);
    MassData md = new MassData();
    md.mass = 5;
    ballBody.setMassData(md);        

    // Create Shape
    CircleShape ballShape = new CircleShape();

    ballShape.m_radius = SMILE_RADIUS;

    // Create fixture
    FixtureDef ballFixture = new FixtureDef();

    ballFixture.shape = ballShape;
    ballFixture.density = SMILE_DENSITY;
    ballFixture.friction = SMILE_FRICTION;
    ballFixture.restitution = SMILE_RESTITUTION;
    // Assign fixture to Body
    ballBody.createFixture(ballFixture);

    // Set sprite
    final CCSprite ballSprite = CCSprite.sprite("ball.jpg");
    ballSprite.setPosition(pos);
    addChild(ballSprite, 0);
    ballBody.setUserData(ballSprite);

}

This is my "tick" method (which I'm not sure is part of what makes gravity working but I include it here for completeness.)

public void tick(float dt) {
    synchronized (world) {
        world.step(1/60, 10, 10);
    }

    // Update sprites
    for (Body b = world.getBodyList(); b != null; b = b.getNext()) {

        if(b == ballBody) {
            CCSprite ballSprite = (CCSprite)ballBody.getUserData();
            if(ballSprite != null) {
                ballSprite.setPosition(worldToScreen(ballBody.getPosition())));     
                ballSprite.setRotation(-1.0f * (float)Math.toDegrees((ballBody.getAngle()))); 
            }
        }           
    }
}
Eddy
  • 3,533
  • 13
  • 59
  • 89

1 Answers1

1

My guessing here is this line may be the problem.-

world.step(1/60, 10, 10);

step function handles the bodies positions based on the time passed since last step. You're doing an integer division 1/60, which result is 0. Try 1.0f / 60.0f instead.

Otherwise, you're telling your world that 0 milliseconds passed since the last step, so bodies will always remain at their initial position.

However, it's not good practice to 'hardcode' your time step. You better pass to your world step the delta time your receiving.-

world.step(dt, 10, 10);

Also, you may simplify your loop to work for any body with an attached CCSprite, like this.-

for (Body b = world.getBodyList(); b != null; b = b.getNext()) {
    CCSprite sprite = (CCSprite)b.getUserData();
    if(sprite != null) {
        sprite.setPosition(worldToScreen(b.getPosition())));     
        sprite.setRotation(-1.0f * (float)Math.toDegrees((b.getAngle()))); 
    }           
}
ssantos
  • 16,001
  • 7
  • 50
  • 70
  • Thanks, @ssantos. I tried it and so the timestep is 0.016666668 but that doesn't change anything. The ball is still stationary. – Eddy Apr 17 '13 at 10:31
  • Mmh, more thoughts, you don't need to manually set `mass data`, as `box2d` will calculate the appropriate mass for each `fixture`. What's the value of `SMILE_DENSITY`? I'm assuming `worldToScreen` function is handling pixels to meters conversion, isn't it? – ssantos Apr 17 '13 at 10:35
  • OK I removed the massdata setting. SMILE_DENSITY = 0.25f; SMILE_FRICTION = 0.1f; SMILE_RESTITUTION = 0.7f; – Eddy Apr 17 '13 at 10:39
  • private CGPoint worldToScreen(final Vec2 coord) { return CGPoint.make(coord.x * SCREEN_TO_WORLD_RATIO, coord.y * SCREEN_TO_WORLD_RATIO); } SCREEN_TO_WORLD_RATION = 2000.0f – Eddy Apr 17 '13 at 10:41
  • Ok, just to make sure the `body` position isn't changing, trace `b.getPosition()` at the first line of your loop, even before `if` conditions. – ssantos Apr 17 '13 at 10:44
  • Looks like it is changing somewhat. Starts at body position = (0.19999999,0.3106177) and moves down to (0.19999999,0.035853803) and then goes up and down a bit and stops at (0.19999999,0.019370025). None of this is scene on the screen. – Eddy Apr 17 '13 at 10:56
  • I've update my answer with a simplified loop. It'd probably not solve the problem, but could help to clarify. Check that execution is reaching inside `if (sprite != null)`, and trace the values of `worldToScreen(b.getPosition())` – ssantos Apr 17 '13 at 11:03
  • Great, actually it is working now. So the issue was that the update method needs to do it for all the bodies, and not just for the ball body? – Eddy Apr 17 '13 at 11:27
  • my next step is to have control over the ball through touch events. I know it's done with mouse joint but if you have code snippet I'll greatly appreciate it. And thanks anyway for this answer! – Eddy Apr 17 '13 at 11:29
  • Well, it should have been enough to update your ball body, my guess is one of your prior conditions was not returning true, so you were not reaching the sprite positioning code. As for `mouseJoint`, you may find useful this thread http://stackoverflow.com/questions/7594620/how-to-use-the-mouse-joint-in-android – ssantos Apr 17 '13 at 11:35
  • Looks like he's using some nonstandard framework (with Vector2 and AnimatedSprite that aren't in the standard one. I'll continue to research it. – Eddy Apr 17 '13 at 12:27