Box2D机身在相机居中时滞后/跳跃

时间:2015-03-05 19:59:54

标签: android camera libgdx box2d interpolation

我正在尝试将相机位置设置到身体的位置,但是当我这样做时,身体会非常明显地跳跃。您可以使用调试渲染器看到这一点,但我的代码中附加了一个精灵。跳跃始终是精灵前进的方向。我有一个带插值的固定时间步,我将精灵位置更新为每帧的box2d体的当前和最后位置的插值。然后将相机设置为插补位置。

import java.util.Random;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Input.Orientation;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.JointEdge;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.viewport.FitViewport;

public class GameScreen implements Screen { 
private static learnGame game;
private static OrthographicCamera camera;
private static FitViewport viewport;
private static Random rand;
private static BitmapFont font;
private static Vector3 touch;
private static double frameTime;
private static double accumulator;
private static float animTime = 0f;
private static float step = 1f / 60f;
private static boolean killBody;
private static Buttons buttons;

public static Player tom;
public static InputMultiplexer multiplexer;
public static Bodies world;
public static boolean paused;
private static Sprite tomSprite = new Sprite(new Texture(Gdx.files.internal("chars/bob.png")));

public GameScreen(learnGame learngame) {
    GameScreen.game = learngame;
    multiplexer = new InputMultiplexer();
    Gdx.input.setInputProcessor(multiplexer);
    camera = new OrthographicCamera();
    viewport = new FitViewport(40, 22.5f, camera);
    buttons = new Buttons(game, multiplexer, viewport); //HUD stuff
    world = new Bodies(viewport, multiplexer); //creates a box2d world
    tom = new Player(world.box2d, 10, 15, 1f, multiplexer); //creates a body with a CircleShape with a radius of 1.  Catches user input to apply forces to the body
    Assets.loadSprites();
    world.box2d.getBodies(world.bodies);
    font = new BitmapFont();

    touch = new Vector3();
    rand = new Random();
}

@Override
public void render(float delta) {

    Gdx.gl.glClearColor(0, 0, 0, 0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    learnGame.batch.setProjectionMatrix(camera.combined);
    frameTime = 0;

    if (!paused) {

        frameTime = Math.min(Gdx.graphics.getRawDeltaTime(), 0.25); 
        accumulator += frameTime;

        tom.update(); //here I apply forces to the body that I attach a camera to
        generalUpdate(delta, touch, camera);
        updatePositions(); //get previous positions for interpolation

        while (accumulator >= step) {
            world.box2d.step(step, 6, 2);
            accumulator -= step;
            interpolate((float) (accumulator / step));

        }
        world.box2d.clearForces();

        learnGame.batch.begin();

        if (tom.getBody().isActive()) {
            tomSprite.setSize(tom.getHeight(), tom.getHeight());
            tomSprite.setOriginCenter();
            tomSprite.setRotation(Assets.tom.angle);//this doesn't work right but not the point.
            tomSprite.setPosition(Assets.tom.pos.x, Assets.tom.pos.y);
            tomSprite.draw(learnGame.batch);
        }

        learnGame.batch.end();

        cameraUpdate(); //update the camera to the same position as the sprite

    } else { //else pause the game
        learnGame.batch.begin();
        learnGame.batch.draw(Assets.pauset, viewport.getCamera().position.x
                - (viewport.getCamera().viewportWidth / 2), viewport.getCamera().position.y
                - (viewport.getCamera().viewportHeight / 2), viewport.getWorldWidth(), viewport.getWorldHeight());
        learnGame.batch.end();
        if (Gdx.input.isKeyJustPressed(Keys.SPACE) || Gdx.input.justTouched()) { 
            paused = false;

        }

    }
    //destroy fixtures and bodies outside of the world step
    for (Fixture fixture : Bodies.fixturesToDestroy) {
        if (Bodies.destroyJoint == true) {
            world.box2d.destroyJoint(Bodies.joint);
            Bodies.joint = null;
            Bodies.destroyJoint = false;
        }
        fixture.getBody().destroyFixture(fixture);
        Bodies.fixturesToDestroy.removeValue(fixture, true);
    }

    for (Body body : Bodies.bodiesToDestroy) {
        world.box2d.destroyBody(body);
        body.setActive(false);
        Bodies.bodiesToDestroy.removeValue(body, true);
    }

}

@Override
public void show() {
    Assets.firstSound.play();

}

@Override
public void resize(int width, int height) {

    viewport.update(width, height, true);
    Assets.reloadFont();

}

@Override
public void pause() {
    paused = true;
}

@Override
public void resume() {
}

@Override
public void hide() {
    paused = true;

}

@Override
public void dispose() {
    Bodies.box2d.dispose();
    Bodies.debugRenderer.dispose();
    Buttons.stage.dispose();
    Assets.cFrame.getTexture().dispose();
    Assets.firstSound.dispose();
    System.out.println("disposed");
}

public void generalUpdate(float delta, Vector3 touch, OrthographicCamera camera) {
    if (Gdx.input.isKeyPressed(Keys.PAGE_UP)) {
        camera.zoom -= 2f * Gdx.graphics.getDeltaTime();
        ;
    } else if (Gdx.input.isKeyPressed(Keys.PAGE_DOWN)) {
        camera.zoom += 2f * Gdx.graphics.getDeltaTime();
    }
}

public static void cameraUpdate() {
    camera.position.set(Assets.tom.pos, 0);
    System.out.println("cam:" + Assets.tom.pos.x);
    camera.update();
}

public static void kill() {
    world.box2d.dispose();
    world.debugRenderer.dispose();
    Assets.cFrame.getTexture().dispose();
}

public void updatePositions() {
    for (MySprite name : Assets.spriteList) {
        name.prevPos = name.body.getTransform().getPosition();
        name.prevAngle = name.body.getTransform().getRotation();
    }

}

public void interpolate(float alpha) {
    for (MySprite name : Assets.spriteList) {
        name.pos.x = (name.body.getTransform().getPosition().x) * alpha + name.prevPos.x * (1.0f - alpha);
        name.pos.y = (name.body.getTransform().getPosition().y) * alpha + name.prevPos.y * (1.0f - alpha);
        name.angle = (name.body.getTransform().getRotation() * alpha + name.prevAngle * (1.0f - alpha));
    }
}
}

我在没有相机移动的情况下测试了我的插补实现,它似乎工作正常。我已经能够在我的桌面和我的机器人上测试它。精灵在我的机器人上“跳”了很多,但它发生在两个设备上。不知道我在哪里出错了,真的很感激一些意见!

1 个答案:

答案 0 :(得分:1)

这是一个老问题,我知道,但我今天刚刚解决了我的问题。

我已经实现了一个固定的Timestep和插值,但是我的身体滞后,抖动和跳跃就像没有明天一样。

请对相机进行以下操作:

  1. 使用身体的插值位置,而不是原始的身体位置。
  2. 使用camera.position.slerp(x,y,z,alpha)进行平滑移动。 (你需要对变量进行一些处理。
  3. 一个非常简单的例子:

    static Vector3 desiredPosition;
    static {
      desiredPosition = new Vector3(0, 0, 0);
    }
    public static moveCam(Player player, Camera camera){
        desiredPosition.x = player.interpolatedPos.x;
        desiredPosition.y = player.interpolatedPos.y;
        camera.slerp(desiredPosition, Gdx.graphics.getDeltaTime * 5);
        camera.update();
    }
    

    并在渲染中执行以下操作

    render(float delta){
        moveCam(player, camera);   
    }
    

    (即时位置设置导致我的移动物体出现抖动)

    我想放弃我的项目,因为这个bug。但我设法做到了。我希望它会帮助那里的人。

    €dit:猜测跳跃身体发生的原因: 插值位置与摄像机不同步(设置位置然后更新世界(现在插值pos用摄像机偏移),然后绘制) 但我对此并不确定。