Anton  Naturkach
Anton Naturkach

Reputation: 3

Newton's cradle on andengine, box2d

i'm trying to create something like Newton's cradle. But when one ball hits another, all balls move simultaneously in same direction. How can I decide this problem? Should i create another physical options of balls in createFixtureDef? Or I need to use some specific algorithm to transfer impuls between balls?

public class MainActivity extends SimpleBaseGameActivity implements IAccelerationListener, IOnAreaTouchListener
{

private static final int CAMERA_WIDTH = 720;
private static final int CAMERA_HEIGHT = 480;
private BitmapTextureAtlas mBitmapTextureAtlas;

final String TAG = "States";
private Scene mScene;

protected ITiledTextureRegion mBoxFaceTextureRegion;
protected ITiledTextureRegion mCircleFaceTextureRegion;

protected PhysicsWorld mPhysicsWorld;
@Override
public EngineOptions onCreateEngineOptions() {
    final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);

    return new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, 
            new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
}

@Override
protected void onCreateResources() {
    BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

    this.mBitmapTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 64, 64, TextureOptions.BILINEAR);
    this.mBoxFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas, this, "face_box_tiled.png", 0, 0, 2, 1); // 64x32
    this.mCircleFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas, this, "face_circle_tiled.png", 0, 32, 2, 1); // 64x32
    this.mBitmapTextureAtlas.load();

}

@Override
protected Scene onCreateScene() {
    this.mEngine.registerUpdateHandler(new FPSLogger());

    this.mScene = new Scene();
    this.mScene.setBackground(new Background(0, 0, 0));
    //this.mScene.setOnSceneTouchListener(this);

    this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);



    this.initJoints(mScene);


    this.mScene.registerUpdateHandler(this.mPhysicsWorld);
    this.mScene.setOnAreaTouchListener(this);

    return this.mScene;
}

private void initJoints(final Scene pScene) {
    final float centerY = CAMERA_HEIGHT / 2;

    final float spriteWidth = this.mBoxFaceTextureRegion.getWidth();
    final float spriteHeight = this.mBoxFaceTextureRegion.getHeight();

    final FixtureDef objectFixtureDef = PhysicsFactory.createFixtureDef(30, 0.2f, 0.2f);

    for(int i = 0; i < 10; i++) {
        final float anchorFaceX =  100 + i * spriteWidth ;
        final float anchorFaceY = centerY;

        final AnimatedSprite anchorFace = new AnimatedSprite(anchorFaceX, anchorFaceY, 
                this.mBoxFaceTextureRegion, this.getVertexBufferObjectManager());
        final Body anchorBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, 
                anchorFace, BodyType.StaticBody, 
                objectFixtureDef);

        final AnimatedSprite movingFace = new AnimatedSprite(anchorFaceX, anchorFaceY + 150, 
                this.mCircleFaceTextureRegion, this.getVertexBufferObjectManager()) ;
//      movingFace.setScale(1.2f);
        final Body movingBody = PhysicsFactory.createCircleBody(this.mPhysicsWorld, 
                        movingFace, BodyType.DynamicBody, objectFixtureDef);
        movingFace.setUserData(movingBody);

//      anchorFace.setScale(1.2f);
        anchorFace.animate(200);
        anchorFace.animate(200);
        final Line connectionLine = new Line(anchorFaceX + spriteWidth / 2,
                                    anchorFaceY + spriteHeight / 2,
                                    anchorFaceX + spriteWidth / 2, 
                                    anchorFaceY + spriteHeight / 2, 
                                    this.getVertexBufferObjectManager());

        pScene.registerTouchArea(movingFace);
        pScene.attachChild(connectionLine);
        pScene.attachChild(anchorFace);
        pScene.attachChild(movingFace);

        this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(anchorFace, 
                anchorBody, true, true){
            @Override
            public void onUpdate(final float pSecondsElapsed) {
                super.onUpdate(pSecondsElapsed);
                final Vector2 movingBodyWorldCenter = movingBody.getWorldCenter();
                connectionLine.setPosition(connectionLine.getX1(), connectionLine.getY1(), 
                        movingBodyWorldCenter.x * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, 
                        movingBodyWorldCenter.y * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
            }
        });

        this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(movingFace, movingBody, true, true));
        final RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
        revoluteJointDef.initialize(anchorBody, movingBody, anchorBody.getWorldCenter());
        this.mPhysicsWorld.createJoint(revoluteJointDef);
    }
}
@Override
public void onAccelerationAccuracyChanged(final AccelerationData pAccelerationData) {
}
@Override
public void onAccelerationChanged(final AccelerationData pAccelerationData) {
    final Vector2 gravity = Vector2Pool.obtain(pAccelerationData.getX(), pAccelerationData.getY());
    this.mPhysicsWorld.setGravity(gravity);
    Vector2Pool.recycle(gravity);
}


public boolean onAreaTouched( final TouchEvent pSceneTouchEvent, 
        final ITouchArea pTouchArea,final float pTouchAreaLocalX,
        final float pTouchAreaLocalY) {

    if(pSceneTouchEvent.isActionMove())
    {
        float touchX = pSceneTouchEvent.getX();
        float touchY = pSceneTouchEvent.getY();
        Log.d(TAG, "move to in X" +touchX + "n Y " +touchY);
        final AnimatedSprite anchorFace = (AnimatedSprite) pTouchArea;
        final Body tochedBody = (Body)anchorFace.getUserData();
         //move sprite to xy
        final float x = pSceneTouchEvent.getX();
        final float y = pSceneTouchEvent.getY();
        final float widthD2 = anchorFace.getWidth() / 2;
        final float heightD2 = anchorFace.getHeight() / 2;
        final float angle = tochedBody.getAngle(); // keeps the body angle
        final Vector2 v2 = Vector2Pool.obtain((x + widthD2) / 32, (y + heightD2) / 32);         
       tochedBody.setTransform(v2, angle);
        Vector2Pool.recycle(v2);
        return true;        
    }
    return false;
}

}

Upvotes: 0

Views: 401

Answers (1)

iforce2d
iforce2d

Reputation: 8262

Unfortunately Box2D is not really suitable for this.

In my experience, you can get one or two swing-thrus to work as long as the balls are not touching each other to start with. That is, each ball starts with a very small gap between it and the neighbor on each side. This means that when they collide each collision is solved using just two balls at a time, for a total of 4 separate collisions for the impact to travel to the other side (for 5 balls), instead of solving all 5 balls as a single 'island'.

Trouble is, the positioning needs to be very precise, and after a few swings the balls stray from that, so that you end up with collisions involving more than two balls at a time. I only ever saw at most 2-3 swings work as desired...

Upvotes: 1

Related Questions