すとーぶろぐ

主に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
またおぞましいものを生み出してしまった…

【Blender】放射マップベイクでワイヤーフレームテクスチャを作る

 

f:id:t_stove_k:20190804222010p:plain

ワイヤーフレームかっこいいですよね。

Unityだとスクリプトとかジオメトリシェーダーを使えば、ワイヤーフレーム表示だけなら案外簡単にできるのですが、そこから色々やろうとすると結構扱いにくい…。

そんなわけで色々(10分ぐらい)考えたところ、ワイヤーフレームテクスチャを作っちゃえば、あとは貼っつけるだけなので楽だし色々扱いやすいじゃんという結論に至りました。

ということで、本記事ではBlenderワイヤーフレームテクスチャを作る方法を紹介します。

 

タイトルにある通り放射マップをベイクする方法で作っていきます。

UV展開して「UV配置をエクスポート」でも作れるのですが、画質低いし、色の設定とかやりにくいしでイマイチです。※そんなことないよ!こうすればいいよ!という方いましたら連絡ください。

 

それでは早速やっていきましょう。

Blenderのバージョンは2.8です。

ちなみに本記事ではUV展開のやり方については説明しません。

 

今回はこちらのAstronautくんのワイヤーフレームテクスチャを作ってみます。objファイルをダウンロードしてBlenderにインポートします。

poly.google.com

 

 

レンダーエンジン変更

ここからが本番です。まずは、レンダーエンジンをCyclesにします。(デフォだとEeveeになってます。Eeveeでできるかは未検証です。すいません。)

f:id:t_stove_k:20190812004040p:plain

 

Astronautくんをワイヤーフレーム表示に

続いてAstronautくんをワイヤーフレーム表示にしていきます。

オブジェクトモードでAstronautくんを選択し、マテリアルAstronaut_matを選択。そして、シェーダーエディターを開きます。

f:id:t_stove_k:20190811235813p:plain


 

開くと下図みたいになってるかと思います。

f:id:t_stove_k:20190804231643p:plain

 

 

ここからノードを追加していきます。「入力>ワイヤーフレーム「シェーダー>放射(英語ならたぶんEmitもしくはEmission)」「シェーダー>シェーダーミックス」の3つのノードを追加します。

f:id:t_stove_k:20190812005228p:plain

 

追加したノードを下左図のようにつなげましょう。3DViewの表示を「レンダー」に切り替えたら下右図のようにワイヤーフレームっぽくなってるかと思います。

f:id:t_stove_k:20190811235940p:plain

 

放射マップをテクスチャにベイク

ワイヤーフレーム表示ができたので、ベイクしていきましょう。

まずは、「UV画像エディター」を開きます。

f:id:t_stove_k:20190812005320p:plain

 

開いたら、「画像>新規」で新しい画像を作成します。

f:id:t_stove_k:20190812003558p:plain

 

 

設定ウィンドウが出てくるので、名前と幅、高さなんかを決めてOKをクリックします。設定はだいたい下図の通りでオッケーです。サイズはちょっと大きめにしてます。

f:id:t_stove_k:20190812000212p:plain

 

下図みたいに真っ黒い画像ができます。この画像にワイヤーフレーム柄をベイクしていきます。

f:id:t_stove_k:20190812003534p:plain

 

ここでもう一回、シェーダーエディターに戻りましょう。そして、「テクスチャ>画像テクスチャ」ノードを追加します。このノードはどこにもつなげなくていいです。

f:id:t_stove_k:20190812003643p:plain

 

画像テクスチャノードに画像を指定する欄があるのでそこに先ほど作った画像(wireframe)を指定します。

f:id:t_stove_k:20190812000121p:plain

 

ベイクタイプを「放射」にし、ベイクをクリックしましょう。

f:id:t_stove_k:20190812003337p:plain

 

ベイク中…

f:id:t_stove_k:20190804232957p:plain

 

 

できあがりーf:id:t_stove_k:20190804231850p:plain




今回はAstronautくんのワイヤーフレームテクスチャを作りましたが、UV展開さえすれば別のモデルでもほぼ同じ手順で作れます。たぶん。

 

おまけ

テクスチャなのでかなり扱いやすいです。下のようなこともスクリプトとかジオメトリシェーダー使うときより全然楽チンにできます。 

 

TextFieldに数字だけ入力できるようにする【Xamarin.iOS】

 

いくつか方法はあるのでしょうが、int.TryParse(string s, out int result)を使うのをすすめます。

このメソッドはstring型のsを受け取り、sがint型の数値に変換可能だったら、resultに変換した値を返します。sが変換不可なら、resultに0を返します。

これ使えば、if文使わなくていいじゃん!コードがシンプルに…!好き…。

おすすめです。

 

 

実際にTryParseを用いると以下のようなコードになります。

TextFieldはNumberFieldという名前にしてます。

.EditingChangedはTextFieldで文字が変更された後に実行されるアクションメソッドです。変更後の文字列も取得できるので、コードがさらにシンプルに仕上がりました。

 

 

これで、 TextFieldに英字なんかを入力しても0になります。空白にしても0です。

 

 

MMDモデルをUnity for Macにインポートする

 

ある日、突然無性にミクさんにダンスさせたくなり、Unity上でダンスさせようとしたら、ちょいと苦戦しました。

Macでやる場合、Windowsのようにすんなりとはいかないみたいです。Windowsで試してはいないのですが(小声)

 

使用OSとUnityのバージョンは

Mac OS X High Sierra バージョン10.13.6

・Unity 2017.4.1f1

です。

 

下準備

 

やることは以下の①と②だけです。

① 最新版の「MMD4Mecanim Beta」をダウンロード、インポートする。

「MMD4Mecanim_Beta_20160815」をダウンロードし、「PMX2FBX」をインポートする。

 

Macの場合、②の手順が必要になります。 以下の質問のベストアンサーが参考になります。

teratail.com

 

 

 

最新版「MMD4Mecanim_Beta」のインポート

  

ここから細かい説明をしていきます。

 

 

Stereoarts Homepage「MMD4Mecanim_Beta」をダウンロードします。

 

f:id:t_stove_k:20181104172449j:plain

赤枠のとこをクリックすればダウンロードできます。

 

 

ダウンロードしたら、Unityにインポートします。「MMD4Mecanim_Beta_~」「MMD4Mecanim.unitypackage」をクリックします。インポート画面が出てきたら、全部インポートします。

 

f:id:t_stove_k:20181104172525j:plain

 

 

 

「 MMD4Mecanim_Beta_20160815」の「PMX2FBX」をインポート

 

こちら「MMD4Mecanim_Beta_20160815」をダウンロードします。

 

f:id:t_stove_k:20181104163715j:plain

赤枠のとこをクリックすればダウンロードできます。 

 

「MMD4Mecanim_Beta_20160815」「MMD4Mecanim.unitypackage」をクリック、インポート画面が出たら、「PMX2FBX」だけをインポートします。

 

f:id:t_stove_k:20181104172642j:plain

 

 

これで下準備完了です。

 

 

 

 MMDモデルのインポート

 

下準備が終わったら、MMDモデルをダウンロードします。

今回の使用モデルはTda式ミクさんです。かわいいです。

bowlroll.net

 

ダウンロードしたら、Unity上にインポートします。ドラッグでAssetフォルダにぶち込めばオッケーです。

 Tda初音ミク・アペンド_Ver1.10.MMD4Mecanim」を選択するとinspector上に利用規約が表示されるので、よく読んで、各項目チェックしていきます。全項目にチェックを入れると、最下部の同意するボタンが押せるようになります。

 

f:id:t_stove_k:20181104164607j:plain

赤枠のAssetを選択するとinspectorに下のような利用規約が表示される。 

 

f:id:t_stove_k:20181104172722j:plain

全項目にチェックを入れると同意するボタンが押せるようになる。 

 

 

 同意するボタンを押すとInspector上の表示が以下のようなものになるので、最下部にあるProcessボタンを押します。

 

f:id:t_stove_k:20181104164424p:plain

 

 

これでミクさんモデルがpmxからfbxに変換されたので、Sceneに追加できるようになります。

丁重に扱いましょう。 ようこそミクさん。

 

f:id:t_stove_k:20181104144150p:plain

 

 

 

 

 

 

【Unity】テクスチャをグレースケールからカラーへ素敵にトランジションさせるShader

 

実行結果

 

f:id:t_stove_k:20181103222058g:plain

世界に色を取り戻しました。Shaderは世界を救う…

 

しくみ

 

「カラー画像からグレースケール画像を作って、ルール画像で2つの画像間のトランジションパターンを決める。」って感じです。

ルール画像とは以下のような規則性があるっぽいグレースケール画像のことです。

 

f:id:t_stove_k:20181103220947p:plain

 

このルール画像をうまいこと使えば、素敵なトランジションを行うことができます。

 

 

コード

 

【Unity】テクスチャトランジション(グレイスケールからカラー。もしくはその逆)【Shader】

 

上記Shaderコードでは大体以下の①〜③の手順を踏んでます。

① 入力テクスチャのオリジナルカラー値(col)からグレースケール値(gray)を生成

② 変数_Thresholdとルール画像のピクセル値(pg)を比較。比較結果から0か1を取得。

③ オリジナルカラー値(col)とグレースケール値(gray)をlerpで補間、出力。補間値は②で取得した値

 

 

問題点

 

ルール画像によっては下図のような残りカスみたいなのができます。

f:id:t_stove_k:20181103211355p:plain

ルール画像のピクセル値(pg)がちょうど0か1となってしまう場合、_Threshold(0〜1)をどんなにいじっても、0より小さくならないし、1より大きくもなりません。つまり、step(pg, _Threshold) の値が変動しなくなってしまいます。step(pg, _Threshold) が変動しないので、最終的に出力するピクセル値も変動しません。

これが原因で変化しないピクセル、残りカスができてしまうみたいです。

今回は解決のためにpgを0.01~0.99の範囲にclampしてます。

Thresholdの範囲を広げるのもありだと思います。

 

 

おまけ

 

グレースケール画像に関してもう少し詳しく知りたい場合は以下の記事を。

qiita.com

 

以下のサイトで素敵なルール画像をたくさんゲットできます。

4you.bz

 

 

 

【Unity】Mathf.Atan2で取得する角度を上向き0度スタートで時計回りにする【2D】

 

以前、2点間の角度を求めるスクリプトを紹介しました。

ただし、この記事のやり方だと

・目標点が始点から見て真右にあるときに角度が0°

・反時計回りに角度増加

となってしまいます。

f:id:t_stove_k:20180828160617j:plain

これだと、「普通、目標点が真上にあるときが0°でしょ?」とか「なんで時計回りじゃないの?」みたいな感じで違和感を覚えるかもしれません。たぶん。

というわけで、本記事ではそんな違和感を解消するために、

・目標点が始点から見て真上にあるときに角度が0°

・時計回りに角度増加

となるスクリプトを紹介します。

f:id:t_stove_k:20180828160640j:plain

 

スクリプトはこんな感じ。

以前のスクリプトと異なるのはMathf.Atan2に渡す引数ぐらいです。

以前は第一引数にy座標の差、第二引数にx座標の差を渡しましたが、今回はこの2つの引数を入れ替えて、第一引数にx座標の差、第二引数にy座標の差を渡します

これで、目標点が真上にあるときに角度0°に、そして、時計回りに角度が増加していきます。

ついでに角度の範囲が0~360になるように調整してます。(29~32行目)

 

 

f:id:t_stove_k:20180828164101g:plain

Textで角度をリアルタイムに表示してます。

 

 

【Unity】2点間の角度を取得する【2D】

 

「2点間の角度を取得する」よりも「点Aから見て点Bは何度の位置にあるのかを取得する」みたいな感じに言った方がピンとくるかもしれません。たぶん。

 

f:id:t_stove_k:20180827152755j:plain

 

スクリプトはこんな感じ。

GetAngle関数の部分を簡単に説明します。まず、2点(startとtarget)のx座標とy座標の差をそれぞれ求めます。その2つの差を引数としてMathf.Atan2に渡せば2点間の角度がラジアンで返ってくるので、Mathf.Rad2Degで度に変換しておしまい! 

ただし、求められる角度はtargetがstartの真右にあるときに0度となります。角度の範囲は-180°~180°です。 

 

 

f:id:t_stove_k:20180827031711g:plain

Textを使って2点間の角度をリアルタイムに表示してみました。 

 

 

Mathf.Atan2について

ここで、上のスクリプトにも登場しているMathf.Atan2について少しだけ説明します。

 

いきなり、数学の復習になりますが、tan(タンジェントとその逆関数Atan(アークタンジェントの関係を式で表すと

tanX=Y

X=AtanY

となります。ご覧の通り、tanXの値がわかれば角度Xを求めることができます

Unityにはtanの値を渡すと角度を返してくれるMathf.Atanという関数が備えられています。

ただし、Mathf.Atanが返してくれる角度は-π/2~π/2で、半周分だけです。

その理由は、Mathf.Atanの引数がtanの値だけだからです。

0°~360°の範囲で、例えば、tanの値が1のとき、角度は45°と225°の2つの場合が考えられます。このとき、sinとcosの値もわかれば、45°なのか225°なのかを判別することができるのですが、Mathf.Atanの引数はtanのみです。sinとcosの値はわからないので、判別できません。

 

じゃあ、一周分の角度を取得したい場合どうすればいいのでしょう?

そんな時はMathf.Atan2を使いましょう!

Mathf.Atan2もMathf.Atanと同様に角度を返してくれる関数です。

異なるのは、引数です。

Mathf.Atan2には引数としてx座標とy座標を渡します。この2つの引数からtanだけでなく、sinとcosの値も求められるので、一周分の角度を返すことができます

 

 

 

以下参考サイト。AtanとAtan2の違いについて本記事よりわかりやすく説明しています。

小ネタ atan2 | ++C++; // 未確認飛行 C ブログ

[C++] C ++のatanとatan2の違いは何ですか? math.h | CODE Q&A [日本語]