「The Nature of Code」から衝突イベントを別クラスに切り分けることについて取り上げます。今までは、実行用のクラスに衝突イベントの処理を書いていました。ここでは、衝突イベントを別クラスに切り分けていきます。Processingでプログラムを書いて、動作を確認します。動作を確認できるところがProcessingの楽しいところです。
衝突イベントを別クラスに切り分ける
CustomListenerクラスに衝突イベントを切り分けます。以下は、その参考例です。
//オブジェクトをコントロールする基本例 //どのオブジェクトがヒットしたか知る方法 import shiffman.box2d.*; import org.jbox2d.common.*; import org.jbox2d.dynamics.joints.*; import org.jbox2d.collision.shapes.*; import org.jbox2d.collision.shapes.Shape; import org.jbox2d.dynamics.*; import org.jbox2d.dynamics.contacts.*; Box2DProcessing box2d; Box box; ArrayList<Particle> particles; Spring spring; //パーリンノイズの値 float xoff = 0; float yoff = 1000; void setup(){ size(400, 300); box2d = new Box2DProcessing(this); box2d.createWorld(); //衝突をリッスンするリスナー box2d.world.setContactListener(new CustomListener()); box = new Box(width/2, height/2); spring = new Spring(); spring.bind(width/2, height/2, box); particles = new ArrayList<Particle>(); } void draw(){ background(255); box2d.step(); //パーティクルの生成・追加 if(random(1) < 0.2){ float sz = random(4, 8); particles.add(new Particle(width/2, -20, sz)); } //パーリンノイズから座標を作る float x = noise(xoff)*width; float y = noise(yoff)*height; xoff += 0.01; yoff += 0.01; if(mousePressed){ spring.update(mouseX, mouseY); spring.display(); }else{ spring.update(x, y); } //パーティクルに対する処理 for(int i = particles.size()-1; i >= 0; i--){ Particle p = particles.get(i); p.display(); if(p.done()){ particles.remove(i); } } box.display(); }
class Box{ Body body; float w; float h; //コンストラクタ Box(float x_, float y_){ float x = x_; float y = y_; w = 24; h = 24; makeBody(new Vec2(x, y), w, h); body.setUserData(this); } //図形の描画 void display(){ Vec2 pos = box2d.getBodyPixelCoord(body); float a = body.getAngle(); rectMode(PConstants.CENTER); pushMatrix(); translate(pos.x, pos.y); rotate(-a); fill(175); stroke(0); rect(0, 0, w, h); popMatrix(); } void makeBody(Vec2 center, float w_, float h_){ //ボディの定義 BodyDef bd = new BodyDef(); bd.type = BodyType.DYNAMIC; bd.position.set(box2d.coordPixelsToWorld(center)); body = box2d.createBody(bd); //シェイプの定義 PolygonShape sd = new PolygonShape(); float box2dW = box2d.scalarPixelsToWorld(w_/2); float box2dH = box2d.scalarPixelsToWorld(h_/2); sd.setAsBox(box2dW, box2dH); //フィクスチャの定義 FixtureDef fd = new FixtureDef(); fd.shape = sd; //物理関係のパラメータ fd.density = 1; fd.friction = 0.3; fd.restitution = 0.5; body.createFixture(fd); body.setLinearVelocity(new Vec2(random(-5, 5), random(2, 5))); body.setAngularVelocity(random(-5, 5)); } }
class Particle{ Body body; float r; color col; //コンストラクタ Particle(float x, float y, float r_){ r = r_; makeBody(x, y, r); body.setUserData(this); col = color(127); } //衝突したら色を変更する void change(){ col = color(255, 0, 0); } void killBody(){ box2d.destroyBody(body); } boolean done(){ Vec2 pos = box2d.getBodyPixelCoord(body); if(pos.y > height+r*2){ killBody(); return true; } return false; } //図形の描画 void display(){ Vec2 pos = box2d.getBodyPixelCoord(body); float a = body.getAngle(); pushMatrix(); translate(pos.x, pos.y); rotate(-a); fill(col); stroke(0); strokeWeight(2); ellipse(0, 0, r*2, r*2); line(0, 0, r, 0); popMatrix(); } void makeBody(float x, float y, float r){ //ボディの定義 BodyDef bd = new BodyDef(); bd.type = BodyType.DYNAMIC; bd.position = box2d.coordPixelsToWorld(x, y); body = box2d.createBody(bd); //シェイプの定義 CircleShape cs = new CircleShape(); cs.m_radius = box2d.scalarPixelsToWorld(r); //フィクスチャの定義 FixtureDef fd = new FixtureDef(); fd.shape = cs; fd.density = 1; fd.friction = 0.01; fd.restitution = 0.3; body.createFixture(fd); body.setAngularVelocity(random(-10, 10)); } }
class Spring{ MouseJoint mouseJoint; //コンストラクタ Spring(){ mouseJoint = null; } //座標の更新 void update(float x, float y){ if(mouseJoint != null){ Vec2 mouseWorld = box2d.coordPixelsToWorld(x, y); mouseJoint.setTarget(mouseWorld); } } //図形の描画 void display(){ if(mouseJoint != null){ //Vec2 v1 = null; Vec2 v1 = new Vec2(0, 0); mouseJoint.getAnchorA(v1); //Vec2 v2 = null; Vec2 v2 = new Vec2(0, 0); mouseJoint.getAnchorB(v2); v1 = box2d.coordWorldToPixels(v1); v2 = box2d.coordWorldToPixels(v2); stroke(0); strokeWeight(1); line(v1.x, v1.y, v2.x, v2.y); } } //画面の座標とBoxオブジェクトの紐付け void bind(float x, float y, Box box){ //マウスジョイントの定義 MouseJointDef md = new MouseJointDef(); md.bodyA = box2d.getGroundBody(); md.bodyB = box.body; Vec2 mp = box2d.coordPixelsToWorld(x, y); md.target.set(mp); md.maxForce = 1000.0f * box.body.m_mass; md.frequencyHz = 5.0f; md.dampingRatio = 0.9f; mouseJoint = (MouseJoint)box2d.world.createJoint(md); } //マウスジョイントを取り除く void destroy(){ if(mouseJoint != null){ box2d.world.destroyJoint(mouseJoint); mouseJoint = null; } } }
import org.jbox2d.callbacks.ContactImpulse; import org.jbox2d.callbacks.ContactListener; import org.jbox2d.collision.Manifold; import org.jbox2d.dynamics.contacts.Contact; class CustomListener implements ContactListener{ //コンストラクタ CustomListener(){ } //衝突したときの処理 void beginContact(Contact cp){ //フィクスチャの取得 Fixture f1 = cp.getFixtureA(); Fixture f2 = cp.getFixtureB(); //ボディの取得 Body b1 = f1.getBody(); Body b2 = f2.getBody(); //オブジェクトの取得 Object o1 = b1.getUserData(); Object o2 = b2.getUserData(); //オブジェクトの型の判定 if(o1.getClass() == Box.class){ Particle p = (Particle)o2; p.change(); }else if(o2.getClass() == Box.class){ Particle p = (Particle)o1; p.change(); } } void endContact(Contact contact){ } void preSolve(Contact contact, Manifold oldManifold){ } void postSolve(Contact contact, ContactImpulse impulse){ } }
まとめ
「The Nature of Code」から衝突イベントを別クラスに切り分けることについて取り上げました。引き続き、「The Nature of Code」の内容を勉強します。
参考書籍
※Javaの勉強にもなるので一石二鳥です。