2018年2月3日土曜日

Processingのbox2dで複数のポリゴンシェイプを組み合わせた場合の表示

Processingにも物理計算をしてくれるライブラリのbox2dがあります。

このライブラリはNature of code、(この教材は素晴らしいもので、0円で入手できる。)でも紹介されている(というよりそのライブラリを作ったヒトが本の作者)のだけれど、box2dは大きなライブラリなので省かれている部分も多いですね。

例えば、polygon shapeを組み合わせてbox2d上の剛体を作った場合に、用意されているサンプルでは組み合わせ順で、最後にcreateFixtureをしたフィクスチャしか表示してくれない。

これはサンプルプログラムのdisplay関数がポリゴンの組み合わせを想定していないため。

わかっているヒトを前提として書きますが、
Fixture f = body.getFixureList();
でとれるフィクスチャは最後に加えたものだけです。
そのフィクスチャの座標だけを描画してしまうので、透明な物体が出来上がってしまう。

であれば、bodyにくっついている他のフィクスチャ(ポリゴン)の座標情報を取り出して、描画したい場合にはf = f.getNext();してボディについている次のフィクスチャを指定する必要があります。ただし、ボディについているフィクスチャの数をlengthやgetSize();で取ってくることができないので、getNext();してみて、nullでないかどうか確認して取ってくる必要があります。

具体的に、二つのポリゴンを組み合わせて描画する場合のprocessing の box2dサンプルにふくまれるPolygonsのCustamShapeというファイルを公開します。
displayの中のwhile文の中身を新しく足しました。

// The Nature of Code
// 
// Spring 2011
// Box2DProcessing example

// A rectangular box
class CustomShape {

  // We need to keep track of a Body and a width and height
  Body body;

  // Constructor
  CustomShape(float x, float y) {
    // Add the box to the box2d world
    makeBody(new Vec2(x, y));
  }

  // This function removes the particle from the box2d world
  void killBody() {
    box2d.destroyBody(body);
  }

  // Is the particle ready for deletion?
  boolean done() {
    // Let's find the screen position of the particle
    Vec2 pos = box2d.getBodyPixelCoord(body);
    // Is it off the bottom of the screen?
    if (pos.y > height) {
      killBody();
      return true;
    }
    return false;
  }

  // Drawing the box
  void display() {
    // We look at each body and get its screen position
    Vec2 pos = box2d.getBodyPixelCoord(body);
    // Get its angle of rotation
    float a = body.getAngle();

    Fixture f = body.getFixtureList();
    PolygonShape ps =  (PolygonShape) f.getShape();
    
    while(true){
      ps = (PolygonShape) f.getShape();
      rectMode(CENTER);
      pushMatrix();
      translate(pos.x, pos.y);
      rotate(-a);
      fill(175);
      stroke(0);
      beginShape();
      //println(vertices.length);
      // For every vertex, convert to pixel vector
      //while(f.getNext() == null){
        for (int i = 0; i < ps.getVertexCount(); i++) {
          Vec2 v = box2d.vectorWorldToPixels(ps.getVertex(i));
          vertex(v.x, v.y);
        }
      endShape(CLOSE);
      popMatrix();
      println(f);
      //f = f.getNext();
      if(f.getNext() == null){
        break;
      }else{
        f = f.getNext();
      }
      
    } 
  }
  // This function adds the rectangle to the box2d world
  void makeBody(Vec2 center) {

    // Define a polygon (this is what we use for a rectangle)
    PolygonShape sd1 = new PolygonShape();

    Vec2[] vertices1 = new Vec2[4];
    vertices1[0] = box2d.vectorPixelsToWorld(new Vec2(0, 3.5));
    vertices1[1] = box2d.vectorPixelsToWorld(new Vec2(10, 6));
    vertices1[2] = box2d.vectorPixelsToWorld(new Vec2(15, 8));
    vertices1[3] = box2d.vectorPixelsToWorld(new Vec2(20, 30.8));

    sd1.set(vertices1, vertices1.length);
    
    PolygonShape sd2 = new PolygonShape();
    Vec2[] vertices2 = new Vec2[4];
    vertices2[0] = box2d.vectorPixelsToWorld(new Vec2(0, -56));
    vertices2[1] = box2d.vectorPixelsToWorld(new Vec2(-5, -6));
    vertices2[2] = box2d.vectorPixelsToWorld(new Vec2(-30, -8));
    vertices2[3] = box2d.vectorPixelsToWorld(new Vec2(-50, -30.8));
    sd2.set(vertices2, vertices2.length);

    // Define the body and make it from the shape
    BodyDef bd = new BodyDef();
    bd.type = BodyType.DYNAMIC;
    bd.position.set(box2d.coordPixelsToWorld(center));
    body = box2d.createBody(bd);

    body.createFixture(sd2, 1.0);
    body.createFixture(sd1, 1.0);
  }
}

Processingでbox2dやってみたいヒトは多そうだけれどこの情報があまりなかったし、この部分で引っかかってたヒトの質問にだれも答えてなかったみたいなので書きました。結構前の質問みたいだったけど、届くといいですね。

https://forum.processing.org/one/topic/jbox2d-problem-i-cannot-render-draw-all-the-polygons-that-are-attached-to-the-body-in-my-code.html

0 件のコメント:

コメントを投稿