バージョン 36.00 では、UEFN はエディタとすべての /Verse.org
モジュールのトランスフォームに LUF 座標系を使用します。 /UnrealEngine.com
と /Fortnite.com
モジュールのトランスフォームでは、XYZ 座標系を使用します。
LUF 座標系の概要
主要な 3D コンテンツ作成ツールはすべて、オブジェクトの位置と回転に直交座標系 (X、Y、Z) を使用しています。 しかし、どの軸 (X、Y、Z) がどの方向 (左右、上下、前後など) を表すという具体的な解釈は、ツールによって異なります。 また、座標系の掌性として知られる回転のモデル化方法も、ツールによって異なります。
そこで、Verse と UEFN を、3D コンテンツ制作やその他の主要ツールセットにおける新しい標準に適合させるため、座標系の表示方法に関して根本的な変更を加えます。
1 つ目は、座標軸に「X」、「Y」、「Z」というラベルの代わりに、説明的な軸名を導入した点です。
左 (以前は -Y)
上 (以前は Z)
前 (以前は X)
以前の XYZ および FRU から LUF に変更したことで、左手座標系 (XYZ および FRU) から右手座標系 (LUF) に掌性が変更しています。
2 つ目の変更点は、UEFN のビューポート ギズモの色が、他の 3D コンテンツ作成ソフトウェアと同じ色になった点です。
左:赤 (以前は緑)
上:緑 (以前は青)
前:青 (以前は赤)
3 つ目は、/Verse.org/SpatialMath
モジュールに加えられた多くの改善点です。
Verse 空間数学モジュールの関数は、すべて右手系の演算に統一されました。 その結果、右手系を明示していた関数のバリアントはすべて撤廃され、右手系がデフォルトの演算に置き換えられました。また、右手系を示すインジケータも関数名から撤廃されました。 詳細については、このページの下の「右手系」セクションを参照してください。
角度のパラメータまたは戻り値を含む回転関数には、ラジアンまたは度を受け付けるか返すバージョンが追加され、それぞれのバリアントには
Radians
またはDegrees
のサフィックスが付きます。RotateBy
、UnrotateBy
、およびすべての(:rotation).Apply*
関数は撤廃され、代わりに左から右への回転を合成する 2 つのrotations
乗算演算子が導入されました。 詳細については、このページの「[トランスフォームと回転の乗算順序]()」のセクションを参照してください。RotateVector
およびUnrotateVector
は撤廃され、代わりにvector3
とrotation
をこの順番で乗算して回転する演算子が導入されました。 詳細については、このページの「空間数学演算の変更点」セクションを参照してください。TransformVector
とTransformVectorNoScale
は撤廃され、代わりに、vector3
とtransform
を乗算する演算子が導入されました。順序は、スケール、回転、平行移動です。 詳細については、このページの「空間数学演算の変更点」セクションを参照してください。
4 つ目は、FBX からのスケルタルメッシュのデフォルト インポートで、上軸と前軸を揃えるオプションが導入された点です。すべてのインポートされたスケルタルメッシュが前軸を向くようになりました。
変更による影響
座標系の変更により、UEFN や /Verse.org
モジュールのトランスフォームを使用している人に影響があります。特に影響のある UEFN と Verse の側面は以下のとおりです。
UEFN の [Details (詳細)] パネル
UEFN の [Viewport (ビューポート)] とギズモ
Verse のトランスフォーム
公開した島が LUF で適切に動作するために、コードやコンテンツを変更する必要はありません。
UEFN の [Details (詳細)] パネル
UEFN の [Details (詳細)] パネルのトランスフォームで、座標が LUF システムで表示されるようになりました。
UEFN ビューポート
UEFN のビューポートで、ギズモの軸と色のマッピングが変更されました。
Verse のトランスフォーム
/Verse.org
モジュールのトランスフォームは LUF 座標系を使用します。 /UnrealEngine.com
モジュールで使われている XYZ 座標系のトランスフォーム方式は、今でも存在しており、/UnrealEngine.com
や /Fortnite.com
モジュールでは、それがデフォルトのトランスフォーム方式として使われています。
これらのモジュールはどちらもトランスフォーム型を含んでいるため、以下の点に注意してください。
/Verse.org
モジュール トランスフォームと/UnrealEngine.com
モジュール トランスフォームの両方を使用する API 関数を同じファイルで使用する場合、2 つのモジュール間で混同しないように、それぞれの型名をパスで修飾する必要があります。 以下のスニペットに例が示されています。Verseパスで修飾された型名using { /UnrealEngine.com/Temporary/SpatialMath } using { /Verse.org/SpatialMath } my_class := class: MyUnrealEngineVector:(/UnrealEngine.com/Temporary/SpatialMath:)vector3 = (/UnrealEngine.com/Temporary/SpatialMath:)vector3{} MyVerseVector:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{}
Scene Graph は、
/Verse.org
モジュールのトランスフォームを使用します。 これは、Scene Graph が LUF 座標系のみを使用するようになったことを意味します。Verse ダイジェストは、トランスフォーム型が UnrealEngine.com/Temporary/SpatialMath モジュールと Verse.org/SpatialMathモジュールで定義されているため、それらを区別するために完全修飾識別子を使っています。
Unreal Engine の座標系については、Unreal Engine の座標系と空間を参照してください。
XYZ から LUF への変換
/Verse.org
モジュールのトランスフォーム (LUF) に切り替わった既存の API 関数を使用している場合は、ユーザー定義の /UnrealEngine.com
トランスフォーム (XYZ) を LUF を使用するように変換するか、新しく作成された FromTransform
変換関数を使用する必要があります。 このセクションでは、変換に伴う潜在的な問題とその解決策を紹介します。
定数型か変数型かの曖昧さ
Verse API に最近加えられた変更により、UnrealEngine.com/Temporary/SpatialMath
と /Verse.org/SpatialMath
の両方が vector3
、回転
、トランスフォーム
の型を定義します。 Verse ファイルに両方のドメインを含めると、その結果として型の曖昧さが生じます。
型の曖昧さの例
次の Verse ファイルは、/UnrealEngine.com/Temporary/SpatialMath モジュール パスと /Verse.org/SpatialMath モジュール パスの両方をインポートし、ユーザー定義クラスは非修飾型 vector3
を使用します。
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/SpatialMath }
my_class := class:
MyVectorOne:vector3 = vector3{}
#Compile Error: Identifier vector3 could be one of many types: UnrealEngine.com.Temporary.SpatialMath.vector3 or Verse.org.SpatialMath.vector3
MyVectorTwo:vector3 = vector3{}
#Compile Error: Identifier vector3 could be one of many types: UnrealEngine.com.Temporary.SpatialMath.vector3 or Verse.org.SpatialMath.vector3
そのため、このファイルをコンパイルしようとすると、型の曖昧さを理由とするコンパイル エラーが発生します。これはコンパイラが MyVectorOne
と MyVectorTwo
を /UnrealEngine.com
の vector3
型として扱うべきか、あるいは /Verse.org
の vector3
型として扱うべきか判断できないためです。 コンパイラは、どの型が指定されているか判断するのに十分な情報がユーザーから与えられていません。
このコンパイル エラーを解決するには、各変数が使用する vector3
型をパス修飾する必要があります。 これらの定数をパス修飾してエラーを解決するにはいくつかの方法があります。
以下のコード スニペットは、vector3
型の定数や変数を完全に修飾する例を示しています。
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/SpatialMath }
my_class := class:
MyVectorOne:(/UnrealEngine.com/Temporary/SpatialMath:)vector3 = (/UnrealEngine.com/Temporary/SpatialMath:)vector3{}
MyVectorTwo:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{}
以下のコード スニペットは、/Verse.org
モジュールから定数または変数を修飾する例を示しています。
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org } # Change the module path to avoid import ambiguity
my_class := class:
MyVectorOne:vector3 = vector3{}
MyVectorTwo:SpatialMath.vector3 = SpatialMath.vector3{} # Specify the submodule
以下のコード スニペットは、/UnrealEngine.com
モジュールから定数または変数を修飾する例を示しています。
using { /UnrealEngine.com/Temporary } # Change the module path to avoid import ambiguity
using { /Verse.org/SpatialMath }
my_class := class:
MyVectorOne:SpatialMath.vector3 = SpatialMath.vector3{} # Specify the submodule
MyVectorTwo:vector3 = vector3{}
Scene Graph 型の変更
Verse ファイルで Scene Graph API を使用しており、他に空間演算する API を使用していない場合、「using
」というキーワードでインポートする空間演算モジュールを変更することで、Verse コード内の空間演算型を変更することができます。 たとえば、次のようなコードがあったとします。
using { /UnrealEngine.com/Temporary/SpatialMath }
my_component := class<final_super>(component):
@editable # This change will work regardless of this field being editable or not
MyVector3:vector3 = vector3{}
MyVector2:vector2 = vector2{}
以下のように変更できます。
using { /Verse.org/SpatialMath } # Changed the module path
my_component := class<final_super>(component):
@editable # This change will work regardless of this field being editable or not
MyVector:vector3 = vector3{} # All transform types now use /Verse.org/SpatialMath
# MyVector2:vector2 = vector2{} Removed since /Verse.org/SpatialMath does not define a vector2
唯一の例外として、Verse.org モジュールは現在 Vector2
型を削除するか、vector3
に変換しなければならない点にご注意ください。/Verse.org
モジュールが現在 vector2
型を定義していないために必要な措置です。
関数の型の不一致
以下のシナリオの場合も、パス修飾の型やコード変換が必要です。
呼び出された関数または代入された変数が、
/UnrealEngine.com
モジュールからの型を受け入れようとしているにもかかわらず、/Verse.org
モジュールからの型が提供されている場合呼び出された関数または代入された変数が、
/Verse.org
モジュールからの型を受け入れようとしているにもかかわらず、/UnrealEngine.com
モジュールからの型が提供されている場合
関数の型が一致しない例
同じプロジェクトに次の 2 つの Verse ファイルが存在します。 FileOne.verse
は /UnrealEngine.com/Temporary/SpatialMath
モジュールからトランスフォームを表示する関数を定義します。 FileTwo.verse
は /Verse.org/SpatialMath
モジュールからの定数トランスフォームを定義し、この値を表示する関数を定義します。この関数は、FileOne.verse
から関数を呼び出します。
using { /UnrealEngine.com/Temporary/SpatialMath }
PrintTransform(T:transform):void=
Print("T: {T}")
using { /Verse.org/SpatialMath }
my_class := class:
T:transform = transform{}
DoPrint():void=
PrintTransform(T) # Compile error: This function parameter expects a value of type (/UnrealEngine.com/Temporary/SpatialMath:)transform, but this argument is an incompatible value of type (/Verse.org/SpatialMath:)transform.
その結果、トランスフォームの型が異なるモジュールで定義された 2 つの異なるトランスフォーム型を参照しているため、コンパイル エラーになります。
このコンパイル エラーを解消するには、トランスフォーム型を、適切なモジュールから正しいトランスフォーム型に変換する必要があります。 FileTwo.verse
のコードを編集して、FromTransform
関数を使います。この関数は、/Verse.org/SpatialMath
のトランスフォームを /UnrealEngine.com/Temporary/SpatialMath
のトランスフォームに変換します。
用意されている変換関数の全リストについては、次のセクションを参照してください。
# Include /UnrealEngine.com to access conversion functions
using { /UnrealEngine.com }
using { /Verse.org/SpatialMath }
my_class := class:
T:transform = transform{}
DoPrint():void=
PrintTransform(Temporary.SpatialMath.FromTransform(T)) # Compile error fixed after converting transform to the correct type from the proper module
ベクター、回転、トランスフォーム型の変換
/UnrealEngine.com
モジュールには、/UnrealEngine.com
モジュールと /Verse.org
モジュールで定義されている潜在的に曖昧な型を変換するための新しい変換関数がいくつか用意されています。
Unreal Engine の空間演算から Verse の空間演算へ
# Util function for converting a `vector3` from /UnrealEngine.com/Temporary/SpatialMath to a `vector3` from /Verse.org/SpatialMath.
FromVector3<public>(InVector3:(/UnrealEngine.com/Temporary/SpatialMath:)vector3)<reads>:(/Verse.org/SpatialMath:)vector3
# Util function for converting a `rotation` from /UnrealEngine.com/Temporary/SpatialMath to a `rotation` from /Verse.org/SpatialMath
FromRotation<public>(InRotation:(/UnrealEngine.com/Temporary/SpatialMath:)rotation)<reads>:(/Verse.org/SpatialMath:)rotation
# Util function for converting a `transform` from /UnrealEngine.com/Temporary/SpatialMath to a `transform` from /Verse.org/SpatialMath.
FromTransform<public>(InTransform:(/UnrealEngine.com/Temporary/SpatialMath:)transform)<reads>:(/Verse.org/SpatialMath:)transform
Verse の空間演算から Unreal Engine の空間演算へ
# Util function for converting a `vector3` from /Verse.org/SpatialMath to a `vector3` from /UnrealEngine.com/Temporary/SpatialMath.
FromVector3<public>(InVector3:(/Verse.org/SpatialMath:)vector3)<reads>:(/UnrealEngine.com/Temporary/SpatialMath:)vector3
# Util function for converting a `rotation` from /Verse.org/SpatialMath to a `rotation` from /UnrealEngine.com/Temporary/SpatialMath.
FromRotation<public>(InRotation:(/Verse.org/SpatialMath:)rotation)<reads>:(/UnrealEngine.com/Temporary/SpatialMath:)rotation
# Util function for converting a `transform` from /Verse.org/SpatialMath to a `transform` from /UnrealEngine.com/Temporary/SpatialMath.
FromTransform<public>(InTransform:(/Verse.org/SpatialMath:)transform)<reads>:(/UnrealEngine.com/Temporary/SpatialMath:)transform
Verse の空間数学の演算と関数
右手系
Verse 空間数学モジュールの関数は、すべて右手系の演算に統一されました。 その結果、掌性を明示していた関数のバリアントはすべて撤廃され、右手系がデフォルトの演算に置き換えられました。また、掌性を示すインジケータも関数名から撤廃されました。 vector3
と rotation
の乗算や、vector3
と transform
の乗算など、掌性の選択が必要なすべての演算は、右手系で行われます。
たとえば、次のコードは、上軸を中心に正の 90 度回転を作成し、それを単位の前ベクターに適用すると、単位左ベクターになることを示しています。
ForwardToLeft:rotation = MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
ForwardVector:vector3 = vector3{Forward := 1.0}
ResultantVector:vector3 = ForwardVector * ForwardToLeft # Apply rotation on the right of the multiplication operator
Print("{ResultantVector}") # {Forward = 0.000000, Left = 1.000000, Up = 0.000000} = {Forward = 1.000000, Left = 0.000000, Up = 0.000000} * {Axis = {Forward = 0.000000, Left = 0.000000, Up = 1.000000}, Angle = 90.000000}
トランスフォームと回転の乗算順序
内部的には、回転は行列で表現されていると考えることができます。 回転 R
をベクター v
に適用する場合、それは行ベクターの掛け算の順序で行われます。 つまり、ベクター v
は行ベクターとして扱われ、回転を適用するときは右側から掛け算を行い、結果は v' = v * R
となります。 ここで、得られるベクター v'
は新しい行ベクターです。 transform T
を vector v
に適用する場合も同様で、v' = v * T
のように右から掛ける形で計算し、得られるベクター v'
もまた行ベクターになります。
ベクターに回転やトランスフォームを適用する際は、順序が重要です。 たとえば、本などの物体を手に持ち、上面が上を向き、表紙/前面が自分の方を向くようにしてみてください。 まず、前方軸を中心に右手系で正の方向に 90 度回転させると、本の上面は右方向を向くようになります。 次に、左軸を中心に 90 度回転させると、本の表紙は上向きに、上面は右方向を向いたままになります。
では、同じ回転を今度は逆の順序で適用してみましょう。 まず、左軸を中心に 90 度回転させると、本の上面が前方を向き、表紙は上向きになります。 次に、前方軸を中心に 90 度回転させると、本の上面は前方を向いたままですが、表紙は右方向を向くようになります。 これは、先ほどとは異なる向きです。このことからも、回転の順序が重要であることがわかります。
このことを踏まえると、2 つの回転 R
と R'
をベクター v
に適用する場合は、意図した順序で回転が適用されるよう注意しなければなりません。 回転 R'
の前に回転 R
を適用する場合、正しい順序は v' = v * R * R'
となります。
同じ原則は回転同士の組み合わせにも当てはまります。 回転 R
に対して、R
の前に行う事前回転 PreR
を R
に適用し、R
の後に行う事後回転 PostR
を適用する場合、PreR * R * PostR
の順序で計算します。 同様に、トランスフォーム T
に対して、事前トランスフォーム PreT
と、事後トランスフォーム PostT
を適用する場合は、PreT * T * PostT
の順序で計算します。 続いて、これらをベクター v
に適用して新しいベクター v'
を得るには、上のとおり、v' = v * PreR * R * PostR
または v' = v * PreT * T * PostT
を計算します。
トランスフォームや回転の結合則も重要です。 ベクターに対して、たとえばトランスフォームのあとに回転といった複数の演算を Verse のコード上で同じ行に記述する場合、演算の結合が重要になります。 たとえば、以下のコードを書いたとします。
ForwardVector:vector3 = vector3{Forward := 1.0}
ForwardToLeftUpOne:transform = transform:
Translation := vector3{Up := 1.0}
Rotation := MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
UpToForward:rotation = MakeRotationFromEulerDegrees(90.0, 0.0, 0.0)
ResultantVector:vector3 = ForwardVector * ForwardToLeftUpOne * UpToForward # compiles
このコードは正常にコンパイルされます。演算が暗黙的に次のように結合されているためです。
ResultantVector:vector3 = (ForwardVector * ForwardToLeftUpOne) * UpToForward # compiles
しかし、次のように明示的に別の順序で結合しようとすると、以下のようになります。
ResultantVector:vector3 = ForwardVector * (ForwardToLeftUpOne * UpToForward) # error, no operator'*'(:transform,:rotation)
この場合はエラーとなります。 エラーを避けるには、ベクターとトランスフォーム、またはベクターと回転の最初の演算を明示的に括弧で囲んで結合します。
ResultantVector:vector3 = (ForwardVector * ForwardToLeftUpOne) * UpToForward # explicitly associated
空間数学演算の変更点
回転と回転の乗算
RotateBy
と UnrotateBy
が撤廃され、2 種類の回転を処理する乗算演算子に置き換えられます。
ForwardToLeft:rotation = MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
UpToForward:rotation = MakeRotationFromEulerDegrees(90.0, 0.0, 0.0)
UpToLeft:rotation = UpToForward * ForwardToLeft # RotateBy, correcting for handedness
逆回転 (unrotate) を行うには、まず回転を反転させ、次に乗算します。 UpToLeft
回転から UpToForward
回転に戻す場合は、ForwardToLeft
回転を反転し、それを UpToLeft
回転の右側に適用します。 以下は、その計算を段階的に示したものです。
# The following comments show the steps involved to reach the desired result.
# This involves multiplication on the right by the inverse, associativity, existence and definition of inverse rotation, and existence and definition of identity rotation
# Pseudocode Steps:
# UpToLeft * ForwardToLeft.Invert() = (UpToForward * ForwardToLeft) * ForwardToLeft.Invert() # multiply on the right by the same expression on both sides of the equality operator
# UpToLeft * ForwardToLeft.Invert() = UpToForward * (ForwardToLeft * ForwardToLeft.Invert()) # reassociate
# UpToLeft * ForwardToLeft.Invert() = UpToForward * IdentityRotation() # rotation * inverse = identity
# UpToLeft * ForwardToLeft.Invert() = UpToForward # rotation * identity = rotation
UpToForwardAgain:rotation = UpToLeft * ForwardToLeft.Invert() # UnrotateBy, correcting for handedness
Vector3 と回転の乗算
同様に、Rotate
と Unrotate
は撤廃され、vector3
に対して右側から rotation
を乗算する演算子に置き換えられました。
ForwardVector:vector3 = vector3{Forward:=1.0}
ForwardToLeft:rotation = MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
LeftVector:vector3 = ForwardVector * ForwardToLeft # Rotate, correcting for handedness
逆回転 (unrotate) を行うには、まず回転を反転させ、次にその回転にベクターを乗算します。 LeftVector
から ForwardVector
に戻す場合、ForwardToLeft
回転を反転し、それを LeftVector
の右側に適用します。
# The steps involved in achieving this calculation are very similar to the steps shown above for obtaining the correct expression for unrotating by a rotation
ForwardVectorAgain:vector3 = LeftVector * ForwardToLeft.Invert() # Unrotate, correcting for handedness
Vector3 とトランスフォームの乗算
TransformVector
と TransformVectorNoScale
は撤廃され、vector3
に対して右側から transform を乗算する演算子に置き換えられました。
DoubleLengthForwardToLeftTranslateUp := transform:
Translation := vector3{Left := 0.0, Up := 1.0, Forward := 0.0}
Rotation := MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
Scale := vector3{Left := 2.0, Up := 2.0, Forward := 2.0}
ForwardVector:vector3 = vector3{Forward:=1.0}
ResultantVector:vector3 = ForwardVector * DoubleLengthForwardToLeftTranslateUp # TransformVector
スケーリングなしのトランスフォーム、すなわち TransformVectorNoScale
を行いたい場合は、作成する transform
にデフォルトのスケール値を設定したままにしておいてください。
ForwardToLeftTranslateUpNoScale := transform:
Translation := vector3{Left := 0.0, Up := 1.0, Forward := 0.0}
Rotation := MakeRotationFromEulerDegrees(0.0, 90.0, 0.0)
ForwardVector:vector3 = vector3{Forward:=1.0}
ResultantVector:vector3 = ForwardVector * ForwardToLeftTranslateUpNoScale # TransformVectorNoScale