2014年11月27日木曜日

Unity:タッチ&ドラッグした方向に回転させるのに苦労した(進行中)

オブジェクトをドラッグして回転させたかった。

ちなみに三角関数をググってもググっても理解できない頭脳だ!

タッチした方向を直接向けるんじゃなくて、タッチ後ドラッグした角度だけ回転させたい。
見た目上はタッチした方向を向いているが、タッチした瞬間にタッチ方向に「カク」っと動かしたくない。

LookAt()を利用したいんだけど、3Dで変な回転をしてしまう。
用途として、最終的に一つの回転軸にして、その回転角度を取得してその後の処理をしたいのに、(x,y,z)にムチャクチャな値が入ってしまう。
見た目にはちゃんと動いてるのに!

とりあえず、現状の解決策は以下。
実は見た目とズレがあるので正確ではないが今のところここか限界。

static    function TouchRot(piv:Transform){
    var posP                    : Vector3 = Camera.main.WorldToScreenPoint(piv.position);        //ボタンの回転基点
    var mousePoint                : Vector3 = Input.mousePosition;        //マウスの位置
    var posB                    : Vector2 = Vector2(mousePoint.x, mousePoint.y);                //その後のマウス位置
    var rotFrom                    : float = Mathf.Atan2(posB.x-posP.x, posB.y-posP.y);
    var rotTo                    : float = 0;
    while ( Input.GetMouseButton(0) ){
        mousePoint                = Input.mousePosition;
        posB                    = Vector2(mousePoint.x, mousePoint.y);
        rotTo                    = Mathf.Atan2(posB.x-posP.x, posB.y-posP.y);
        piv.localRotation.z        += -(rotFrom-rotTo)/2;
        rotFrom                    = rotTo;
        yield;
    }
}

以下は解説

static    function TouchRot(piv:Transform){
タッチ用のオブジェクトと回転用のオブジェクトを分けたので、この関数を呼び出す時に回転用のオブジェクトを関数に渡す。
タッチ用のオブジェクトが回転用のオブジェクトの子になっている。

var posP                    : Vector3 = Camera.main.WorldToScreenPoint(piv.position);
回転用オブジェクトの「スクリーン上の位置」を取得。
スクリーン面のx,y位置と奥行きzが返ってくる。
実は全てWorldPoint(シーン内の位置関係)でやりたかったんだけど、そうするとx,y,zの三軸で考えなきゃいけない。
なので、今後の計算は奥行きzを無視して2D平面上で行う。

var mousePoint                : Vector3 = Input.mousePosition;
これはマウススクリーン上の位置。
当然奥行きzは0で返ってくる。

var posB                    : Vector2 = Vector2(mousePoint.x, mousePoint.y);
Input.mousePositionがVector3(つまり3D座標)で返ってくるので、zを削って2D座標にする。
ちなみに、この後のwhile内で更新し続けてマウスのリアルタイムな位置をここに入れておく。

var rotFrom                    : float = Mathf.Atan2(posB.x-posP.x, posB.y-posP.y);
回転は1フレーム前のマウスの位置が作る角度と、現在のマウスの位置が作る角度の差分を回転値として回転用のオブジェクトに追加してゆく予定。
なので、これは差分の元となる1フレーム前の角度を入れる変数。
んで、Mathf.Atan2()というやつ。
全く意味がわからないけど、どうもある点の(x,y)座標を入れてやると、角度を返してくれるらしい。
ただし、基点が(0,0)で計算するらしい。
でも、僕は「回転用オブジェクト」を基点として回転させたい。
ので、「回転用オブジェクト」の位置を(0,0)の位置にあるとして計算しなけりゃならない。
(これに気づくまでずっと変な回転してた)
というわけで、タッチ位置から「回転用オブジェクト」の位置分だけ引いている。
こうすると実際の角度計算は、画面平面の左下を(0,0)として、その周囲をタッチしたこととして角度を返してくる。
そして、ここ重要!
Mathf.Atan2から出てくるfloat数ですが・・・
これなにを表してるのか全くわかってません!
おそらくQuarternionに関係してるっぽいんだけど、よーわからん。
よーわからんけど、最終的にうまくいくので、「回転用オブジェクト」のlocalRotation.zにぶちこんでる。

var rotTo                    : float = 0;
rotFromに対応する、現在の角度を入れる変数。

while ( Input.GetMouseButton(0) ){
タッチした指が離れる(クリックしたマウスのボタンをはなす)まで以下の処理を行う。
ちなみに、関数自体を、Input.GetMouseButtonDown()をきっかけにして呼び出しているので、関数内にはその記述はない。

mousePoint                = Input.mousePosition;
posB                    = Vector2(mousePoint.x, mousePoint.y);

現在のマウス位置を取得。

rotTo                    = Mathf.Atan2(posB.x-posP.x, posB.y-posP.y);
現在のマウス位置が作り出す角度を取得。

piv.localRotation.z        += -(rotFrom-rotTo)/2;
ここが一番の無理やり部分。
1フレーム前のマウス角度(「回転オブジェクト」の角度じゃなことに注意)と現在のマウス角度の差分を、「回転オブジェクト」の角度に追加している。
・・・んだけど。
・何故か逆回転してしまったので、マイナスを付けている。
・何故か二倍くらい動いているので2を掛けている。
全くもって何でこんなことになっているのかわからない。
まず、2倍動いているっぽいのも見た目で判断しているのだ!
但し今自分のプロジェクトで必要なのは、初期位置から前後90度くらいの動きなので、あまり誤差は気にしない!
でもこのあたりは、後々ちゃんとしなきゃいけない時が来ると思う。

rotFrom                    = rotTo;
次のフレームの処理に向けて、現在のマウス角度を1フレーム前のマウス角度として代入。

yield;
1フレーム処理を待ってwhileの冒頭に戻す。


ちなみに・・・
他にも色々処理方法を調べてたんだけど・・・

rotBtn.LookAt(Vector3(worldPoint.x, rotBtn.position.y, worldPoint.z));
LookAt()は回転軸が荒れるので却下しました。

0 コメント:

コメントを投稿