滑らかな回転
課題
3Dオブジェクトをスムーズに回転させ、新しい方向に向けさせたい場合。
解決策
この問題に初めて直面した時、おそらくx軸/y軸/z軸に対する角度を表す3つの値である「オイラー角」の概念が頭に浮かぶでしょう。Godotではオブジェクトのオイラー角をrotationプロパティで確認できますが、3D環境で作業する際にこれを直接使用するのは推奨されません。これには理由があります。例えば「ジンバルロック」という問題があり、これは回転のうち1つが90度に達すると、自由度が1つ失われてしまう現象です。
オイラー角の背景や、ジンバルロックといった関連する問題についてさらに詳しく知りたい方には、こちらの解説動画がオススメです
Godotではオブジェクトのtransformプロパティを利用することで、3Dオイラー角を使用する必要を回避できます。このプロパティはオブジェクトの空間内における位置と向きを同時に表現します。これは数学的なマトリックス構造によって実現されていますが、実際に使用する際にはその背後にある数学的原理を理解する必要はありません。
look_at()
以下の例では、ミサイルや矢のような3Dオブジェクトを目標方向に向ける方法を示します。これはNode3Dクラスのlook_at()メソッドを使用することで実現できます。
func _process(delta):
var target_position = $Target.transform.origin
$Arrow.look_at(target_position, Vector3.UP)
このコードでは、ノード($Arrow)がターゲットの位置を常に向くようになります。ターゲットがどのように移動しても関係ありません。
注:look_at() 関数は2つのパラメーターを必要とします。ターゲット位置と「上方向ベクトル」です。飛行機が目標地点に向かって機首を向ける様子を想像してみてください。機体の向きには無数の方法が考えられます。これは、航空機がその軸を中心に回転できるためです。この第2パラメーターによって、最終的な機体の向きをどのように定義するかを指定します。
スムーズな回転制御
上記のコードは動作しますが、回転を瞬時にターゲット位置にスナップさせます。対象物が非常にゆっくりと動いている場合なら許容できるかもしれませんが、見た目に不自然です。開始姿勢から終了姿勢まで、滑らかに「補間」しながら徐々に回転させる方がより自然に見えるでしょう。
Godotはこの問題も適切に解決しています。look_at()の代わりに、Transformオブジェクトのlooking_at()メソッドを使用できます。このメソッドはノード自体を回転させることなく、ターゲットを見るための変換行列を返すだけです。さらに、interpolate_with()メソッドと組み合わせることで、現在の向きから目標の向きへと滑らかに遷移させることができます。
var speed = 5
func _process(delta):
var target_position = $Target.transform.origin
var new_transform = $Arrow.transform.looking_at(target_position, Vector3.UP)
$Arrow.transform = $Arrow.transform.interpolate_with(new_transform, speed * delta)
注:interpolate_with() は transform を操作するため、回転と位置の両方に対して補間を行えます。
まとめ
これで完了です!この便利な方法を使って3Dオブジェクトを回転させ、角度の計算に煩わされることなく作業を進めてください!