简体   繁体   中英

Physics and gravity in Andengine/Box2D platformer

I'm putting together a 2D platformers using Andengine with the Box2D extension using the PhysicsJumpExample ( https://code.google.com/p/andengineexamples/source/browse/src/org/anddev/andengine/examples/PhysicsJumpExample.java ) as a base. I'm also using the TMX extension for level loading.

My level, player controls etc seem to be working OK but I'm really struggling with the Box2D physics in terms of gravity. The AnimatedSprite being controlled is being acted upon by gravity but the descent is sooo slow when using "Earth" gravity (~-9.8). If I increase this gravity then the descent is quicker but then the Sprite bounces up and down on the solid "floor" and sideways movement is greatly reduced (due to friction I would assume).

Finally, for some reason, every example I've looked at has used a positive value for gravity when instantiating the PhysicsWorld but I've had to reverse it in order to have gravity act "as normal".

Obviously I'm doing something very wrong and would greatly appreciate some pointers in getting this sorted out.

Code for the 2 classes in question below.

public class GameActivity extends SimpleBaseGameActivity {

private final static String TAG = "GameActivity";

private final int CAMERA_WIDTH = 800;
private final int CAMERA_HEIGHT = 480;

private HUD _hud;
private Text _score;
private Text _debug;
private PhysicsWorld mPhysicsWorld;
private PhysicsHandler _playerPhysics;
private TMXTiledMap _map;
private PlayerSprite _player;
private BitmapTextureAtlas mOnScreenControlTexture;
private TextureRegion mOnScreenControlBaseTextureRegion;
private TextureRegion mOnScreenControlKnobTextureRegion;
private Scene mScene;
private Font mFont;

private ITexture mPlayerTexture;
private TiledTextureRegion mPlayerTextureRegion;

@Override
public EngineOptions onCreateEngineOptions() {
    final BoundCamera camera = new BoundCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
    EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
    engineOptions.getAudioOptions().setNeedsMusic(true).setNeedsSound(true);
    engineOptions.setWakeLockOptions(WakeLockOptions.SCREEN_ON);
    return engineOptions;
}

@Override
public void onCreateResources() throws IOException {
}

@Override
public Scene onCreateScene() {
    this.mEngine.registerUpdateHandler(new FPSLogger());
    this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, 0 - (SensorManager.GRAVITY_EARTH)), false);
    this.mScene = new Scene();
    this.mScene.getBackground().setColor(Color.BLACK);
    try {
        createFont();
        createWorld();
        createPlayer();
        createControllers();
        createHUD();
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
    }
    this.mScene.registerUpdateHandler(this.mPhysicsWorld);
    return mScene;
}

private void createFont() {
    FontFactory.setAssetBasePath("media/fonts/");
    final ITexture mainFontTexture = new BitmapTextureAtlas(getTextureManager(), 256, 256, TextureOptions.NEAREST);
    mFont = FontFactory.createStrokeFromAsset(getFontManager(), mainFontTexture, getAssets(), "game.otf", 40f, true, android.graphics.Color.WHITE, 2f,
        android.graphics.Color.BLACK);
    mFont.load();
}

private void createHUD() {
    Log.d(TAG, "createHud");
    _hud = new HUD();
    _score = new Text(mEngine.getCamera().getCameraSceneWidth() - 80, mEngine.getCamera().getCameraSceneHeight() - 20, mFont, "0123456789", new TextOptions(
        HorizontalAlign.RIGHT), getVertexBufferObjectManager());
    _score.setText("0000");
    _hud.attachChild(_score);
    _debug = new Text(150, mEngine.getCamera().getCameraSceneHeight() - 20, mFont, "abcdefghijklmnopqrstuvwxyz0123456789.,()[] ", new TextOptions(HorizontalAlign.LEFT),
        getVertexBufferObjectManager());
    _debug.setText("");
    _hud.attachChild(_debug);
    mEngine.getCamera().setHUD(_hud);
}

private void createPlayer() throws Exception {
    Log.d(TAG, "createPlayer");
    this.mPlayerTexture = new AssetBitmapTexture(getTextureManager(), getAssets(), "media/sprites/p1.png");
    this.mPlayerTextureRegion = TextureRegionFactory.extractTiledFromTexture(this.mPlayerTexture, 5, 2);
    this.mPlayerTexture.load();
    _player = new PlayerSprite(50, 320, this.mPlayerTextureRegion, mEngine.getCamera(), this.mPhysicsWorld, getVertexBufferObjectManager());
    _player.setUserData(_player.getBody());
    _playerPhysics = new PhysicsHandler(_player);
    this.mPhysicsWorld.setContactListener(new ContactListener() {

        @Override
        public void preSolve(Contact contact, Manifold oldManifold) {
            // TODO Auto-generated method stub

        }

        @Override
        public void postSolve(Contact contact, ContactImpulse impulse) {
            // TODO Auto-generated method stub

        }

        @Override
        public void endContact(Contact contact) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beginContact(Contact contact) {
            if (contact == null || contact.getFixtureA() == null || contact.getFixtureB() == null)
                return;
            if (contact.getFixtureA().getBody().getUserData() == null || contact.getFixtureB().getBody().getUserData() == null)
                return;
            if (contact.getFixtureA().getBody().getUserData().toString().equals("player") || contact.getFixtureB().getBody().getUserData().toString().equals("player")) {
                _player.setJumping(false);
            }
        }
    });
    _player.registerUpdateHandler(_playerPhysics);
    this.mScene.attachChild(_player);
}

private void createControllers() {
    Log.d(TAG, "createControllers");
    BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("media/");
    mOnScreenControlTexture = new BitmapTextureAtlas(getTextureManager(), 256, 128, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
    mOnScreenControlBaseTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mOnScreenControlTexture, this, "images/controller.png", 0, 0);
    mOnScreenControlKnobTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mOnScreenControlTexture, this, "images/controller_top.png", 128, 0);
    getTextureManager().loadTexture(mOnScreenControlTexture);
    final float x1 = mOnScreenControlBaseTextureRegion.getWidth();
    final float y1 = (this.mEngine.getCamera().getCameraSceneHeight() - mOnScreenControlBaseTextureRegion.getHeight());
    final DigitalOnScreenControl velocityOnScreenControl = new DigitalOnScreenControl(x1, y1, this.mEngine.getCamera(), this.mOnScreenControlBaseTextureRegion,
        this.mOnScreenControlKnobTextureRegion, 0.1f, true, getVertexBufferObjectManager(), new IOnScreenControlListener() {

            @Override
            public void onControlChange(BaseOnScreenControl pBaseOnScreenControl, float pValueX, float pValueY) {
                float x = 0, y = 0;
                if (pValueX == -1 && pValueY == 0) {
                    if (_player.getDirection() != PlayerDirection.LEFT) {
                        _player.setRotation(-1);
                        _player.animate(75);
                    }
                    x = pValueX * 8;
                    y = pValueY * 8;
                    _player.setDirection(PlayerDirection.LEFT);
                } else if (pValueX == -1 && pValueY == 1) {
                    if (!_player.isJumping()) {
                        if (_player.getDirection() != PlayerDirection.UP_LEFT) {
                            _player.animate(25);
                        }
                        x = SensorManager.GRAVITY_EARTH * -10;
                        y = SensorManager.GRAVITY_EARTH * -10;
                        _player.setDirection(PlayerDirection.UP_LEFT);
                    }
                } else if (pValueX == 0 && pValueY == 1) {
                    if (!_player.isJumping()) {
                        if (_player.getDirection() != PlayerDirection.UP) {
                            _player.animate(75);
                        }
                        y = SensorManager.GRAVITY_EARTH * 2;
                        _player.setDirection(PlayerDirection.UP);
                    }
                } else if (pValueX == 1 && pValueY == 1) {
                    if (!_player.isJumping()) {
                        if (_player.getDirection() != PlayerDirection.UP_RIGHT) {
                            _player.animate(25);
                        }
                        x = SensorManager.GRAVITY_EARTH * 10;
                        y = SensorManager.GRAVITY_EARTH * -10;
                        _player.setDirection(PlayerDirection.UP_RIGHT);
                    }
                } else if (pValueX == 1 && pValueY == 0) {
                    if (_player.getDirection() != PlayerDirection.RIGHT) {
                        _player.animate(75);
                    }
                    x = pValueX * 8;
                    y = pValueY * 8;
                    _player.setDirection(PlayerDirection.RIGHT);
                } else if (pValueX == 1 && pValueY == -1) {
                    _player.setDirection(PlayerDirection.DOWN_RIGHT);
                } else if (pValueX == 0 && pValueY == 1) {
                    _player.setDirection(PlayerDirection.DOWN);
                } else if (pValueX == -1 && pValueY == -1) {
                    _player.setDirection(PlayerDirection.DOWN_LEFT);
                } else {
                    _player.setDirection(PlayerDirection.NONE);
                    if (!_player.isJumping()) {
                        _player.stopAnimation(0);
                    }
                }
                final Vector2 velocity = Vector2Pool.obtain(x, y);
                _player.getBody().setLinearVelocity(velocity);
                Vector2Pool.recycle(velocity);
                _debug.setText(_player.getDirection().toString() + ": " + (pValueX * 60) + "," + (pValueY * 60));
            }
        });
    velocityOnScreenControl.getControlBase().setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    velocityOnScreenControl.getControlBase().setAlpha(0.5f);

    this.mScene.setChildScene(velocityOnScreenControl);
}

private void createWorld() {
    Log.d(TAG, "createWorld");
    try {
        final TMXLoader tmxLoader = new TMXLoader(getAssets(), getTextureManager(), TextureOptions.NEAREST, getVertexBufferObjectManager());
        _map = tmxLoader.loadFromAsset("media/levels/1/1.tmx");
        final TMXLayer tmxLayer = _map.getTMXLayers().get(0);
        tmxLayer.detachSelf();
        this.mScene.attachChild(tmxLayer);
        ((BoundCamera) this.mEngine.getCamera()).setBounds(0, 0, tmxLayer.getHeight(), tmxLayer.getWidth());
        ((BoundCamera) this.mEngine.getCamera()).setBoundsEnabled(true);

        float height = tmxLayer.getHeight();
        float width = tmxLayer.getWidth();

        final IShape bottom = new Rectangle(0, height - 2, width, 2, getVertexBufferObjectManager());
        bottom.setVisible(false);
        final IShape top = new Rectangle(0, 0, width, 2, getVertexBufferObjectManager());
        top.setVisible(false);
        final IShape left = new Rectangle(0, 0, 2, height, getVertexBufferObjectManager());
        left.setVisible(false);
        final IShape right = new Rectangle(width - 2, 0, 2, height, getVertexBufferObjectManager());
        right.setVisible(false);

        final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
        PhysicsFactory.createBoxBody(this.mPhysicsWorld, bottom, BodyType.StaticBody, wallFixtureDef);
        PhysicsFactory.createBoxBody(this.mPhysicsWorld, top, BodyType.StaticBody, wallFixtureDef);
        PhysicsFactory.createBoxBody(this.mPhysicsWorld, left, BodyType.StaticBody, wallFixtureDef);
        PhysicsFactory.createBoxBody(this.mPhysicsWorld, right, BodyType.StaticBody, wallFixtureDef);

        this.mScene.attachChild(bottom);
        this.mScene.attachChild(top);
        this.mScene.attachChild(left);
        this.mScene.attachChild(right);

        float yOffset = _map.getTMXLayers().get(0).getHeight();
        for (final TMXObjectGroup group : _map.getTMXObjectGroups()) {
            if (group.getTMXObjectGroupProperties().containsTMXProperty("solid", "true")) {
                for (final TMXObject object : group.getTMXObjects()) {
                    final Rectangle rect = new Rectangle(object.getX() + object.getWidth() / 2, yOffset - object.getY() - object.getHeight() / 2, object.getWidth(),
                        object.getHeight(), getVertexBufferObjectManager());
                    final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
                    Body b = PhysicsFactory.createBoxBody(this.mPhysicsWorld, rect, BodyType.StaticBody, boxFixtureDef);
                    b.setUserData("solid");
                    rect.setVisible(false);
                    rect.setZIndex(0);
                    this.mScene.attachChild(rect);
                }
            }
        }
    } catch (final TMXLoadException e) {
        Log.e(TAG, e.getMessage());
    }
}

@Override
public void onDestroyResources() throws IOException {
    Log.d(TAG, "onDestroyResources: Cleanup scenes/resources");
    super.onDestroyResources();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    System.exit(0);
}

}

public class PlayerSprite extends AnimatedSprite {

public enum PlayerDirection {
    NONE, LEFT, UP_LEFT, UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT
}

private Body _body;
private PlayerDirection _direction = PlayerDirection.NONE;
private boolean _jumping = false;

public PlayerSprite(float pX, float pY, ITiledTextureRegion region, Camera camera, PhysicsWorld physics, VertexBufferObjectManager vbo) {
    super(pX, pY, region, vbo);
    createPhysics(camera, physics);
    camera.setChaseEntity(this);
}

public Body getBody() {
    return _body;
}

public PlayerDirection getDirection() {
    return _direction;
}

public void setDirection(PlayerDirection direction) {
    _direction = direction;
}

public boolean isJumping() {
    return _jumping;
}

public void setJumping(boolean jumping) {
    _jumping = jumping;
}

private void createPhysics(final Camera camera, PhysicsWorld physics) {
    _body = PhysicsFactory.createBoxBody(physics, this, BodyType.DynamicBody, PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f));
    _body.setUserData("player");
    physics.registerPhysicsConnector(new PhysicsConnector(this, _body, true, true));
}

}

So as is always the case simply posting and asking for help has miraculously resulted in self resolution.

For anybody else who encounters something similar (though I doubt anyone could be so silly!) the problem was that the "controller" was enacting forces upon the player (left, right, jump etc) but the OnControlChange event of IOnScreenControlListener gets called constantly and I had "_player.getBody().setLinearVelocity(velocity);" (with the velocity being a default 0, 0) called repeatedly thereby negating some of the gravitational force.

Doh!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM