すとーぶろぐ

主にUnity。実質備忘録。

【openFrameworks】ofxBox2dCircleでofImageに当たり判定をつける

環境

macOS Mojave 10.14.4
openFrameworks0.10.1
Xcode10.1


プロジェクトの準備

まずはアドオンofxBox2dをダウンロードします。
GitHub - vanderlin/ofxBox2d: Openframework wrapper for box2d



ダウンロードしたzipを解凍したらofxBox2d-masterというフォルダがあるのでそれをaddonsフォルダにぶち込みます。
f:id:t_stove_k:20190815232608p:plain:w500



続いてProjectGeneratorを使って新規プロジェクトを作成します。
ProjectGeneratorを立ち上げたら、以下のようなウィンドウが出てきます。AddonsにofxBox2d-masterを追加する以外は適当でいいです。もろもろ設定が終わったら、Generateをクリックします。
f:id:t_stove_k:20190815233021p:plain:w350


画像ファイルの準備

ofImageで使用する画像(myface.png)はbin/dataフォルダ内にぶち込みます。
f:id:t_stove_k:20190816191202p:plain:w500


コードかきかき

大体準備が整ったところでコードをかいていきます。
端折ってるところもありますが、ofApp.hとofApp.cppは以下のようにすればオッケーです。


ofApp.h

#include "ofMain.h"
#include "ofxBox2d.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();		
		
    ofxBox2d box2d;
    vector<shared_ptr<ofxBox2dCircle>> circles;
    float radius=40;
    
    ofImage image;
};


ofApp.cpp

void ofApp::setup(){
    ofBackground(0);
    
    box2d.init();
    box2d.setGravity(0, 10);
    box2d.createGround();
    box2d.setFPS(30);
    
    image.load("myface.png");
}

bool objectKiller(shared_ptr<ofxBox2dCircle> shape) {
    ofVec2f pos=shape->getPosition();
    return !ofRectangle(0, -100, ofGetWidth(), ofGetHeight()+100).inside(pos);
}

//--------------------------------------------------------------
void ofApp::update(){
    box2d.update();
    
    if((int)ofRandom(0, 10) == 0) {
        auto circle = std::make_shared<ofxBox2dCircle>();
        circle->setPhysics(0.3, 0.5, 0.1);
        circle->setup(box2d.getWorld(), ofGetWidth()/2+ofRandom(-50, 50), -20, radius);
        circles.push_back(circle);
    }
    
    ofRemove(circles,objectKiller);
}

//--------------------------------------------------------------
void ofApp::draw(){
    
    for(int i=0;i<circles.size();i++){
        circles[i]->draw();
        
        ofPushMatrix();
        ofTranslate(circles[i]->getPosition());
        ofRotate(circles[i]->getRotation());
        ofSetRectMode(OF_RECTMODE_CENTER);
        image.draw(0,0,radius*2,radius*2);
        ofPopMatrix();
    }
    
}

ofApp.hではofxBox2d.hをインクルードして必要な変数(box2d、circles、radius、image)を宣言してます。


ofApp.cppではupdate内でcircle(circlesに格納)を発生(+消去)させて、draw内でcircle(draw内ではcircles[i])の上にimageを描画してます。ぶっちゃけcirclesは描画する必要はないですが、わかりやすいかと思って今回は描画してます。


ofPushMatrix();
ofTranslate(circles[i]->getPosition());
ofRotate(circles[i]->getRotation());
ofSetRectMode(OF_RECTMODE_CENTER);
image.draw(0,0,radius*2,radius*2);
ofPopMatrix();

imageをdrawしてる部分をもう少し詳しく見てみます。
注意して欲しいのはofTranslateもofRotateも座標系を平行移動、回転するものであるということです。なので、上のコードでやってるのは、「まず、座標系をcircleのpositionに平行移動させ、circleのrotation分回転させたあとに、その座標系の(0,0)の位置にimageをdrawする」という処理になります。


座標系を変化させてるので、何もしなかったら他の描画物にも影響が出てしまいます。それを防ぐのが、ofPushMatrix()とofPopMatrix()です。この二つの関数で挟まれた部分の座標変化は外部に影響を及ぼしません。今回はimageだけを移動、回転させたいので上のように挟んでいます。


これでcircleとimageがぴったり重なる…!と思いきや全然ずれてます。なぜかというと、RectModeがデフォルトだとOF_RECTMODE_CORNER(左上から描画)になってるからです。これをOF_RECTMODE_CENTER(中心から描画)にするために、image.draw()の直前にofSetRectMode(OF_RECTMODE_CENTER)を加えます。これでぴったり重なります!

実行結果

f:id:t_stove_k:20190817033435g:plain
またおぞましいものを生み出してしまった…