1. 程式人生 > >3.Libgdx擴充套件學習之Box2D_夾具

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;
        }
    }
}

這裡寫圖片描述