縁の下の力持ち関数2
今章は、三角関数を扱う数学関数の紹介を行います。
もし、おかしい表現等がございましたらご報告願います。
尚、「ラジアンとは」等の言葉の意味について、軽く触れる程度で詳細には説明しません。
詳しく知りたい場合は、他の専門的なページを検索し、そちらで調べてください。

初めにHSP命令ではなく、三角関数側の説明を少しだけしておきます。

30(60)度の直角三角形は、辺の長さの比が1:2:3で、
45度の直角三角形は、1:1:2と学校で習いました(または、これから習います)ね。
サイン(正弦)30は2分の1、コサイン(余弦)30は2分の3、タンジェント(正接)30は3分の1、
サイン45は2分の1、コサイン45も2分の3、タンジェント45は1、
サイン60は2分の3、コサイン60は2分の1、タンジェント60は3というのもありました。
さて、このサインX度や、コサインY度という表記は、
「円周を360等分した弧の中心に対する角度」を基本の単位とする度数法です。
360という数は1〜10のうち割り切れないのは7だけ、と約数が多く除算しやすい数であるものの、
それは数学においては重要な要素ではないということで、一般的には度数法を用いるのではなく、
「円の半径に等しい長さの弧の中心に対する角度(平面角)」を単位とする
弧度法(ラジアンで測ること)を代わりに用いるようです。


ラジアンというのもまた角度の単位で、度数法の360度が2πラジアンになるということから、
1ラジアン(=360/2π)は、約57.295779度になり、
ラジアンから度への変換は「ラジアン×180÷π」で近似値を求められるわけですが、
HSP3.2β4からはhspmath.asより標準マクロとして移行された下記関数で変換できます。
角度 = rad2deg(ラジアン角)
角度変換後の角度受取先を指定する。
ラジアン角弧度法によるラジアンを単位とした実数値を指定する。
また、1度は、π/180ラジアンということで、度からラジアンへの変換は「度×π÷180」です。 コレもHSP3.2β4からはhspmath.asより標準マクロとして移行された下記関数で変換できます。
ラジアン角 = deg2rad(角度)
ラジアン角変換後のラジアン値の受取先を指定する。
角度変換対象の度数法を単位とした角度を指定する。
尚、「π」は円周率のことで、当時は計算が面倒だったのに、一時「約3」で計算することになった 3以下の小数が14159265358979…と無限に続く値(無理数)のことです。 円周率を示すM_PIもHSP3.2β4から標準マクロとして定義されましたので、 HSP3.2以降を使っているのであれば円周率をスクリプトに直接書く時はM_PIを使う方が良いでしょう。 尚、確認する時は「mes M_PI」としても仕様上、小数第6位しか表示されませんので、 「mes strf("%.16f", M_PI)」と言うようにすると良いでしょう。 以上を前説としまして、HSP命令の紹介をしていきます。 縦方向の動きを制御する時に利用できる関数の紹介から始めましょう。
正弦値 = sin(角度)
正弦値パラメータで指定した角度を元にしたサインを返す。
角度弧度法によるラジアンを単位とした実数値を指定する。
SINeの略で、ラジアンを単位とした値をパラメータに渡すことでY軸(縦)方向の大きさを返します。 角度0を真右(3時)方向として、反時計回りに増加し、 90度が真上(12時)方向、180度が真左(9時)方向、270度が真下(6時)方向になります。 つまり、90度が一番大きく、270度が一番小さくなります。 90度は、前説の「度からラジアンへの変換」の通り、90×π÷180で約1.57となります。 注意点ですが、度からラジアンへ変換を行う場合、記述するパラメータの順番に気をつけましょう。 HSPは、左側にある型に合わせる仕様となっているため、 例えば45度を変換する場合に「45×3.14÷180」と演算させると、整数値型の0となりますが、 「3.14×45÷180」と、実数型を先にすることで、0.785と求めることが出来ます。 見落としがちな点ですが、ここは常に気をつけるようにしなければ精度を落としかねません。
 1
 2
	mes 45 * 3.14 / 180 // 整数型で演算
	mes 3.14 * 45 / 180 // 実数型で演算
さて、sin関数のパラメータに指定してみると、結果は最大値を示す「1.000000」が返りましたね。 一番小さい270度で試してみると、270×π÷180で約4.71ですが、 4.71にしてみると「−0.999997」となり、最小値を示す「−1.000000」ではありません。 これは、4.71の元になった「πの精度が3.14と低かったため」であります。 既に書いたように、πは3.14159…と無限に続く値なので、πの精度をもう少し上げて、 3.141としてみるか、結果値4.71を1桁増やして4.711としてみると、 期待通りに「−1.000000」と表示されましたね。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	height = 0   // ジャンプの高さ(現在位置ではない)
	power  = 10  // ジャンプの速さ(「180÷power−1」回で着地する)
	speed  = 200 // 1回の移動量(最大でspeed座標分上に動く)
	ground = 300 // 地面の位置(基点)
	char   = 80  // キャラクタサイズ
	font msgothic, char
	repeat
		wait 3
		gosub *check
		gosub *draw
	loop

*draw
	redraw 0
	color 255, 255, 255 : boxf
	color 100, 50 : boxf , ground + char
	color , 255 : line , ground - speed, ginfo_winx, ground - speed
	color , , 255
	title str(position)
	pos 300, position : mes "♀"
	redraw
	return

*check
	stick key, 16, 1
	// スペースキーが押されたかジャンプ中なら上昇及び下降
	if (key & 16) || (height) {
		height = (height + power) \ 180 // 角度による高さ
	}
	position = ground - sin(3.14 * height / 180) * speed // 現在位置の算出
	return
続いて、横方向の動きを制御する関数の説明です。
余弦値 = cos(角度)
余弦値パラメータで指定した角度を元にしたコサインを返す。
角度弧度法によるラジアンを単位とした実数値を指定する。
COSineの略で、パラメータにはラジアンによる実数値を指定すると、X軸(横)方向の制御が出来ます。 横の動きと言えども、3時の方向から反時計回りに動く時のX座標の動きと言う意味で、 0度の時が最大で1.0となり、180度(約3.14)が最小で−1.0となります。 横方向の制御時に使用しなければ、用途はその限りではないことでしょう(但し、範囲は−1.0〜1.0)。 尚、sin関数と同じく、180度を示す最小値3.14だと精度が低いために−0.999999が返ります。 −1.0を取得するためには、もう1桁以上多く3.141…を指定してください。 cos関数だけでなく、sin関数にも言える関数の実際の用途ですが、 Y軸方向にサイン、X軸方向にコサインの動きを付けると時計回りに円運動を行うことが出来ます。 Y方向に一定量ずつ増加、または減少させ、X方向にコサインカーブを付けるか、 X方向に一定量ずつ増加、または減少させ、Y方向にサインカーブを付けると波の動きに、 X方向のコサイン値または、Y方向のサイン値に一定量ずつ増加、または減少させると渦巻き状に動きます。 他にも星芒形を描くアステロイド、連珠形を描くレムニスケート、葉形線と呼ばれるストロフォイド、 円に巻きつけた糸をほどく動きインボリュートなど、数多くの曲線はサイン・コサインを用いて実現します。 何らかのツールでグラフに使用するなり、シューティングゲームで弾の動きに使用するなり、 キャラクタのアクションに用いたり、応用方法はいろいろあります。 これらの動きをどこで用いるかはプログラムを組むあなたが考えてください。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
	repeat 500
		redraw 0
		x = cos(0.15 * cnt) * 1.0 * cnt + ginfo_winx / 2 // コサインを利用してX座標を決定
		y = sin(0.15 * cnt) * 0.9 * cnt + ginfo_winy / 2 // サインを利用してY座標を決定
		if cnt \ 10 = 0 {
			font msmincho, cnt / 10 + 7 // 徐々に拡大させる
			redraw                      // 10回に1回だけ描画途中を見せる
			wait 1
		}
		pos x, y : mes "★"
	loop
	redraw
	dialog "完了"
続いて、X軸に対するY軸の移動比率を取得できるタンジェント関数の紹介です。
正接値 = tan(角度)
正接値パラメータで指定した角度を元にしたタンジェントを返す。
角度弧度法によるラジアンを単位とした実数値を指定する。
TANgentの略で、パラメータにラジアンによる角度を指定すると、正接値を取得できます。 タンジェントの覚え方は「底辺分の高さ」、言い方を変えると「高さ÷底辺」でしたから、 「Y軸方向の移動量÷X軸方向の移動量」と言うことになり、XとYの比率を取得出来ることを意味します。 例えば、Xに1動いた時にYも1動いた時の角度である45度をラジアンに直して表示させて見ると 「mes tan(3.141592 * 45 / 180)」で、予想通りに比率1倍と返ることが確認できます。 πが3.14だと、精度が低く0.0008程の誤差が出てしまいますので、上記桁まで指定して確認しました。 角度0は、三角形としてありえないので、タンジェントも0となります。 90度も同様にしてありえないのですが、90度の近似値を指定してみると、 8バイト実数の有効桁でオーバーフローする、とてつもなく大きな値となりました。 90度の位置はXに0、Yに1動くことを意味しますから、「1÷0」で、計算できませんよね。 どんな電卓でも計算できませんから、オーバーフローしようがそれで正常なのです。 さて、タンジェントは高さを求める時に役立つ関数で、「距離×タンジェント角度」で求まります。 例えば、自分の前に建っているビルの高さを求めるのに、ビルの立っている位置までの距離と、 天辺を見上げる角度さえわかれば、実際に測らずともそのビルの高さを知ることが出来るというものです。 このほかの応用としての使い道はサイン・コサインと比べて限られているかもしれません。 …ということで、基本の三角形で具体的な長さを求めてみましょう。 30度の直角三角形は1:2:3の比です。 60度のラジアンが60×π÷180とすると、底辺に対して高さは約1.732051となりまして、 底辺の長さである1を、高さを求める式に代入しますと、そのまま約1.732051となりますね。 前章のsqrt関数で、3の語呂あわせは「人並みに奢れや(=17320508)」と書いたのを覚えてますか? 1.732051というのは、17320508…を四捨五入して丸められた数なので、 高さ1.732051は、この3のことだろうというのが分かると同時に、 角度と底辺の長さから、きちんと高さが求められたことも実証されましたね。 当然、30度と底辺に3を持ってきても求まりますよ。 30度のタンジェント値は約0.577350となり、見たことない数に一瞬はアレ?と思いますが、 この数は比率ですから、底辺の1.73205117320508を掛けると、高さ1.0が得られます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	x = 100
	kaku = 45
	objsize 100, 20
	pos  10, 10 : mes "X方向の距離"
	pos 150, 10 : input x, , , 8
	pos  10, 50 : mes "角度"
	pos 150, 50 : input kaku, , , 3
	pos 150, 90 : button "Y方向の距離", *calc
	stop

*calc
	y = tan(3.14159265358979 * kaku / 180) * x // 比率からY距離を求める
	color 255, 255, 255 : boxf , 150 : color
	pos 100, 150 : mes "※下はイメージであり、角度も距離も適当です。"
	pos 30, 395 : mes " ○\n(自分)"
	color 255
	line 50, 400, 350, 400
	pos 350, 410 : mes "X方向の距離(" + x + ")"
	line 50, 400, 350, 200
	pos 100, 300 : mes "角度(" + kaku + ")"
	color , , 255
	line 350, 200, 350, 400
	pos 360, 280 : mes "Y方向の距離(" + y + ")"
この章最後の、底辺と高さから角度を求める逆三角関数のアークタンジェント関数紹介に移ります。
ラジアン = atan(Y値, X値)
ラジアンX軸と、原点(0, 0)と点(X, Y)を結ぶ直線との角度を弧度法ラジアンで返す。
Y値角度形成に必要なY座標値を指定する。
X値角度形成に必要なX座標値を指定する。省略時は1指定を意味し、0指定は度数法の90が返る。
ArcTANgentの略で、他言語だと、パラメータの数により別関数となっているのを良く見かけますが、 HSPでは、1つ、2ついずれの指定時もatan関数となっているようです。 いずれも、Y÷Xの角度(1つの場合はXに1指定)を返すわけですが、 1つに集約する(Y÷Xを手動計算後に設定する)場合に比べてパラメータ2つ側を利用すると、 ゼロ除算回避処理を設定しておく手間が省けます。 尚、90度以下は「tan(A) = B」の時「atan(B) = A」の相互関係があるわけですが、 tan関数とは対照的に、円周率を求めるのに用いられたり(求めることはほとんどない?)、 シューティングゲームの「自機に向かって弾を発射する」プログラム等で幅広く利用できるかと思います。 91度以上の時に反転してマイナスとなるためで、関係がなくなるという意味ではありません。 コチラで確認してみてください。
 1
 2
 3
 4
 5
 6
 7
 8
	randomize
	deg = double(rnd(90000)) / 1000 // 90度(小数点以下3桁)以下の乱数を発生
	tangent = tan(3.14 * deg / 180)
	arctangent = atan(tangent)
	mes "指定角度(度数法)=" + deg
	mes "タンジェント=" + tangent
	mes "アークタンジェント=" + arctangent
	mes "確認角度(度数法)=" + arctangent * 180 / 3.14
下記のサンプルは、ウィンドウ上にあるマウスカーソル位置の方を目で追うというものです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	// 顔情報
	fs = 60 : es = 16 : ps = 4                                    // 顔size 目size 黒目size
	fx = (ginfo_winx - fs) / 2 : fy = (ginfo_winy - fs) / 2       // 顔の中心
	lx = 10 : ly = 15                                             // 左目開始X, 左目開始Y
	rx = 35 : ry = 15                                             // 右目開始X, 右目開始Y
	lcx = fx + lx + (es - ps) / 2 : lcy = fy + ly + (es - ps) / 2 // 左目中心X, 左目中心Y
	rcx = fx + rx + (es - ps) / 2 : rcy = fy + ry + (es - ps) / 2 // 右目中心X, 右目中心Y
	// 顔作成
	buffer 1
	circle  0,  0, fs, fs, 0                                      // 顔配置
	circle lx, ly, lx + es, ly + es, 0                            // 左目配置
	circle rx, ry, rx + es, ry + es, 0                            // 右目配置
	line fs / 2, fs / 2 - 5, fs / 2, fs / 2 + 10                  // 鼻配置
	line 15, ly + es + 10, fs / 2, ly + es + 15                   // 口(左部)配置
	line fs / 2, ry + es + 15, fs - 15, ry + es + 10              // 口(右部)配置
	circle fs, 0, fs + ps, ps                                     // 黒目
	// マウス位置を追う
	gsel 0
	repeat
		wait 5
		gosub *geteyeinfo
		gosub *draw
	loop

*geteyeinfo
	mx = mousex : my = mousey
	rad.0 = atan(my - lcy, mx - lcx) // 左目の角度を決定
	rad.1 = atan(my - rcy, mx - rcx) // 右目の角度を決定
	return

*draw
	redraw 0
	color 255, 255, 255 : boxf fx, fy, fx + fs, fy + fs
	pos fx, fy : gcopy 1, , , fs, fs
	pos  lcx + cos(rad.0) * 5, lcy + sin(rad.0) * 5 : gcopy 1, fs, , ps, ps
	pos  rcx + cos(rad.1) * 5, rcy + sin(rad.1) * 5 : gcopy 1, fs, , ps, ps
	redraw 1
	return
指数や対数などの数学関数が他にもありますが、これはまた別の機会と言うことで。