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);
我正在通过 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);