Java水平世界2D物理引擎对象组碰撞排除实现咨询
嘿,这个需求其实在2D物理引擎里挺常见的,jbox2d和dyn4j都有现成的方案解决,只是可能你没挖到它们的碰撞过滤机制而已。我来给你拆解几种可行的办法:
方案1:用内置的类别/掩码(Category & Mask)过滤(最推荐)
这是两个引擎都支持的原生碰撞过滤方案,性能最优,适合静态的碰撞规则(比如你说的子弹碰盒子、飞机穿盒子但碰子弹这种固定规则)。
核心逻辑是给每个物体的Fixture分配:
- Category Bits:标记当前物体属于哪个类别(用16位二进制数表示,每个位代表一个类别)
- Mask Bits:标记当前物体可以和哪些类别的物体碰撞(同样用16位二进制数,位为1表示允许碰撞)
举个对应你需求的例子:
- 子弹:类别
0x0001(二进制0001),掩码0x0002 | 0x0004(允许和盒子、飞机碰撞) - 盒子:类别
0x0002(二进制0010),掩码0x0001(只允许和子弹碰撞) - 飞机:类别
0x0004(二进制0100),掩码0x0001(只允许和子弹碰撞)
JBox2D 代码示例
// 定义类别常量 public static final short CATEGORY_BULLET = 0x0001; public static final short CATEGORY_BOX = 0x0002; public static final short CATEGORY_PLANE = 0x0004; // 配置子弹的Fixture FixtureDef bulletFixtureDef = new FixtureDef(); bulletFixtureDef.shape = new CircleShape(0.5f); // 子弹形状 bulletFixtureDef.filter.categoryBits = CATEGORY_BULLET; bulletFixtureDef.filter.maskBits = CATEGORY_BOX | CATEGORY_PLANE; // 和盒子、飞机碰撞 // 配置盒子的Fixture FixtureDef boxFixtureDef = new FixtureDef(); boxFixtureDef.shape = new PolygonShape(); ((PolygonShape) boxFixtureDef.shape).setAsBox(2f, 2f); // 盒子形状 boxFixtureDef.filter.categoryBits = CATEGORY_BOX; boxFixtureDef.filter.maskBits = CATEGORY_BULLET; // 只和子弹碰撞 // 配置飞机的Fixture FixtureDef planeFixtureDef = new FixtureDef(); planeFixtureDef.shape = new PolygonShape(); ((PolygonShape) planeFixtureDef.shape).setAsBox(5f, 1f); // 飞机形状 planeFixtureDef.filter.categoryBits = CATEGORY_PLANE; planeFixtureDef.filter.maskBits = CATEGORY_BULLET; // 只和子弹碰撞
dyn4j 代码示例
// 定义类别常量 public static final short CATEGORY_BULLET = 0x0001; public static final short CATEGORY_BOX = 0x0002; public static final short CATEGORY_PLANE = 0x0004; // 配置子弹的Fixture CircleShape bulletShape = new CircleShape(0.5); Fixture bulletFixture = new Fixture(bulletShape); Filter bulletFilter = new Filter(); bulletFilter.setCategory(CATEGORY_BULLET); bulletFilter.setMask(CATEGORY_BOX | CATEGORY_PLANE); bulletFixture.setFilter(bulletFilter); Body bulletBody = new Body(); bulletBody.addFixture(bulletFixture); // 配置盒子的Fixture RectangleShape boxShape = new RectangleShape(2, 2); Fixture boxFixture = new Fixture(boxShape); Filter boxFilter = new Filter(); boxFilter.setCategory(CATEGORY_BOX); boxFilter.setMask(CATEGORY_BULLET); boxFixture.setFilter(boxFilter); Body boxBody = new Body(); boxBody.addFixture(boxFixture); // 配置飞机的Fixture RectangleShape planeShape = new RectangleShape(5, 1); Fixture planeFixture = new Fixture(planeShape); Filter planeFilter = new Filter(); planeFilter.setCategory(CATEGORY_PLANE); planeFilter.setMask(CATEGORY_BULLET); planeFixture.setFilter(planeFilter); Body planeBody = new Body(); planeBody.addFixture(planeFixture);
方案2:组索引(Group Index)快速分组排除
如果你的规则是简单的“某一组物体互相不碰撞”,可以用组索引来快速实现。组索引的优先级比类别/掩码高:
- 正组索引:同一组的物体必须碰撞
- 负组索引:同一组的物体永远不碰撞
比如把飞机和盒子设为同一负组,子弹设为不同组,就能实现飞机和盒子不碰撞,但都和子弹碰撞:
JBox2D 示例
planeFixtureDef.filter.groupIndex = -1; boxFixtureDef.filter.groupIndex = -1; bulletFixtureDef.filter.groupIndex = 0; // 0表示不参与组规则,按类别/掩码判断
dyn4j 示例
planeFilter.setGroup(-1); boxFilter.setGroup(-1); bulletFilter.setGroup(0);
方案3:自定义碰撞监听器(复杂动态规则必备)
如果你的碰撞规则是动态的(比如飞机在某些状态下可以碰盒子,某些状态下不行),就需要自定义碰撞监听器,在碰撞发生前手动判断是否允许碰撞。
JBox2D 示例
world.setContactListener(new ContactListener() { @Override public boolean shouldCollide(Fixture fixtureA, Fixture fixtureB) { // 从Body的UserData中获取物体类型(你需要提前给Body设置UserData) Object dataA = fixtureA.getBody().getUserData(); Object dataB = fixtureB.getBody().getUserData(); // 自定义规则:飞机和盒子不碰撞,其他情况允许 if ((dataA instanceof Plane && dataB instanceof Box) || (dataA instanceof Box && dataB instanceof Plane)) { return false; // 阻止碰撞 } return true; // 允许其他所有碰撞 } // 其他接口方法可以留空或默认实现 @Override public void beginContact(Contact contact) {} @Override public void endContact(Contact contact) {} @Override public void preSolve(Contact contact, Manifold oldManifold) {} @Override public void postSolve(Contact contact, ContactImpulse impulse) {} });
dyn4j 示例
world.addCollisionListener(new CollisionListener() { @Override public boolean collision(PhysicsBody bodyA, PhysicsBody bodyB) { // 判断物体类型 if ((bodyA instanceof PlaneBody && bodyB instanceof BoxBody) || (bodyA instanceof BoxBody && bodyB instanceof PlaneBody)) { return false; // 阻止碰撞 } return true; // 允许其他碰撞 } // 其他接口方法按需实现 });
总结
- 优先用类别/掩码:性能最好,适合静态规则
- 简单分组用组索引:代码更简洁
- 复杂动态规则用自定义监听器:灵活性最高,但性能略低(因为是Java层判断)
内容的提问来源于stack exchange,提问作者PhilipRoman
相关产品推荐
相关产品推荐

