Box2D 碰撞检测失败
Box2D Collision detection fails
所以我有一个我真的不明白的问题。在试图找出问题所在后,我决定记录一个 video 并在这里提问。
在视频中,请注意左下角的 True/False 布尔值。这是我的变量 canJump 的值。一开始,只需左右移动 "Player",值就会在 true 和 false 之间变化。当玩家上下斜坡时也会发生这种情况。
map/collision 层是用 Tiled 创建的。
我的 TiledObject class:
public class TiledObjectUtil {
public static float PPM = 32;
public static void parseTiledObjectLayer(World world, MapObjects objects) {
for(MapObject object : objects) {
Shape shape;
if(object instanceof PolylineMapObject) {
shape = createPolyLine((PolylineMapObject) object);
} else {
continue;
}
Body body;
BodyDef bdef = new BodyDef();
bdef.type = BodyDef.BodyType.StaticBody;
body = world.createBody(bdef);
body.createFixture(shape, 1.0f);
shape.dispose();
}
}
private static ChainShape createPolyLine(PolylineMapObject polyline) {
float[] vertices = polyline.getPolyline().getTransformedVertices();
Vector2[] worldVertices = new Vector2[vertices.length / 2];
for(int i = 0; i<worldVertices.length; i++) {
worldVertices[i] = new Vector2(vertices[i * 2] / PPM, vertices[i*2+1] / PPM);
}
ChainShape cs = new ChainShape();
cs.createChain(worldVertices);
return cs;
}}
还有我的玩家class:
public class Player {
private BodyDef def = new BodyDef();
public Body playerBody;
private float speed = 10;
public Player() {
}
public void update() {
if (InputUtil.moveLeft) {
playerBody.setLinearVelocity(-speed, playerBody.getLinearVelocity().y);
}
if (InputUtil.moveRight) {
playerBody.setLinearVelocity(speed, playerBody.getLinearVelocity().y);
}
if (!InputUtil.moveLeft && !InputUtil.moveRight) {
playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);
}
}
public void jump() {
if (ContactUtil.canJump) {
playerBody.applyLinearImpulse(0, 80, 0, 0, true);
}
}
public Body createPlayer(World world) {
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(20, 20);
def.fixedRotation = true;
playerBody = world.createBody(def);
PolygonShape shape = new PolygonShape();
shape.setAsBox(2f / 2, 2f / 2);
FixtureDef playerFixture = new FixtureDef();
playerFixture.density = 1f;
playerFixture.shape = shape;
playerFixture.restitution = 0f;
playerFixture.friction = 1f;
playerBody.createFixture(playerFixture);
shape.setAsBox(2f / 2, 1f / 2, new Vector2(0, 0 - 1), 0);
playerFixture.shape = shape;
playerFixture.isSensor = true;
playerBody.createFixture(playerFixture).setUserData("player");
shape.dispose();
return playerBody;
} }
最后,我的 ContactListener:
public class ContactUtil implements ContactListener {
public static boolean canJump;
public ContactUtil() {
}
@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());
if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
canJump = true;
}
if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
canJump = true;
}
}
@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());
if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
canJump = false;
}
if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
canJump = false;
}
}
此外,我的播放器每次停在斜坡上时都会跳动一点。我知道这是由于这一行:playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);
如果有人知道更好的处理运动的方法,将不胜感激。
我好像记得以前遇到过这个确切的问题!我认为问题出在你在 beginContact
和 endContact
.
中的逻辑上
我会用我粗糙的 MS Paint 技能来解释这个问题。想象一下以下情况:您的玩家在空中,向下方的平台坠落。如您所料,canJump
为假:
玩家掉落并着陆,beginContact
平台一被调用。这会将 canJump
设置为 true
。同样,这是我们所期望的:
现在玩家向右移动,直到他们接触到二号平台。 beginContact
调用平台二。再一次,canJump
被设置为 true。
这就是问题所在。最后一步,玩家仍与一号平台保持联系。但现在他们不是,所以 endContact
与平台一被称为。 canJump
现在设置为 false
。这解释了您遇到的意外行为。
解决方法很简单。您需要维护播放器底部接触的联系人列表。在这篇 iforce2d 文章中有一个指南可以做到这一点:http://www.iforce2d.net/b2dtut/jumpability
祝你好运!
所以我有一个我真的不明白的问题。在试图找出问题所在后,我决定记录一个 video 并在这里提问。
在视频中,请注意左下角的 True/False 布尔值。这是我的变量 canJump 的值。一开始,只需左右移动 "Player",值就会在 true 和 false 之间变化。当玩家上下斜坡时也会发生这种情况。
map/collision 层是用 Tiled 创建的。
我的 TiledObject class:
public class TiledObjectUtil {
public static float PPM = 32;
public static void parseTiledObjectLayer(World world, MapObjects objects) {
for(MapObject object : objects) {
Shape shape;
if(object instanceof PolylineMapObject) {
shape = createPolyLine((PolylineMapObject) object);
} else {
continue;
}
Body body;
BodyDef bdef = new BodyDef();
bdef.type = BodyDef.BodyType.StaticBody;
body = world.createBody(bdef);
body.createFixture(shape, 1.0f);
shape.dispose();
}
}
private static ChainShape createPolyLine(PolylineMapObject polyline) {
float[] vertices = polyline.getPolyline().getTransformedVertices();
Vector2[] worldVertices = new Vector2[vertices.length / 2];
for(int i = 0; i<worldVertices.length; i++) {
worldVertices[i] = new Vector2(vertices[i * 2] / PPM, vertices[i*2+1] / PPM);
}
ChainShape cs = new ChainShape();
cs.createChain(worldVertices);
return cs;
}}
还有我的玩家class:
public class Player {
private BodyDef def = new BodyDef();
public Body playerBody;
private float speed = 10;
public Player() {
}
public void update() {
if (InputUtil.moveLeft) {
playerBody.setLinearVelocity(-speed, playerBody.getLinearVelocity().y);
}
if (InputUtil.moveRight) {
playerBody.setLinearVelocity(speed, playerBody.getLinearVelocity().y);
}
if (!InputUtil.moveLeft && !InputUtil.moveRight) {
playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);
}
}
public void jump() {
if (ContactUtil.canJump) {
playerBody.applyLinearImpulse(0, 80, 0, 0, true);
}
}
public Body createPlayer(World world) {
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(20, 20);
def.fixedRotation = true;
playerBody = world.createBody(def);
PolygonShape shape = new PolygonShape();
shape.setAsBox(2f / 2, 2f / 2);
FixtureDef playerFixture = new FixtureDef();
playerFixture.density = 1f;
playerFixture.shape = shape;
playerFixture.restitution = 0f;
playerFixture.friction = 1f;
playerBody.createFixture(playerFixture);
shape.setAsBox(2f / 2, 1f / 2, new Vector2(0, 0 - 1), 0);
playerFixture.shape = shape;
playerFixture.isSensor = true;
playerBody.createFixture(playerFixture).setUserData("player");
shape.dispose();
return playerBody;
} }
最后,我的 ContactListener:
public class ContactUtil implements ContactListener {
public static boolean canJump;
public ContactUtil() {
}
@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());
if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
canJump = true;
}
if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
canJump = true;
}
}
@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());
if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
canJump = false;
}
if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
canJump = false;
}
}
此外,我的播放器每次停在斜坡上时都会跳动一点。我知道这是由于这一行:playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);
如果有人知道更好的处理运动的方法,将不胜感激。
我好像记得以前遇到过这个确切的问题!我认为问题出在你在 beginContact
和 endContact
.
我会用我粗糙的 MS Paint 技能来解释这个问题。想象一下以下情况:您的玩家在空中,向下方的平台坠落。如您所料,canJump
为假:
玩家掉落并着陆,beginContact
平台一被调用。这会将 canJump
设置为 true
。同样,这是我们所期望的:
现在玩家向右移动,直到他们接触到二号平台。 beginContact
调用平台二。再一次,canJump
被设置为 true。
这就是问题所在。最后一步,玩家仍与一号平台保持联系。但现在他们不是,所以 endContact
与平台一被称为。 canJump
现在设置为 false
。这解释了您遇到的意外行为。
解决方法很简单。您需要维护播放器底部接触的联系人列表。在这篇 iforce2d 文章中有一个指南可以做到这一点:http://www.iforce2d.net/b2dtut/jumpability
祝你好运!