Box2D 线速度导致两个接触体之间产生间隙

Box2D Linear Velocity Causes Gap Between Two Touching Bodies

我正在通过 LibGDX 使用 Box2D 开发一个 Android 项目,其中我计划拥有多个可以在屏幕上拖动和抛出的动态物体。屏幕上也会有一些静态物体,绝对不允许动态物体通过它们。我编写了一个简单的应用程序,其中包含一个静态 body 平台和一个动态 body 方块,作为第一次尝试落在平台上。

盒子的行为似乎有点问题。当我启动这个应用程序时,重力将盒子从屏幕顶部拉到它所在的平台上。如果我触摸盒子并尝试将其向下拖过平台,当我松开手指时,盒子实际上会向上漂浮一点并向上弹射(有时它甚至会在向上弹射之前快速来回旋转)。但是,如果我四处拖动盒子并手动将其放在平台上,然后尝试将盒子拉过平台,它的行为与我预期的一样,因为当我松开手指时它根本不会移动并且不会向上弹.该问题仅在重力负责将其拉到平台时显示。我的问题如下:

当我尝试将它拖过平台时,为什么盒子自然停在平台上后 levitate/rotate?

当我稍微拖动它然后尝试将它拖过平台时,为什么盒子表现正确(静止不动)?

目前,我将盒子上的恢复原状设置为每当它被触摸时为零,并在它被释放时重置为 0.3 并且出现此问题。奇怪的是,如果我在创建框时将恢复原状设置为零并保持原样,这个问题似乎完全消失了。不过,我不想将恢复原状设置为零。

我在这个屏幕截图中的框(默认的 Bad Logic 徽标)上向下拖动。注意它和平台之间的差距。 Screenshot Image

这是我的代码。任何帮助将不胜感激! 包裹 com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
    private static float VIEWPORT_WIDTH = 4.8f;
    private static float VIEWPORT_HEIGHT = 8f;
    private static float PPM = 300f;
    private static float BOX_RESTITUTION = 0.3f;

    World world;
    OrthographicCamera camera;
    Box2DDebugRenderer renderer;
    Body squareBody;
    SpriteBatch batch;
    Sprite box;
    boolean draggingBox = false;
    Vector2 touchPoint;

    @Override
    public void create () {
        Gdx.input.setInputProcessor(this);

        this.batch = new SpriteBatch();
        this.world = new World(new Vector2(0f, -9.81f), true);
        this.renderer = new Box2DDebugRenderer();
        this.camera = new OrthographicCamera();
        this.camera.setToOrtho(false, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);

        // Platform
        BodyDef platformBodyDef = new BodyDef();
        platformBodyDef.type = BodyDef.BodyType.StaticBody;
        platformBodyDef.position.set(VIEWPORT_WIDTH / 2, 2f);
        Body platformBody = this.world.createBody(platformBodyDef);
        PolygonShape platformShape = new PolygonShape();
        platformShape.setAsBox(VIEWPORT_WIDTH / 2 - 0.2f, 0.5f / 2);
        FixtureDef platformFixtureDef = new FixtureDef();
        platformFixtureDef.shape = platformShape;
        platformFixtureDef.friction = 1f;
        platformFixtureDef.restitution = 0f;
        platformFixtureDef.density = 3f;
        platformBody.createFixture(platformFixtureDef);

        // Box sprite
        this.box = new Sprite(new Texture("badlogic.jpg"));

        // Square
        BodyDef squareBodyDef = new BodyDef();
        squareBodyDef.type = BodyDef.BodyType.DynamicBody;
        squareBodyDef.position.set(VIEWPORT_WIDTH / 2, 7.5f);
        this.squareBody = this.world.createBody(squareBodyDef);
        PolygonShape squareShape = new PolygonShape();
        squareShape.setAsBox(this.box.getWidth() / PPM / 2, this.box.getHeight() / PPM / 2);
        FixtureDef squareFixtureDef = new FixtureDef();
        squareFixtureDef.shape = squareShape;
        squareFixtureDef.friction = 1f;
        squareFixtureDef.restitution = BOX_RESTITUTION;
        squareFixtureDef.density = 1f;
        this.squareBody.createFixture(squareFixtureDef);
    }

    @Override
    public void render () {
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        this.camera.update();
        this.world.step(1/60f, 6, 2);
        this.renderer.render(this.world, this.camera.combined);

        // Set the velocity so the box will follow the users finger when dragged.
        if (this.draggingBox == true) {
            Vector2 velocity = this.touchPoint.cpy();
            velocity.sub(new Vector2(this.box.getX() / PPM, this.box.getY() / PPM));
            velocity.scl(10f);
            this.squareBody.setLinearVelocity(velocity);
        }

        // Update the position/rotation of the box sprite to match the physics body.
        this.box.setPosition(
                this.squareBody.getPosition().x * PPM - (this.box.getWidth() / 2),
                this.squareBody.getPosition().y * PPM - (this.box.getHeight() / 2));
        this.box.setRotation((float) Math.toDegrees(this.squareBody.getAngle()));

        this.batch.begin();
        this.box.draw(this.batch);
        this.batch.end();
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        Vector3 touchPoint3D = this.camera.unproject(new Vector3(screenX, screenY, 0));
        Vector2 touchPoint2D = new Vector2(touchPoint3D.x, touchPoint3D.y);

        // Get the box boundary and convert it to meters.
        Rectangle boxBoundary = this.box.getBoundingRectangle();
        boxBoundary.setX(boxBoundary.getX() / PPM);
        boxBoundary.setY(boxBoundary.getY() / PPM);
        boxBoundary.setWidth(boxBoundary.getWidth() / PPM);
        boxBoundary.setHeight(boxBoundary.getHeight() / PPM);

        // If the box boundary contains the touch point, start the dragging and remove restitution.
        if (boxBoundary.contains(touchPoint2D)) {
            this.draggingBox = true;
            this.touchPoint = touchPoint2D;
            this.squareBody.getFixtureList().get(0).setRestitution(0);
        }

        return true;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        this.draggingBox = false;
        this.touchPoint = null;
        this.squareBody.getFixtureList().get(0).setRestitution(BOX_RESTITUTION);
        return true;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        Vector3 touchPoint3D = this.camera.unproject(new Vector3(screenX, screenY, 0));
        this.touchPoint = new Vector2(touchPoint3D.x, touchPoint3D.y);

        return true;
    }

    @Override
    public void dispose () {
        this.box.getTexture().dispose();
        batch.dispose();
    }

    @Override
    public boolean keyDown(int keycode) {
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        return false;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}

在创建夹具后,Box2D 似乎不能很好地处理夹具内的恢复变化。这一行是罪魁祸首:

this.squareBody.getFixtureList().get(0).setRestitution(0);

不过,我找到了解决此问题的方法。如果我从 body 中销毁夹具,创建一个具有所需恢复原状的新夹具,并将新夹具添加到 body,这个问题就会消失。类似于下面的内容(尽管您可能想将 Fixture 创建提取到一个新函数中):

// Destroy the current Fixture that exists on the Body. Multiple Fixtures can be assigned, but in this case there is only one.
this.squareBody.destroyFixture(this.squareBody.getFixtureList().get(0));

// Create a new shape to define a new Fixture.
PolygonShape squareShape = new PolygonShape();
squareShape.setAsBox(this.box.getWidth() / PPM / 2, this.box.getHeight() / PPM / 2);

// Create a new Fixture with the desired restitution value.
FixtureDef squareFixtureDef = new FixtureDef();
squareFixtureDef.shape = squareShape;
squareFixtureDef.friction = 1f;
squareFixtureDef.restitution = 0;
squareFixtureDef.density = 1f;

// Add the new Fixture to the Body.
this.squareBody.createFixture(squareFixtureDef);