『はじめてゲームプログラミング』の角度センサーがおかしい。
ご無沙汰しております。
ChinoGrandHotelです。
ニンテンドースイッチの『はじめてゲームプログラミング(略してはじプロ)』というソフトを購入しました。
直感的な操作で簡単にゲームプログラミングができちゃう大変素晴らしいソフトです。
毎日コツコツとゲーム作りに勤しんでいるわけなのですが、
最近とある不具合に直面して死ぬほど困ってました。
今回は、その原因および解明するまでの過程などをここに残したいと思います。
不具合について
まずは下記の動画をご覧ください。
これは浮遊する機体を操るレースゲームのプロトタイプみたいなものです。
機体の向きを変えたり左右に傾けたりできます。
で、本題です。
機体が傾いたとき、元の姿勢に戻すための力が発生します。
機体の中心部に角度センサーが埋め込まれていて、そこから機体の角度を取得して元の姿勢に戻るまで力が発生し続ける、という仕組みです。
動画内では正面を向いた状態で機体を傾けたとき、ちゃんと元の姿勢に戻っています。
しかし、横を向いた状態で同じ操作をしたとき、やけにグラグラしています。
左右に傾けただけなのに、なぜか前後にも傾き始めました。
これ、何度試しても何故か横を向いた状態だけ上手くいかないのです。
どういうことなの...。
検証してみた
まず、角度センサーがちゃんと正しく角度を認識できているのかを検証してみました。
下記の動画をご覧ください。
赤:X軸角度
緑:Y軸角度
青:Z軸角度
という感じです。
ちなみに角度センサーはゲーム開始時の箱の向きを基準としています。
箱の回転方向は回転時の箱の向きを基準としています。
まず、箱をZ軸で回転させるとZ軸角度のみが変動しました。
これは当然ですね。
その後、箱をY軸で90度回転させてからZ軸で回転させました。
結果、X軸角度とZ軸角度の両方が変動しました。
は?
Z軸角度が変動する→わかる
X軸角度が変動する→わからない
しかも片方じゃなくて両方変動しましたね。
どういうことなの...。
不具合の原因
どうやら箱をY軸で90度回転したあと、Z軸で回転するとX軸角度とZ軸角度の両方が動くみたいです。
これが不具合の原因でした。
機体が傾いたとき、元の姿勢に戻すための力が発生すると説明してました。
これは左右の傾きだけでなく、前後の傾きにも同様の力が発生するようになっています。
前方に傾けば後方へ、後方に傾けば前方へ傾く力が発生します。
そういう風に作ってあるんです。
そのとき必要になるのがX軸の角度です。
機体が前後に傾いたとき、すなわちX軸の角度が動いたときに前後方向の制御が働くわけです。
つまり、本来はZ軸で回転しているだけなのに、なんか知らないけどX軸角度も動いてしまったため前後方向の制御が働き、やけにグラグラしてしまったと。
はえ~。
この角度センサーおかしくない?
友達に聞いてみた
ちょうど3Dに詳しい友達がいたので聞いてみました。
さっきの検証動画も添えて送信しました。
そこで友達から返ってきたヒントが『ジンバルロック』でした。
ジンバルロックとオイラー角について
正直あんまりよくわかってないので詳しくは下記の記事を読んでください。
どうやら3Dのモノを回転させたときに3本の回転軸のうち2本が重なってしまい、回転軸が1本無駄になっちゃう現象をジンバルロックって言うらしいです。
例えばY軸に90度回転させたあと、X軸とZ軸を別々に動かしても全く同じ方向に回転しちゃうんだそうです。
はえ~...それっぽいかも。
たしかに検証でも最初に箱をY軸で90度回転させたあと、Z軸に回転させてました。
そこで角度センサーのX軸角度とZ軸角度の両方が変動したので、ジンバルロックってやつが発生したっぽいですね。
で、そもそもなぜこんなことが起こるのかというと、オイラー角というものの仕様だそうです。
下記の図のように、X軸・Y軸・Z軸という3本の回転軸でモノの姿勢を表現する方法がオイラー角って呼ばれてるみたいですね。
それぞれの軸から見てどれくらい回転したかを3つの数値で表現しているわけです。
さらに、オイラー角から姿勢を表現する際は回転する軸の優先順位が前提として必要になっています。
例えば(0, 90, -90)というオイラー角に対して、Y軸から先に回転させるかZ軸から先に回転させるかで結果が変わってしまいます。
最初に検証したときはY軸回転から先に実行していたので、私の中では勝手にY軸優先を前提として結果をイメージしてました。
じゃあ角度センサーもY軸を優先として計算してくれてるのか?
そんなことはなさそう。
推理パート
なんか色々な情報を読みまくって結局なに?って感じになったので、少し整理しましょう。
①角度センサーはモノの姿勢からオイラー角を逆算している。
今回、角度センサーはゲーム開始時の箱の向きを基準としていたので、最初の姿勢からどれだけ動いたかをオイラー角で逆算していると。
②同じ姿勢でも複数のオイラー角で表現できる。
今回検証したY軸に90度、Z軸に-90度と回転したときの姿勢も、よく考えてみたらX軸に-90度、Y軸に90度で回転させても同じ姿勢になるんですよね。
そうなると、姿勢からオイラー角を逆算したら正解が複数出てくるわけですね。
③回転の優先順位によって同じオイラー角でも結果が異なる。
そもそも角度センサーがどういう優先順位で逆算しているかわかってませんが。
④箱を回転させる時の基準と、角度センサーの基準がそもそも違う。
これ、後から気づいたんですけどめちゃくちゃ重要っぽい。
検証の際に記載してある通り、箱の回転を指示するときは回転時の箱の向きが基準になっています。
つまり箱がY軸の回転で横を向いたとき、箱の正面に伸びるZ軸の棒も一緒に横を向くわけです。
一方、角度センサーはゲーム開始時の箱の向きが基準になります。
なので箱が回転しちゃうと向きの基準でズレが生じてしまうんですね。
たしかに。
要するに...
角度センサーくんはオイラー角を逆算したところ、複数の解が出てきてしまいました。
じゃあ角度センサーくんはどの解を出力しているのか?というわけなのですが、
全部まとめて返してるんじゃないかと思いました。
この辺は私の憶測に過ぎないのですが、
たぶんオイラー角を逆算して複数の解が出てきた場合、それらの解から各回転軸の最大値を求めてるんじゃないかと。
今回の検証で言えば、Y軸を90度回転させるとき、X軸とZ軸のどちらを回転させても成立するのでどちらも90度と返しているわけです。
まぁたしかにどの解が最も適切なのかってセンサー側で判断しようがないですからね。
とりあえずX軸角度とZ軸角度の両方に出力しておけばX軸とY軸だけ欲しい場合とZ軸とY軸だけ欲しい場合はクリアできるってノリなのかな。
3つの軸全部欲しい場合だけこういう問題にぶつかるってわけですね。たぶん。
結論:角度センサーを信じるな。
じゃあどうやってモノの角度を正しく測るんだよと思ったのですが、
位置を角度に変換してくれるヤツを利用することで解決しました。
まだ試作中なのですが、以下の動画では角度センサーを使わずにY軸角度を計算しています。
左側:角度センサーによる各軸の角度
右側:角度センサーを使わずに計算した角度(Y軸のみ完成)
まず、回転するモノの横に位置センサーを連結しています。
こいつからXYZ座標が取れるので、位置センサーのX座標とZ座標を使って角度に変換します。
いわゆる逆三角関数ってやつなんですけど、あんまり詳しくないので詳細はインターネットに聞いてください。
とにかく、これでY軸角度を計算することができました。
あとは同じようなものをX軸とZ軸で用意してやればいけそうな気がします。
やったぜ。
この調子で以下のような近未来クソ面白レースゲームを作っていきたいと思います。
おまけ
さっきの検証で使ったのと同じやつです。
当然ですが、Y軸で回転させるとY軸角度のみが動きます。
微妙に他の軸も動いちゃってますが、まぁ誤差レベルです。
上図は179度ほど回転させた状態です。
ここからさらに右方向へ回すと...。
Y軸角度がマイナスになります。
-180度~180度で表しているので、180度を超えると-180度に戻るわけです。
ここまでは普通ですよね。
じゃあ箱の初期角度をY軸180度ジャストにしてみます。
今回の検証では角度センサーの基準をワールドの向きとしているため、間違いなく角度センサーのY軸は180度または-180度で出力してくれると思います。
これで実行したとき、3つの軸それぞれの角度はどのようになるでしょうか?
正解は...
以上です。
※今回色々と迷走して結論を出した形なので、なんか指摘とかあればください。