2014年2月2日日曜日

加速度センサーと画面回転~Unity

Unityで加速度センサーを使った画面回転のメモ。

多くのAndroid端末には傾きセンサ(加速度センサ)が内蔵されていると思うのでそれを利用して表示画面の自動回転方法を検討してみた(実機はKindle Fire HD8.9使用)。

結論は

「いじるな!」

に落ち着いた(マテ


一応言語はC#で試行。


■予備知識


【傾きを見るのに加速センサとかおかしくね?】



※そこからはじまるのかYOと言う一人ツッコミをしつつも書いてしまったので。

疑問: 加速度センサなのに角度を調べるとか?角度センサじゃないの?加速度センサとかおかしくね?角度センサに改名しろやゴルァ?  

 

回答: いやいや、そうでもない。そもそも加速度センサなのであるっ(キリっ

 

あくまで加速度センサの特性を利用して「傾きらしきもの」を得て(角度に換算して)いると考えるべき。

 

加速度センサ=各軸にかかるGを計測しているとイメージすると良い。
なので実機を左右や上下にブンブン振ったりすると内蔵の加速度センサはそのGを捕らえることができる。これなら加速度センサらしいよね?

で、この加速度センサは「G」 を測定するわけだけど、じゃあ実機を静止状態にしてたらGは0じゃないか?という疑問がでてくる。しかしこれは「No~!」で、地球上では常に重力=1.0Gで真下にひっぱられていることを忘れてはいけない。

つまり静止状態でも常に真下方向に1.0Gの力ベクトルがかかっている。これはブンブン振ったときのGと同じなので地球の中心から垂直に((((((((⊂( ・ω・)⊃ブーンと振り続け=加速しているのと同じ。加速センサは実機が静止状態でもそのGを計測している。

実機筐体が水平なら1軸に1.0Gがかかる。しかし筐体を傾けるとこの1.0GはXYZの3軸に分力してしまう(小学校あたりの力学)。
つまりNormalizeされたXYZ各軸の値のような状態になり、加速度センサはこの3軸値を計測する。
よって、重力1.0Gの分力結果=XYZ軸値により、めでたく傾きらしきものを得られるというしくみ。

と、いうことはGPSと併用すると速度が得られるので、それに加速度センサの結果を掛け合わせるとその機器の地上でのベクトル=移動方向もわかっちゃうという寸法だね。GPSが無くても歩行速度値を固定で与えてやればベクトル値は一応得られる可能性。

つまり単なるエンコーダー的な角度センサよりも随分とオーバーテクノロジなセンサ(加速度センサ)を内蔵してるので、角度くらい余裕でわかっちゃうYOということだね。 ※裏をかえすと、加減速が激しい場所(ジェットコースター?)では地球の重力1.0Gで分力されないので角度計測がうまくいかないってことになるね!(ん?ということはジェットコースターのGとか計測できるスマホアプリもできるかも?。これちょっと面白いかもしれないw)
そして宇宙空間では0GなのでKindleの自動画面回転もきかないってことだね!!(振ればいいのか!?


と、いうことで以下本題(´∀`)


■機器の傾き情報の取得

以下で行う。
Vector3 acceleration = Input.acceleration;
各軸は-1.0~0.0~1.0の範囲を持つ。



■物理画面と表示画面

このAPIで取得した傾き情報は現在の「論理画面」の向きを基準としてXYZ各軸の値を返す。
物理画面(機器筐体)の上下左右ではない(重要ポイント)。
つまり、画面を回転すると物理筐体でさっきまでY軸だった方向がX軸に変わったりする。XY軸はあくまで論理画面基準。(アプリ的にはこの方が作りやすい)。

Zは筐体の表裏が論理表示面に常に合致するので、実質見た目は筐体が真上~水平~真下の回転に対応する結果を得られる。




■各軸と表示画面の対応

各軸はソフトウェア的な表示画面の上下左右に合わせて変動する。
  • Y軸: 論地画面の上下方向に傾けていくと変動。
    論地画面上部を上にして立て掛けるようにすると、~-1.0に変動する。
    水平で0.0。
    逆で~1.0に変動。
  • X軸: 論地画面の左右に傾けると変動。
    左に傾ける(左を下)と~-1.0に変動。
    水平で0.0.
    右に傾ける(右を下)と~1.0に変動。
  • Z軸: 論地画面=物理筐体を水平に上向きで~-1.0に変動。
    垂直で0.0.
    下向きに水平で~1.0に変動。
    ※この軸は結果的に物理筐体に連動してるようにみえる。
    なんか軸がちがうんじゃね?的な気もするが実機動作(Kindle FireHD8.9)がこうなってるのでそういうことだと思っとけばきっと幸せ。 



■自動制御API

全自動で傾き&画面制御する方法は以下。
Screen.orientation = ScreenOrientation.AutoRotation;
ただこの場合傾き判定の閾値がゆるく、各軸±0.8付近(ほぼ垂直)で切り替わりが発生する。
Kindleの標準UI操作ではもう少しセンシティブに設定されている(だいたい±0.2~0.3付近)。
この閾値を変更できればいいのだが指定方法が見つからない。
※どうもこの辺はOS依存性部分らしい雰囲気。AndroidのAPIか何かを叩く必要が?
追記:IOSはActivityからOS API直叩きでできるらしい。結局OS依存コードになる可能性大。機種依存かどうかは未確認だがその可能性が高いという情報もネットで発見。



■その他気になる点

180度回転させると遅い。4~5秒かかる。90度回転は早い。
 (Activityの破棄>再生成でガベコレ?Unity自体が重い?)

この原因はわからないが「そういうもの」と思うしかないという結論に至る。
ちなみに90度回転を2回繰り返し180度回転させた方が明らかに早い。しかし回転完了イベントが拾えない為(拾い方が現状不明)、確実性に欠ける制御になりよろしくない
※発振するような挙動が考えられうる>完了待ちウェイトを伸ばす>フィーリングが悪くなるの試行地獄


回転完了イベントはUnityでは今のところみあたらず。Activityにもそのものズバリのイベントがあるのか不明(今のところ未発見。
※一応画面回転時はActivity破棄>再生成>onCreate()らしいので、これが使える可能性はあるらしい。
※追記:この180度回転だけ遅い現象は後日の検証で他のAndroid機では発生せず。
Kindle FireHD8.9特有の問題かもしれません。



■まとめ

UnityのAPIのみで画面回転を追及するのは多分厳しい感じ。180度回転遅い問題がどうにも回避できない。
Android SDK(Activity?)を含めた機種依存な実装が必要かもしれない。
※ほぼActivityアクセスによるOS依存or機種依存コード要だろうという認識に至る。

画面回転の挙動はいろいろとクセがある。(少なくともKindle Fire HD8.9では)


その他メモ:
  • 画面回転完了時はActivityのonCreate()がくるらしい。稀にこないときもあるらしい。
    つまりあてにならない?
  • onCreate()はUnity側でオーバライドできるらしい。Activity層のスレッド経由のコールバックはC#でうまくいった。しかしonCreate()のオーバライドがうまくいかない。ぐぬぬ。
    ※Java Scriptの方が親和性が高い?
  • そもそもUnityでの論理画面回転の重要性は?
    ゲーム用途なら低いのではないか?そもそも固定の方が多い?→ 実装優先度低。
    ※実はゲーム以外用途も考えていたので。
  •  ScreenOrientation.AutoRotationでいいのでは?
    いいかもしれない(笑。デバイスに任せるほうがデバイス依存度的にも丸く収まる?
    追記:と、いうか現状これでFA。90度回転なら問題ないし。

≪画面回転自前実装済みコード≫  ・⌒ヾ(*´_`)ポイ

ε=(ノ゚ー゚)ノ ≪ScreenOrientation.AutoRotation≫




また無駄な工数を使ってしまった・・・Orz
 

情報源: Unity - ユーザマニュアル - 入力 / Input

0 件のコメント:

コメントを投稿