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()は回転軸が荒れるので却下しました。
Blenderから出力したオブジェクトをInstantiateする時の回転
ちょっと今頭を使う時間がないのよ。
Blenderから出力したオブジェクトはおそらく
-Z Forward
Y Up
オプションで出していると思いますが。
これをこのままUnityに表示すると
Euler(90, 0, 0)
で配置されます。
これをよくあるInstantiateの構文で出そうとすると。
Instantiate (prefab, Vector3(0, 0, 0), Quaternion.identity);
てなって、これで出すと90度回転してしまいます。
なので、回転を設定・・・
えええーまたQuaternionじゃねーの!
というわけで、90度回転させて置く場合の記述方法
Instantiate (prefab, Vector3(0, 0, 0), Quaternion.Euler(-90, 0, 0));
理屈じゃねーんだ!
身体で覚えろ!
・・・・
何かね
Quaternion.EulerAngles(-90, 0, 0)
とか
Quaternion.eulerAngles(-90, 0, 0)
とか色々試しちゃったよ・・・
つまり総当りで見つけ出したってわけ!
記憶障害のある文系ってこれだから!
ね!
BlenderからUnityへのモデルとアニメーションの出力
スケール
UnityはBlenderの100倍のスケールで動作している
Blenderからの吐き出しを100倍にしないとUnity上で1の大きさで表示されない
しかし
単純に100倍して出力するとトップノードに100倍のスケールが入る
このため子ノードのローカル移動が(ワールド移動は問題ないが)1/100単位になってしまう
そのため小数点以下2ケタが無視されてしまい誤差が生じる
かといって
Blender上で100倍のスケールで作業するとビューのクリッピングやデフォルトのオブジェクトの大きさが小さすぎるなどの問題が生じる
現在はBlenderで10倍のサイズで作業して
出力する際に10倍して対応している
フレーム番号の違い
Blenderのスタートフレーム=1の場合
Unityのスタートフレーム=0
1フレームの扱い
Blenderで1fだけのモーションを作った場合
Unityの最少アニメーション長は0.1f
つまりBlenderの1.1f分のアニメーションも再生されてしまう
停止したアニメーションを作成したい場合
Blenderでは(例えば)1~2fで作成し
Unity上で1~2fとしなければならない
アニメーションの出力
Takeを出力できるという記述もあるが今のところ再現しない
よって
A:モデルとアニメーションを同時に出力してUnity上でアニメーションを分割する
B:モデルとアニメーションを別々に出力してUnityで分割して使用
C:モデルとアニメーションを別々でアニメーションも分割して出力
しかしCの場合再生されないアニメーションがあったのでAかBが望ましい
(ファイル数が多くなるのでデータロード時間の問題?)
また出力ファイルの大きさの問題からBを推奨する
その場合「フレーム番号の違い」に注意して分割すること
アニメーションが正常に再生されない場合
Anim.Compressionがデフォルトでは「Keyframe Reduction」になっているので
「Off」に設定する
アニメーションするオブジェクトの移動
(Genericモードの場合=Mecanim使用)
トップノード無し | トップノード有り | |
オブジェクト自体を移動する | ワールド位置に戻る | ワールド位置に戻る |
Unity上で親を付ける | ワールド位置に戻る | ワールド位置に戻る |
ApplyRootMotion = off | 移動先でアニメするが トップノードが動かない | 移動先でアニメ |
Blenderでモデルを作成する時にBlender上では動かさないが
Unity上で全体位置を操作するためのトップノードを作成し
ApplyRootMotionをoffして作業することを推奨します
★RigセクションのRoot Nodeの項目変更による動作については未調査
出力ファイルの大きさ(参考)
モデルの構造やアニメーションの長さに依存しますがアニメーションするノードだけをアニメーションファイルとして別に出力した方がサイズが小さくなります
また複数のアニメーションを含む場合は別々に出力するよりも1ファイルで出したほうが小さくなります
モデルとアニメーションを同時に出力>510kb
モデルのみの出力>197kbアニメーションのみを1フィアルで出力>170kb
アニメーションのみを分割して出力(複数のアニメーションファイルの合計)>346kb
プログラムによるアニメーションよりもアニメーションクリップが優先される
つまりそういうこと。キャラクターの首の角度をプログラムで回転させようとしてもアニメーションクリップが適用されている限り動かすことができない。
頭と体を別オブジェクトにして間にアニメーションクリップと関係のないノードを挿入する?
現在キャラクターアニメーションを使っていないのでよくわからない。
現在作成中のもの=このブログの前提
来年あたり落ち着いたら開始する予定ですが、それまでこのブログで取り上げる予定はありあせん。
以下しばらくやらないもの。
・「人型」キャラクターの操作
・カメラの操作
・エフェクト
・マテリアルやテクスチャ
・敵のAI
2014年11月6日木曜日
Blender:Edit ModeでPick Shortest Pathされてしまう(MAXキーショートカット)
Ctrl Left Mouse Button
したらピックしたメッシュなりをそのまま選ぶはずなのに、何故か直前に選んだメッシュからそのメッシュまでつながったメッシュが全部選ばれてしまう!
これ、困ってる人にしかわからない説明だけど、困ってる人はわかるはず!
というわけで、
MAX Key Short Cutにしている人向け
Toggle Selectionにする方法
ふースッキリ!
Save User Settinを押すのを忘れずに!
2014年11月5日水曜日
Blender:Attach とDetach
とりあず愚痴るのでさっさと知りたい人は、読み飛ばして貰えればいいと思いますが。
なんで!
Joinの隣にSeparateが無いんだ!
さてそれでは・・・
Join
Separate
Blender:Duplicate LinkedとUnLink
それだと、なんかしらんが二重に登録されてたりしてうまく動かない場合が多い!
それはさておき