角度と座標の計算-三角関数を使う
内容 (What's Covered)
角度と座標を使った計算には、三角関数が必要になります。この文書では、三角関数について簡単な解説を加え、サンプルスクリプトをご紹介します。
三角関数の意義
高校で習う数学によると、三角関数はつぎのように定義されます。直角三角形の底辺と斜辺のなす角度を θ とすると、sinθ および cosθ は以下の式の値になります。
sinθ = 高さ/斜辺
cosθ = 底辺/斜辺
三角関数に苦手意識をもつ方は多いようです。その大きな理由のひとつは、この比率にどんな意味があるのかわからないということではないでしょうか。まず、その点からご説明しておきましょう。
X軸-Y軸からなる平面の直交座標に、原点を中心とした半径1の円を描きます。このとき原点からX軸に対して角度 θ の直線が円周と交わる点の座標は、(cosθ, sinθ) となります。これは、この交点からX軸に対して垂線を下ろし、角度θの線分と垂線およびX軸とでつくられる直角三角形を考えてみればわかります。斜辺となる半径が1ですから、高さがそのまま sinθ、底辺が cosθ となるからです。しかし、ここで はもう三角形のことは忘れて結構です。原点からX軸に対して角度 θ、距離1の座標が、(cosθ, sinθ) であるということさえ覚えておけばよいのです。
2次元の平面上の位置を特定する場合、直交座標を使うことが一般的です。Director のステージ上の位置も、直交座標 (x, y) で表わされます。しかし、平面上の位置を決める方法は、直交座標に限られるわけではありません。たとえば、レーダーを考えてみましょう。ここでは「北北西に 1km の距離」といった表現がされます。これは、位置を角度 (東西南北の方向) と距離 (km) とで定めているのです。そして、三角関数はこの角度と距離によって定義された位置を、直交座標に変換するための関数なのです。角度θ、距離1の座標は (cosθ, sinθ) でした。すると、角度 θ で距離Dの位置座標は (D*cosθ, D*sinθ) となります。実際の値は、関数電卓や Lingo で簡単に求めることができます。
tan() と atan() 関数
三角関数にはもうひとつ tan があります。これはつぎのように定義されます。
tanθ = 高さ/底辺 = sinθ/cosθ
X軸-Y軸の直交座標で表現すると、X軸の距離1の点 (1, 0) から立てた垂線と角度 θ の線分との交点の高さになります。元々は、太陽の角度と物体の影から、物体の実際の高さを計算するという用途で考えられたようです。影の長さがDなら、物体の高さは D*tanθ になるわけです。
ところで関数には、一般に逆関数というものが考えられます。そして三角関数にも逆関数があります。これは三角関数の値から、逆に角度を返す関数です。数学では3つの三角関数それぞれについて、逆関数が定義されています。ただ Lingo では、逆関数は 'tan()' についてのみ提供されています。これが 'atan()' 関数です (アークタンジェントと呼びます)。座標から三角関数を計算する場合、tan が一番簡単です (sin や cos を求めるには、ピタゴラスの定理を使って原点から座標までの距離を計算しなけ ればなりません)。座標が (x, y) のとき、角度をθするとつぎの式が成り立ちます。
tanθ = y / x
そこで逆関数 atan を使えば、角度θはつぎの式で求められます。
θ = atan(tanθ) = atan(y / x)
ところが、atan 関数には重要な制約があります。角度が 45度のとき tan は1です。しかし角度が 135度のときも、やはり tan は1になるのです。つまり、三角関数 tan の値からは、X座標が正の場合と負の場合とを区別できません。一般に、関数はかならずひとつの値を返さなければなりません。そこで、逆関数 atan の値は、X座標が正の場合の -90度から 90度の値にするという制約が与えられたのです。したがって、'atan()' 関
数を使って座標から角度を求めたいときには、座標xとyそれぞれの正負を合わせて考える必要があります。
ラジアン
三角関数をとっつきにくいものにしているもうひとつの理由として、角度の単位があります。三角関数では角度に「ラジアン」という単位を使うことが多いのです。Lingo でも三角関数には、ラジアンが用いられています。360度は 2πラジアンです。すると1ラジアンは、約 57.3度ということになります。なぜこんな半端な単位を使うのでしょうか。
三角関数は円運動の分析にも使われます。運動を解析する際には、運動の距離を知る必要があります。ところが度数で示される角度は、距離と直接の関係がありません。そこで、また半径1の円を基準に考えます。半径1の扇形の角度を、その円弧の長さで表わした単位がラジアンなのです。したがって、360度は円周の長さである2πになるわけです。
もっとも使う理由がわかったところで、1ラジアンがどれくらいの角度になるのか想像できる方は少ないでしょう。そこで、度数で表わされる角度とラジアンとの変換の ユーザー定義関数(ムービースクリプト)をご紹介しておきます。
-- [度数をラジアンに変換するムービースクリプト]
on xDegsToRads nDegrees
nRadians = nDegrees * PI / 180
return nRadiansend
-- [ラジアンを度数に変換するムービースクリプト]
on xRadsToDegs nRadians
nDegrees = nRadians * 180 / PI
return nDegreesend
座標から角度を計算するスクリプト
逆関数 atan を使って、座標から角度を返すユーザー定義関数を考えてみましょう。
X-Yの直交座標で考えたとき、atan はX座標の正負を区別できませんでした。ですから、まずX座標が正か負かを判定し、2つの場合を分けて処理する必要があります。さらに、Xが0の場合も問題です。前述のように、tan は y/x で計算されます。xが0のとき、数学的には tan が「∞(無限大)」ということになるだけです。しかし、コンピュータには無限大という値はありませんし、0による割算はエラーになります。したがって、この場合もまた別処理が必要です。
Y座標の正負も分ける必要があります。なぜなら、Yが負の値を取ると、atan がマイナスの値で返されるからです。角度を0から 2π(360度) の値で返す関数にしないと、実用になりません。では、以上の場合分けをして、処理の大筋を考えます。
まずX座標が正の場合です。Y座標も正なら atan 関数の値がそのまま使えます。Y座標が負のときは、atan 関数の値はマイナスです。ですから、2π(360度) に関数の値を加えればよいでしょう (関数の値がマイナスなので、数値は減ります)。
つぎにX座標が負の場合にはどうでしょう。Y座標が正のときは、π(180度) から atan 関数の値を引きます。Y座標が負のときは、π(180度) に X軸 (正方向) との角度を加えることになります。しかし、atan 関数はマイナスの値です。ですから、計算式上はこの場合もやはり π(180度) から関数の値を引けばよいのです (関数の値がマイナスなので、数値は増えます)。したがって、X座標が負のときは、Y座標の正負を区別することなく、π(180度) マイナス atan 関数の値というとになるのです。
最後にX座標が 0 のときです。この数値はもう決まっています。Y座標が正なら π/2(90度)、負なら 3π/2(270度) です。もうひとつY座標が 0 のときも考えておく必要があります。このときは角度 0 としましょう。
以上の処理をムービースクリプトにしたのが、つぎのユーザー定義関数です。処理手順は、これまでご説明したとおりです。スクリプトにはコメントを加えてありますので、ご参照ください。角度はラジアンで処理しています。度数にしたいときは、前にご紹介したムービースクリプトで簡単に変換できます。
on xGetRadians nX, nY
if nX = 0 then -- X座標が0のとき
if nY = 0 then -- Y座標も0のとき
nRadians = 0
else
if nY > 0 then -- Y座標が正のとき
nRadians = PI / 2
else -- Y座標が負のとき
nRadians = PI * 3 / 2
end if
end if
else -- X座標が0以外のとき
nTan = nY.float / abs(nX) -- tan値を計算
nRadians = atan(nTan) -- atan値を取得
if nX < 0 then -- X座標が負のとき
nRadians = PI - nRadians
else -- X座標が正のとき
if nY < 0 then -- Y座標が負のとき
nRadians = 2 * PI + nRadians
end if -- Y座標も正ならatan値をそのまま使う
end if
end if
return nRadians -- 結果をラジアンで返す
end
スプライトをマウスポインタの方向に向かせるビヘイビア
では、座標から角度を計算するムービースクリプトを利用した、簡単なビヘイビアのサンプルをご紹介しましょう。以下のビヘイビアをビットマップのスプライトに設定すると、スプライトがマウスポインタの方向に回転します。X軸正の方向が基準となりますので、スプライトのマウスポインタに向けたい部分を水平方向右向きに配置します。ビヘイビアの基本的なスタイルについては、別途 ビヘイビアの基本的なスタイル (dr0229) をご参照ください。
property pMySpriteproperty plMyLoc
on beginSprite me
-- スプライトへの参照をプロパティに格納
pMySprite = sprite(me.spriteNum)
-- スプライトの位置座標をプロパティに格納
plMyLoc = pMySprite.loc
end
on exitFrame me
-- スプライトからマウスへの相対座標を計算
lVector = the mouseLoc - plMyLoc
-- 角度をラジアンで取得
nRadian = xGetRadians(lVector.locH.float, lVector.locV.float)
-- 度数に変換した角度にスプライトを回転
pMySprite.rotation = xRadsToDegs(nRadian)
end
sagyou
ここで、疑問を持たれた方もあるかもしれません。これまでの説明では、数学的なX-Yの直交座標を前提にしてきました。つまり、垂直方向のY軸は上にいくほど数値が大きくなります。ところが、Director で扱う座標系は左上を原点とし、垂直方向のY軸は下に向かって数値が増えます。この点を考慮して、角度を計算するスクリプトを修正する必要はないのでしょうか。結論からいえば、その必要はありません。直交 座標は、座標軸が垂直に交わってさえいればよいのです。グラフ用紙を上下逆さにしようが、裏返そうが (こうすると Director の座標系と同じになります)、座標と角度との関係に変わりはないからです。
| : | 2006-04-22 |
|---|---|
| : | 228532 |
| : | Macintosh, Windows |
| : | MX2004 |
| : | http://go.adobe.com/kb/ts_228532_ja-jp |
| : | director |