数学的な知識が必要だが、内積やベクトルの基本を知っていれば簡単に理解できる。Lingo だけ見ると何してるのかさっぱりだが、、、。 元ネタは、Director Online 、「3D Lingo: Using Vector Math to Move a Model In a Plane」。 これは非常に分かりやすかったので、参考にまとめてみた。 「内積0ってところがミソだったんだ。」と今まで、別ネタのソースを理解しまいまま使っていたので、理解し直しひと安心。SOLAのソースも書き直した。
--- 基本事項 ---
[ ベクトルの加法 ]
Lingoでは普通に + を用いる。
VectorC = VectorA + VectorB
[ ベクトルの内積 ]
Lingoでは dot() を用いる。
vecA. dot(VecB) = ( vecA.magnitude ) * ( vecB.magnitude ) * cosT
内積から、2つのベクトル間の角度のコサインを求められる。 コサイン 90度が 0であり、内積が 0ならベクトルは垂直である。 また、面積も求めることが出来る。 内積については以下を参考に。
http://www.crossroad.jp/mathnavi/kousiki/kousiki-index.html
http://www.ies.co.jp/LoveMath/2nd_grade/naiseki_n-j/naiseki_n1.html
--- 理論 ---
図で、直接求められる値は以下のとおり。
tObjectWorldLoc = myModel.worldPosition
tCameraWorldLoc = myCamera.worldPosition
tMouseWorldLoc = myCamera.spriteSpaceToWorldSpace(tSpriteLoc)
tStartVector = tObjectWorldLoc - tMouseWorldLoc
tRayVector = tMouseToCamera.normalize()
tZAxis = myCamera.getWorldTransform().zAxis
最終的に NewPosition を求める。 図から、NewPosition はふたつの式で求めることができる。
NewPosition = tCameraWorldLoc + tDistanceToModel * tRayVector
NewPosition = tCameraWorldLoc + tStartVector + VectorB
これらから、
tDistanceToModel * tRayVector = tStartVector + VectorB
内積を利用して、 VectorB を消去する。(お互いに垂直なベクトルの内積は「0」である。)
tDistanceToModel * tRayVector .dot(tZAxis) = tStartVector.dot(tZAxis) + VectorB.dot(tZAxis)
tDistanceToModel * tRayVector .dot(tZAxis) = tStartVector.dot(tZAxis) + 0
tDistanceToModel = tStartVector.dot(tZAxis) / tRayVector .dot(tZAxis)
よって
NewPosition = tCameraWorldLoc + ( tStartVector.dot(tZAxis) / tRayVector .dot(tZAxis) ) * tRayVector
--- 実践 ---
「tStartVector」は mouseDown で先にプロパティー化しておく。 後は TimeOut や exitframe などで毎フレームごと計算する。モデルの基点とクリック位置のオフセットも必要だが、元ネタを参考に。元ネタではマウスポイントでオフセットするが、ベクトルでオフセットしても良い。ちなみに僕は今までベクトルでオフセットしていたが、マウスポイントをオフセットするこの手法に目から鱗、簡単すぎる。ま、問題はあるが、、。
--- 応用 ---
tZAxis の値を代えることで、異なる平面上にロックできる。例えば、zx平面(水平面)上で移動したいなら、vector(0,-1,0)。移動したい平面と垂直のベクトルを使用する。(ホントかなあ?)