3.Libgdx擴充套件學習之Box2D_夾具
文章中涉及的很多概念,都是來自《Box2D中文手冊》。有統一的解釋方便理解
夾具
概念介紹
形狀不知道物體的存在,並可獨立於物理模擬而被使用。因此 Box2D 提供 Fixture 類,用於將形狀附加到物體上。一個物體可以有零個或多個 fixture。擁有多個 fixture 的物體有時被叫做組合物體。
fixture具有下列屬性:
- 關聯的形狀
- broad-phase 代理
- 密度(density)、摩擦(friction)、恢復(restitution)
- 碰撞篩選標記(collision filtering flags)
- 指向父物體的指標
- 感測器標記(sensor flag)
建立夾具
建立夾具,必須先初始化和夾具定義FixtureDef,並將定義傳到父物體中,
先看夾具定義:
/** A fixture definition is used to create a fixture. This class defines an abstract fixture definition. You can reuse fixture
* definitions safely.
* @author mzechner */
public class FixtureDef {
/** The shape, this must be set. The shape will be cloned, so you can create the shape on the stack. */
public Shape shape;
/** The friction coefficient, usually in the range [0,1]. **/
public float friction = 0.2f;
/** The restitution (elasticity) usually in the range [0,1]. **/
public float restitution = 0;
/** The density, usually in kg/m^2. **/
public float density = 0;
/** A sensor shape collects contact information but never generates a collision response. */
public boolean isSensor = false;
/** Contact filtering data. **/
public final Filter filter = new Filter();
}
上面列出的FixtureDef屬性,其實就是上面文字描述的程式碼形式,在實際操作中我們是這樣建立的:
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circleShape;
fixtureDef.density = 1.0f;
fixtureDef.restitution = 0.6f;
body.createFixture(fixtureDef);
這會建立 fixture,並將它附加到物體之上。不需要儲存 fixture 的指標,因為當它的父物體被摧毀時, fixture 也會自動被摧毀。 可以在單個物體上建立多個 fixture。
可以摧毀父物體上的 fixture,來模擬一個可分裂開的物體。
FixtureDef屬性介紹
密度(density)
fixture 的密度用來計算父物體的質量屬性。密度值可以為零或者是正數。所有的 fixture 都應該使用相似的密度,這樣做可以改善堆疊穩定性。
當設定密度的時候,物體的質量不會立即改變。你必須呼叫 resetMassData ,使之生效。
fixture.setDensity (float density)
body.resetMassData ()
摩擦(friction)
摩擦可以使物件逼真地沿其它物件滑動。摩擦引數通常會設定在 0 到 1 之間,但也可是任意的非負數, 0 意味著沒有摩擦, 1 會產生強摩擦。
恢復係數(restitution)
恢復可以使物件彈起。恢復的值通常設定在 0 到 1 之間。想象一個小球掉落到桌子上,值為 0 表示著小球不會彈起, 這稱為非彈性碰撞。值為 1 表示小球的速度跟原來一樣,只是方向相反,這稱為完全彈性碰撞。
篩選(filter)
碰撞篩選是為了防止某些 fixture 之間發生碰撞。比如,創造了一個騎自行車的角色。希望自行車與地形之間有碰撞,角色與地形有碰撞,但你不希望角色和自行車之間發生碰撞 (因為它們必須重疊)。 Box2D 通過種群和分組來支援這樣的碰撞篩選。
Box2D 支援 16 個種群。任意 fixture 你都可以指定它屬於哪個種群。你還可以指定這個 fixture 可以和其它哪些種群發生碰撞。例如,你可以在一個多人遊戲中指定玩家之間不會碰撞,怪物之間也不會碰撞,但是玩家和怪物會發生碰撞。這是通過掩碼來完成的,例如:
playerFixtureDef.filter.categoryBits = 0x0002
monsterFixtureDef.filter.categoryBits = 0x0004
playerFixtureDef.filter.maskBits = 0x0004
monsterFixtureDef.filter.maskBits = 0x0002
下面是產生碰撞的規則:
short catA = fixtureA.filter.categoryBits ;
short maskA = fixtureA.filter.maskBits;
short catB = fixtureB.filter.categoryBits
short maskB = fixtureB.filter.maskBits;
if((catA & catB) != 0 && (catB & maskA) != 0) {
// fixtures can collide
}
碰撞分組讓你指定一個整數的組索引。你可以融同一個組的所有fixture總是相互碰撞(正索引)或者永遠不碰撞(負索引)。組索引通常用於一些以某種方式關聯的事物,就像自行車的那些不見。在下面例子中,fixture1和fixture2總是碰撞,而fixture3和fixture4永遠不會碰撞
fixture1Def.filter.groupIndex = 2;
fixture2Def.filter.groupIndex = 2;
fixture3Def.filter.groupIndex = -8;
fixture4Def.filter.groupIndex = -8;
如果組索引不同,碰撞篩選會按照種群和掩碼來進行。換句話說,分組篩選和種群篩選相比,具有更高的優選級。
注意在Box2D中還有其它的碰撞篩選,這裡有一個列表:
- static上的fixture只會與dynamic物體上的fixture發生碰撞
- kinematic物體只會和dynamic物體發生碰撞
- 同一個物體上的fixture永遠不會相互碰撞
- 如果兩個物體用關節連線起來,物體上面的fixture可以選擇啟用或者禁止它們之間的相互碰撞
感測器
有時候遊戲邏輯需要判斷兩個 fixture 是否相交,而不想有碰撞反應。這可以通過感測器(sensor)來完成。感測器也是個 fixture,但它只會偵測碰撞,而不產生其它反應。
可以將任意 fixture 標記為感測器。感測器可以是 static、 kinematic 或 dynamic 的。記住,每個物體上可以有多個 fixture,感測器和實體 fixture 是可以混合存在的。而且,只有至少一個物體是dynamic 的,感測器才會產生接觸事件,而 kinematic 與 kinematic 、 kinematic 與 static ,或者static 與 static 之間都不會產生接觸事件。
public class BodyShapeA extends ApplicationAdapter {
World world;
Box2DDebugRenderer box2DDebugRenderer;
OrthographicCamera camera;
float scene_width = 12.8f;
float scene_height = 7.2f;
@Override
public void create() {
world = new World(new Vector2(0.0f, -9.8f), true);
box2DDebugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(scene_width, scene_height);
camera.position.set(scene_width / 2, scene_height / 2, 0);
camera.update();
createStaticBox();
Gdx.input.setInputProcessor(new InputHandA());
}
@Override
public void render() {
world.step( 1/ 60f, 6, 2);
Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
box2DDebugRenderer.render(world, camera.combined);
}
@Override
public void dispose() {
world.dispose();
box2DDebugRenderer.dispose();
}
private void createStaticBox() {
// 建立地面
createBorderBox(scene_width * 0.5f, 0.2f, scene_width * 0.5f, 0.2f, 0);
// 建立傾斜矩形,這裡建立矩形,然後用angle來控制
createBorderBox(3.0f, 5.0f, 0.1f, 1.3f, MathUtils.PI / 3);
// 建立池子
createBorderBox(5.0f, 2.5f, 0.1f, 1.0f, 0);
createBorderBox(9.0f, 2.5f, 0.1f, 1.0f, 0);
createBorderBox(7.0f, 1.6f, 2.1f, 0.1f, 0);
}
private void createBorderBox(float px, float py, float w, float h, float angle) {
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(px, py);
bodyDef.angle = angle;
Body body = world.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(w, h);
body.createFixture(polygonShape, 0);
polygonShape.dispose();
}
private void createCircle(float x, float y) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(x, y);
Body body = world.createBody(bodyDef);
CircleShape circleShape = new CircleShape();
circleShape.setRadius(0.2f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circleShape;
fixtureDef.density = 1.0f;
fixtureDef.restitution = 0.6f;
body.createFixture(fixtureDef);
circleShape.dispose();
}
private class InputHandA extends InputAdapter {
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
// 在螢幕點擊出建立響應的圓形Body
Vector3 vector3 = new Vector3(screenX, screenY, 0);
camera.unproject(vector3);
createCircle(vector3.x, vector3.y);
return true;
}
}
}