執筆バージョン: Unreal Engine 4.25
|
こんにちは、アートディレクターの黒澤です。
今回はUnreal Engineでレベルで配置したアクターの座標に意図しない端数が入ってしまう問題について解説したいと思います。
はじめに断っておきますが、Unreal Engineでは意図しない端数が頻繁に入るということではなく、端数が入る原因は初学者がやりがちなミスがほとんどです。少なくとも原因がわかっていれば対応はできます。
なぜ座標に意図しない端数が入るのか?
floatによる小数点誤差
Locationに意図しない端数が入る原因の一つにfloatによる小数点誤差問題があります。
多くの3Dアプリケーションでも同じことが言えるのですが、UnrealEngineの座標の値はfloatで持っています。floatとは、floating point number(浮動小数点)という型の数値表現で、あらゆる数値を厳密に表現できるわけではなく、特に小数点の表現で誤差が生じます。
適当なオブジェクトを原点に配置した後、LocationのXに、『100.1』を入力してみましょう。

『100.1』と入力したはずなのに、『100.0.99998』と表示されてしまいます。

これが浮動小数点の誤差です。
整数は比較的保護されるのですが、小数点はどんな厳密な値でも表現できるわけではないので注意してください。
浮動小数点とその誤差については、以下の記事がわかりやすかったです。
https://qiita.com/angel_p_57/items/24078ba4aa5881805ab2
クォータニオン
Rotationに意図しない値が入る場合は、クォータニオンに関する問題があります。
これもUnreal Engineに限った話でもないのですが、ビューポートで回転を行う際にはクォータニオンで計算しており、表示上はオイラーに変換して表現しています。
オイラーというのは、オブジェクトのローカル軸をヨーピッチロールで回転させて特定の方向を向ける表現形式のデータです。
クォータニオンとは『任意の回転軸を想定し、その軸に対してどれくらい回転しているか』という表現形式のデータです。オイラーもクォータニオンも表現形式が違うだけで、どちらも向きを表すデータです。
クォータニオンは『任意の回転軸に対する回転』というデータなので、別の回転軸においてはどれくらいの回転しているのかを簡単に求めることができるので、自然に別の方向に向けることができます。オイラーは一見シンプルなデータ形式なのですが、ジンバルロックが起こるようなケースがあり、計算上の問題を抱えているため、多くの3Dアプリケーションでは計算にはクォータニオンを使用しています。
クォータニオンは表示上はわかりにくい値なので、Unreal EngineのDetailパネルではオイラーに変換して表示しています。しかしクォータニオンはラジアンを使用したデータなので、弧度法から変換するときに誤差が生じます。
これもやってみましょう。
- 回転を90°にスナップします。
- Z軸(青い軸)をマイナス方向に回転させ、-90にします。
- さらにマイナス方向に回転させ、-180にします。
- 今度は逆にプラス方向に回転させます。

+90°に回転を行ったことで、-90になりそうなのですが、-89.999969°になります。
これがクォータニオンを変換したときの誤差です。

クォータニオンは以下の記事がわかりやすかったです。
https://qiita.com/drken/items/0639cf34cce14e8d58a5
ローカル座標系のままで操作
これはどちらかというとユーザーの誤操作によるものなのですが、グリッドにスナップしたいオブジェクトをローカル座標モードで移動することで、端数が入ってしまうとことがあります(ありました…
前述のようにfloatによる小数点誤差はグリッドスナップで移動すればかなり防げます。ですが、回転のスナップを使用してもクォータニオンの変換誤差は生じてしまうので、回転の値には端数が入ります。そこで回転に端数が入ったままローカル座標で操作すると、Locationの値も端数になってしまいます。
これもやってみましょう。
- 先程の座標のように、回転に端数が入った状態を作ります。
- ローカル座標モードで、グリッドスナップを使って移動します。


Z=-89.999969°という回転軸にそって移動したことで、Locationの値にもめちゃくちゃ端数が入ってしまいます(´;ω;`)

端数が入るのを避けるには
グリッドスナップをうまく使う
floatによる小数点誤差ですが、整数の誤差は起きにくいので、壁などのきっちり配置したいオブジェクトはグリッドにスナップして使えるように作りましょう
座標系を使い分けよう
ローカル座標系のままで操作することによるLocationのズレですが、ワールド座標で動かすと問題ないので、座標系はちゃんと使い分けましょう。
あと、そもそもの話として、アクターの座標の値には多少注意を払って作業することをおすすめします。
端数の座標を修正するには
実際の案件でも、このような座標系にまつわる様々なトラブルがあり、すでに配置が終わったレベルの座標に意図しない端数が入り込んでいました。
そこでレベル上の座標をまとめて修正するEditor Utility Widgetを書きました。

具体的な処理は以下になります。
- 選択したオブジェクトをリスト化
- リストから順番に取り出して座標の値をRound関数で四捨五入
- アクターの座標にセットする
- 2~3をリストがなくなるまで繰り返します。
このようにEditor Utility Widgetはレベル上のパラメータにもアクセスでき、簡単な処理でまとめて処理できるのでとても便利です。