~~NOTOC~~ ====== Quaternion ====== このページは過去の情報となります。\\ 新しいスクリプトリファレンスは**[[https://developer.virtualcast.jp/vci-docs/api/|こちら]]**になります。 QuaternionはX,Y,Z,Wの4つの値を持ったクラスです。\\ 回転を表現するクラスですが、値を直接指定してQuaternionを直接変化させる事はありません。\\ 基本的に、変化させたい場合は下記にあげる関数を使用して変化させます。 ^ 名前 ^ 説明 ^ バージョン ^ | [[vci/script/reference/quaternion#初期化について | _new ]] | 初期化 | | [[vci/script/reference/quaternion#FromToRotation ]] | fromDirection→toDirectionへ回転 | | [[vci/script/reference/quaternion#Inverse ]] | rotationと逆のQuaternionを作成 | | [[vci/script/reference/quaternion#Slerp ]] | a と b の間を t で球面補間 [0≦t≦1] | | [[vci/script/reference/quaternion#SlerpUnclamped]] | t の値を制限しないSlerp | | [[vci/script/reference/quaternion#Lerp ]] | a と b の間を t で線形補間 [0≦t≦1](バネ) | | [[vci/script/reference/quaternion#LerpUnclamped ]] | t の値を制限しないSlerp(バネ) | | [[vci/script/reference/quaternion#AngleAxis ]] | Axisのベクトルを軸にAngle度回転 | | [[vci/script/reference/quaternion#LookRotation ]] | forwardとupwardsに対応する方向に設定 | | [[vci/script/reference/quaternion#Dot ]] | 2の回転 a と b の内積を返す | | [[vci/script/reference/quaternion#Angle ]] | 2つの回転 a と b の角度を返す | | [[vci/script/reference/quaternion#Euler ]] | オイラー角でQuaternionを作成 | | [[vci/script/reference/quaternion#ToAngleAxis(使用不可) ]] | 回転を座標に対する角度の値 (AngleAxis) に変換 | | [[vci/script/reference/quaternion#RotateTowards ]] | 2つのQuaternion間の最大となる回転を補間 | | [[vci/script/reference/quaternion#Normalize ]] | qのノルムを1にし、単位Quaternionを返す | | GetHashCode | 使用しない | | [[vci/script/reference/quaternion#ToString ]] | 文字列に変換 | | [[vci/script/reference/quaternion#ToEuler(非推奨) ]] | オイラー角に変換する場合は'eulerAngles'が推奨 | | [[vci/script/reference/quaternion#ToEulerAngles(非推奨) ]] | オイラー角に変換する場合は'eulerAngles'が推奨 | | [[vci/script/reference/quaternion#identity ]] | (0, 0, 0, 1)の基準となるQuaternionを作成 | | [[vci/script/reference/quaternion#eulerAngles ]] | 回転をオイラー角の値で返す | | [[vci/script/reference/quaternion#normalized]] | 単位Quaternionを返す | | x num | ※基本的に直接Quaternionを変更しない | | y num | ::: | | z num | ::: | | w num | ::: | | kEpsilon num | | ===== 初期化について ===== サンプル\\ rotate = Quaternion.identity print(rotate) 実行結果\\ (0.0, 0.0, 0.0, 1.0) 基本的に''Quaternion.identity''でQuaternionを初期化します。\\ ''__new()''でも宣言可能ですが、Quaternionを直接扱う事は少ないです。\\ また、上記のサンプルでは''rotate.x = 1''とする事で、各要素に対して直接アクセス可能ですが、Quaternionを操作する際は用意された関数を使い操作する事を推奨します。\\ サンプル\\ --SunItemのゲームオブジェクト名とGetTransform("")の("")の中を一致させる必要があります。 Subitem = vci.assets.GetTransform("Subitem") function onUngrab() local rotate = Quaternion.identity local velocity = Vector3.zero Subitem.SetLocalRotation(rotate) Subitem.SetVelocity(velocity) end 実行結果\\ (VCIをつかんだ状態から離すと、回転(Rotation)が初期状態(x = 0, y = 0, z = 0)になります。) ''SetLocalRotation()''を使う事で、作成したQuaternionの姿勢にする事ができます。\\ その際は、姿勢を変更した後に''SetVelocity()''を0にする事で、rigidbodyによる姿勢の更新を防ぎます。 ===== FromToRotation ===== **FromToRotation fun(fromDirection: Vec3, toDirection: Vec3): Quaternion**\\ 2つのベクトルの差分をQuaternionで返します。 === サンプル === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() local Vec1 = Subitem.GetUp(); local Vec2 = Subitem2.GetPosition() - Subitem.GetPosition(); local Rot1 = Subitem.GetRotation(); local Rot2 = Quaternion.FromToRotation(Vec1, Vec2); local Rot3 = Rot2*Rot1; Subitem.SetRotation(Rot3); end === 実行結果 === Subitemの上面(up)が常にSubitem2の方向を向きます。 ただし、LookRotationを使った方が安定します。 ===== Inverse ===== **Inverse fun(rotation: Quaternion): Quaternion**\\ 逆のQuaternionを作成します。 === サンプル === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() Subitem.SetRotation(Quaternion.Inverse(Subitem2.GetRotation())); end === 実行結果 === Subitemが常にSubitem2と逆に回転します。 ===== Slerp ===== **Slerp fun(a: Quaternion, b: Quaternion, t: num): Quaternion**\\ 第一引数 と 第二引数 の間を 第三引数の値 で球面補間します。\\ 第三引数の値 は[0, 1]の範囲です。 === サンプル1 === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() -- time [0,1] local time = 0.5 * math.sin(os.time()) + 0.5 local rotate = Quaternion.identity Subitem.SetRotation(Quaternion.Slerp(rotate, Subitem2.GetRotation(), time)); end === 実行結果1 === Subitemが(0,0,0,1)からSubitem2の角度へ球面補間を行って往復回転します。 \\ === サンプル2 === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() Subitem.SetRotation(Quaternion.Slerp(Subitem.GetRotation(), Subitem2.GetRotation(), 0.01)); end === 実行結果2 === SubitemがSubitem2の角度へゆっくり回転します。 ===== SlerpUnclamped ===== **SlerpUnclamped fun(a: Quaternion, b: Quaternion, t: num): Quaternion**\\ Slerpと同じですが、第三引数 の値を[0, 1]に制限しません。 === サンプル === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() -- time [0,2] local time = math.sin(os.time()) + 1 local rotate = Quaternion.identity Subitem.SetRotation(Quaternion.SlerpUnclamped(rotate, Subitem2.GetRotation(), time)); end === 実行結果 === Subitemが(0,0,0,1)からSubitem2の角度を通り越して球面補間を行って往復回転します。 ===== Lerp ===== **Lerp fun(a: Quaternion, b: Quaternion, t: num): Quaternion**\\ 第一引数 と 第二引数 の間を 第三引数の値 で線形補間します。\\ 第三引数の値 は[0, 1]の範囲です。 === サンプル1 === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() -- time [0,1] local time = 0.5 * math.sin(os.time()) + 0.5 local rotate = Quaternion.identity Subitem.SetRotation(Quaternion.Lerp(rotate, Subitem2.GetRotation(), time)); end === 実行結果1 === Subitemが(0,0,0,1)からSubitem2の角度へ線形補間を行って往復回転します。 \\ === サンプル2 === --SunItemのゲームオブジェクト名とGetTransform("")の("")の中を一致させる必要があります。 Subitem = vci.assets.GetTransform("Subitem") base = Quaternion.identity target = Quaternion.Euler(0, 90, 0) function update() local time = math.abs(math.sin((vci.me.FrameCount / 60))) local lerp = Quaternion.Lerp(base, target, time) Subitem.SetRotation(lerp) end === 実行結果2 === (Subitemがゆっくりと振幅するように90度回転します。) 説明\\ 基準となるQuaternionと、ターゲットになるQuaternionの中間を補間するQuaternionを作成します。\\ 0-1の値で指定すると指定した値の%に応じたQuaternionが返ってきます。\\ ゆっくりと値を変化させることで、特定の姿勢から特定の姿勢にゆっくりと変化します。 ===== LerpUnclamped ===== **LerpUnclamped fun(a: Quaternion, b: Quaternion, t: num): Quaternion**\\ Lerpと同じですが、第三引数 の値を[0, 1]に制限しません。 === サンプル === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() -- time [0,2] local time = math.sin(os.time()) + 1 local rotate = Quaternion.identity Subitem.SetRotation(Quaternion.LerpUnclamped(rotate, Subitem2.GetRotation(), time)); end === 実行結果 === Subitemが(0,0,0,1)からSubitem2の角度を通り越して線形補間を行って往復回転します。 ===== AngleAxis ===== **AngleAxis fun(angle: number, axis: Vector3): Quaternion**\\ Vector3で方向を指定してその方向を回転の軸とし、angleの値だけ回転させます。\\ サンプルでは''axis = Vector3.up''で下から上に向かった軸を作成して回転させています。\\ angleは0~360の値で指定します。 === サンプル === --SunItemのゲームオブジェクト名とGetTransform("")の("")の中を一致させる必要があります。 local _subitem = vci.assets.GetTransform("Subitem") local _angle = 0 function onUse() _angle = _angle + 10 if _angle > 360 then _angle = 0 end print("angle : ".._angle) local axis = Vector3.up local rotate = Quaternion.AngleAxis(_angle, axis) Subitem.SetRotation(rotate) end === 実行結果 === (VCIをUseすると10度づつ回転します) ===== LookRotation ===== **LookRotation fun(forward: Vector3, upwards: Vector3): Quaternion**\\ upのベクトルと、forwardのベクトルを使用して、Quaternionを作成します。\\ サンプルでは''rotate''と''vci.assets.GetTransform(target).GetRotation()''は同じ値をとります。 === サンプル1 === function onUngrab(target) local forward = vci.assets.GetTransform(target).GetForward() local up = vci.assets.GetTransform(target).GetUp() local rotate = Quaternion.LookRotation(forward, up) print(rotate) print(vci.assets.GetTransform(target).GetRotation()) end === 実行結果1 === (0.0, -0.3, -0.1, 1.0) (0.0, -0.3, -0.1, 1.0) (0.0, 0.9, 0.0, 0.4) (0.0, 0.9, 0.0, 0.4) \\ === サンプル2 === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() local Vec = Subitem2.GetPosition() - Subitem.GetPosition(); Subitem.SetRotation(Quaternion.LookRotation(Vec)); end === 実行結果2 === Subitemが常にSubitem2の方を向きます。 ===== Dot ===== **Dot fun(a: Quaternion, b: Quaternion): number**\\ 2つのQuaternionを比較して、その内積をとります。\\ 戻り値は''-1~1''の間になります。 === サンプル === function onUngrab(target) local rotate = vci.assets.GetTransform(target).GetRotation() local base = Quaternion.identity print(Quaternion.Dot(rotate, base)) end === 実行結果 === 0.886427819728851 -0.289039075374603 (VCIを手から離した時に、つかんだアイテムとbaseとの内積をとります) ===== Angle ===== **Angle fun(a: Quaternion, b: Quaternion): number**\\ 第一引数 と 第二引数 の角度を返します。 === サンプル === function onUse(use) local base = Quaternion.Euler(0, 10, 0) local target = Quaternion.Euler(0, 135, 0) print(Quaternion.Angle(base, target)); end === 実行結果 === (125) ===== Euler ===== **Euler fun(x: number, y: number, z: number): Quaternion**\\ 回転をオイラー角(0°~360°)の値で指定し、Quaternionを作成します。\\ 要素の指定は前から X → Y → Z の順に行います。 === サンプル === ---SunItemのゲームオブジェクト名とGetTransform("")の("")の中を一致させる必要があります。 Subitem = vci.assets.GetTransform("Subitem") function onUngrab() local rotate = Quaternion.Euler(30, 45, 60) local velocity = Vector3.zero Subitem.SetLocalRotation(rotate) Subitem.SetVelocity(velocity) end === 実行結果 === (VCIをつかんだ状態から離すと、回転(Rotation)が(x = 30, y = 45, z = 60)になります。) ===== ToAngleAxis(使用不可) ===== **ToAngleAxis fun(angle: usertype, axis: usertype)**\\ 回転を座標に対する角度の値 (AngleAxis) に変換します。\\ ※現在使用することが出来ません。 === サンプル === function onUse(use) local rotate = Quaternion.AngleAxis(30.0, Vector3.up) local angle = 0 local axis = Vector3.zero print(rotate) rotate.ToAngleAxis(angle, axis) print(angle) print(axis) end === 実行結果 === 現在ToAngleAxisを使用することができません。 ===== RotateTowards ===== **RotateTowards fun(from: Quaternion, to: Quaternion, maxDegreesDelta: num): Quaternion**\\ 2つのQuaternion間の最大となる回転を補間します。 === サンプル === local Subitem = vci.assets.GetTransform("Subitem") local Subitem2 = vci.assets.GetTransform("Subitem2") function updateAll() local move = Quaternion.RotateTowards(Subitem.GetRotation(), Subitem2.GetRotation(), 0.25,0) Subitem.SetRotation(move) end === 実行結果 === Subitemの回転がSubitem2の角度と同じになるように毎フレーム0.25度ずつ回転します ===== Normalize ===== **Normalize fun(q: Quaternion): Quaternion**\\ qのノルムを1にし、単位Quaternionを返します。\\ 引数なしで四元数quatの関数として使った場合(例:quat.Normalize())、オブジェクト側(quat)の値の変更がなされます。 === サンプル === function onUse(use) local zero = Quaternion.identity local rotate = Quaternion.Euler(0, 280, 180) rotate.x = 2 print(rotate) print(Quaternion.Normalize(rotate)) end === 実行結果 === (2.0, 0.0, -0.8, 0.0) (0.9, 0.0, -0.4, 0.0) ===== ToString ===== **ToString fun(): string**\\ 文字列に変換します。 === サンプル === function onUse(use) local rotate = Quaternion.identity print(rotate.ToString()) end === 実行結果 === "(0.0, 0.0, 0.0, 1.0)" ===== ToEuler(非推奨) ===== **ToEuler fun(): Vector3**\\ ToEuler()は非推奨です。オイラー角に変換する場合は'eulerAngles'が推奨されます。\\ -πrad ~ πradの範囲で値を返します。 === サンプル === function onUse(use) local rotate = Quaternion.Euler(60, 270, 30) print(rotate) print(rotate.ToEuler()*180/3.14159265359) end === 実行結果 === (60, -90, 30) ===== ToEulerAngles(非推奨) ===== **EulerAngles fun(x: number, y: number, z: number): Quaternion**\\ ToEulerAngles()は非推奨です。オイラー角に変換する場合は'eulerAngles'が推奨されます。\\ -πrad ~ πradの範囲で値を返します。 === サンプル === function onUse(use) local rotate = Quaternion.Euler(60, 270, 30) print(rotate) print(rotate.ToEulerAngles()*180/3.14159265359) end === 実行結果 === (60, -90, 30) ===== identity ===== **identity Quaternion**\\ 基本的にQuaternion.identityでQuaternionを初期化します。 === サンプル === rotate = Quaternion.identity print(rotate) === 実行結果 === (0.0, 0.0, 0.0, 1.0) ===== eulerAngles ===== **eulerAngles Vector3**\\ Quaternionの値をオイラー角の値で返します。\\ ''eulerAngles.''の後にアクセスしたい要素を指定する事で個別にアクセスする事も可能です。 === サンプル === euler = Quaternion.Euler(30, 45, 60) print(euler) print(euler.eulerAngles) print(euler.eulerAngles.x) print(euler.eulerAngles.y) print(euler.eulerAngles.z) === 実行結果 === (0.4, 0.2, 0.4, 0.8) (30.0, 45.0, 60.0) 30.0000019073486 45 60.0000038146973 ===== normalized ===== **normalized Quaternion** 単位Quaternionを返します。 === サンプル === function onUse(use) local rotate = Quaternion.Euler(0,​ 280, 180) rotate.x = 2 print(rotate) print(rotate.normalized) end === 実行結果 === (2.0, 0.0, -0.8, 0.0) (0.9, 0.0, -0.4, 0.0)