AOYAMA KOJI's PROGRAMMING BLOG

三角関数で円の動きを表現する初中級者向けプログラミング解説

2020/05/05
三角関数で円の動きを表現する

 休校が続き学校再開の目処が立たないことで9月新学期が検討されているようですね。 多くの国では新学期が9月らしいので、留学や海外進学など世界を見据えて9月新学期に私は賛成です。 ただ変更前後の混乱はとんでもないことになるでしょうし、受験生の皆さんの負担も相当でしょうから、これはさすがに無責任な発言と自覚しています。 それでもいつか実現すると良いと思っていますよ!
 という流れでふと、 ゲームプログラミングでも結構使う三角関数が、大学受験数学の問題として出されているかが気になりました。 結論からいうと、重要な位置を占めそうです。 そこで、本記事では、プログラミングによる体験を含めて、三角関数をできるだけ楽しく解説します。



[PR]

センター試験でほぼ必須の三角関数


 独立学校法人 大学入試センターさんのページ内で、 過去問題が公開されていたので3年分調べてみたところ、 すべての年度の数学Ⅱ第1問に三角関数を含む式が登場していました。

・平成31年度: \( f(\theta) = 3\sin^2\theta + 4\sin\theta \cos\theta - \cos^2\theta \)
・平成30年度: \( 2\sin(\theta + \frac{\pi}{5}) - 2\cos(\theta + \frac{\pi}{30}) = 1 \)
・平成29年度: \( \cos2\alpha + \cos2\beta = \frac{4}{15} \)

 これらはその一部ですが、数学Ⅱは事実上必須と言って良いと思いますので、三角関数は大学受験としても重要な位置めそうですね。 ゲーム開発者の視点ではいと思います!

\(\sin\)(サイン)と\(\cos\)(コサイン)


三角関数で円の動きを表現する fig.2  説明の順番が前後している気もしますが、\(\sin\)\(\cos\)三角関数と呼ばれるものです。
 図のように配置された、斜めの部分が長さ1の直角三角形で、 左下の角度が \(\theta\)(シータ)のとき、 縦の長さが\(\sin\theta\)、横の長さが\(\cos\theta\)です。 斜めの長さが1じゃない場合は、斜めの長さ分の縦(横)の長さってことになります。
 どっちが\(\sin\)だっけ?という場合は、 英語のcを描くときに斜めから横に、英語のsを筆記体で描くときは斜めから縦に描くので、 文字の書き方をイメージして横が\(\cos\)縦が\(\sin\)と覚えると良いと教わりました。 父から。

円の動きを三角関数で


 三角関数を用いて円の動きを表現することができます。半径\(r\)の円は、\(x=r\cos\theta\)、\(y=r\sin\theta\)として、\(\theta\)を一周回してあげれば円の動きをします。
三角関数で円の動きを表現する fig.3 三角関数で円の動きを表現する

ラジアン


 問題文では角度の場所に\(\pi\)が出てきますが、これは円周率の\(\pi\)です。実際の値は\(3.14159…\)ってやつですね。 \(2\pi\)一周になります。 つまり例えば\(\frac{\pi}{5}\)は、私達が普段使う一周\(360^\circ\)の形式で言えば\(36^\circ\)になります。 この一周\(2\pi\)で表す形をラジアンと呼びます。 radian あるいは rad と書きます。 ラジアンを使う方が数学的に都合が良いそうです。

円を描くプログラミング


 そしてプログラムもこのラジアンを使います。やってみましょう。

 以下は、記載のプログラムを一定間隔で繰り返し実行する仕組みになっています。 「10回実行」や「100回実行」を押下してみてください。 円を描くようにボールが動けば成功です!
waiting...
g.fAngle += 2*Math.PI/100;
g.iBallX = 144 + 74*Math.cos(g.fAngle);
g.iBallY =  74 - 74*Math.sin(g.fAngle);

角度はg.fAngle


 角度は g.fAngle という変数に入っています。 変数というのは値を入れておく入れ物です。 変数 g.fAngle は初期値として 0 が入っています。

100回で1周するg.fAngle


g.fAngle += 2*Math.PI/100;

 この行は、\( \frac{2\pi}{100} \) を 変数 g.fAngle に加えるプログラムです。 Math.PI は \(\pi\) です。 プログラムで除算は「/」(スラッシュ)を使用します。 「+=」(プラスイコール)は右辺の値を左辺の変数に加算する機能です。
 1周である \( 2\pi \) を100で割ったものを加えていますので、これを100回実行すれば一周します。

ボールの座標は(g.iBallX,g.iBallY)


 g.iBallX と g.iBallY も変数で、(g.iBallX,g.iBallY) がボールの座標を表すようにこの記事のプログラムはできています。 ただし数学の座標と異なり、左上が(0,0)で、右と下に向かって数値が大きくなります。 特にY座標、すなわち上下反転していますので、若干注意です。

g.fAngleを角度としてcosを求めてX座標に


g.iBallX = 144 + 74*Math.cos(g.fAngle);

 この行は、\( 144 + 74 \cos(g.fAngle) \) の計算結果を 変数 g.iBallX に代入するプログラムになります。 プログラムで乗算は「*」(アスタリスク)を使用します。 同様に次の行
g.iBallY = 74 - 74*Math.sin(g.fAngle);

 は、\( 74 - 74 \sin(g.fAngle) \) の計算結果を 変数 g.iBallY に代入するプログラムです。 マイナスにしているのはY座標系が上下反転しているからです。 これで (g.iBallX,g.iBallY) が、中心(144,74)、半径74の円を、反時計回りに描くプログラムになります。
[PR]

サインカーブを描いてみよう


 今度は、次のプログラムを実行してみてください。 記載のプログラムを一定間隔で繰り返し実行する仕組みになっています。
waiting...
g.fAngle += 2*Math.PI/100;
g.iBallX = Math.floor( (g.fAngle*288/(2*Math.PI)) ) % 288;
g.iBallY = 74 - 74*Math.sin(g.fAngle);

三角関数で円の動きを表現する fig.4  この絵の感じの軌跡で動けば大成功です。 これはサインカーブと呼ばれるもので、うねうね動くときに使えます。
 以下、このプログラムを解説します。

X座標は角度


g.iBallX = Math.floor( (g.fAngle*288/(2*Math.PI)) ) % 288;

 この部分は、角度をボールの横の位置、X座標を設定するためのものです。
 この中の「g.fAngle * 288 / (2 * Math.PI)」の部分を数式にすると \( g.fAngle \times \frac{288}{2 \pi} \) です。 \(2 \pi\)で割り、\( 288 \)を掛けることで、「\( 0 から 2 \pi \)」の値を「\( 0 から 288 \)」に変換できます。 ボールのX座標は0が左端、288が右端です。
 その結果を「Math.floor(…)」によって整数に変換します。Math.floorは少数点以下切り捨ての処理です。
 次の「%」(パーセント)は剰余を求める演算子です。 「○ % 288」の形で○を288で割った余りが求まります。 こうすることで、288すなわち右端を超えたら、また左端から続く形にできます。
 右辺全体を強引に数式で書くと \( [g.fAngle \times \frac{288}{2 \pi} ] \ (MOD \ 288) \) になります。

Y座標は\(\sin\)の値


g.iBallY = 74 - 74*Math.sin(g.fAngle);

 この行は \( 74 - 74 \sin(g.fAngle) \) を計算して、変数 g.iBallY に代入します。 sinの値は-1から1なので、それに74を掛けることで-74から74になります。 そこに74を加えると0から148になります。これは上端から下端の数値です。
 なおこの行を
g.iBallY = 74 - 74*Math.cos(g.fAngle);

と変えれば \(\cos\) の値による軌跡が描けます。

サインカーブでうねうね演出



 そんなサインカーブを使えばうねうね演出が表現できます。 例えばこの記事で動画ファイルを作成できる横うねうね演出も、サインカーブを使用しています。

三次元ゲームでもよく使う


 三次元ゲームだと、カメラすなわち視点を回転させたり、キャラクターが回転したり、 キャラクターの関節を曲げる部分でも回転の動きをしますが、それらの回転に関して三角関数を使用して処理します。
 回転の変換は、2011年度以前の高校数学で一次変換行列 \( \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix} \) を習いました。
 これは二次元座標系での変換処理ですが、三次元空間の回転もこれを応用してできますので、三角関数はやはり重要です。 しかし2012年度以降に高校数学から行列がなくなってしまったようです。 個人的には残念です。

\(\tan\)(タンジェント)はあまり使わないが…


 三角関数にはもうひとつ\(\tan\)(タンジェント)もあるのですが、こちらはあまり使わない…と思います。

\(\arctan\)(アークタンジェント)はよく使う


 一方、その逆変換である\(\arctan\)(アークタンジェント)は、ゲームでもよく使用します。 こちらは逆三角関数と呼ばれます。
 例えば二次元座標系で、敵キャラクター(\(E\))と自キャラクター(\(P\))のX座標の差分分のY座標の差分の\(\arctan\)、数式で \(\arctan( \frac{Py-Ey}{Px-Ex} )\) を求めると、敵キャラクターからの自キャラクターへの角度が求まります。 その角度に向かって弾を打ったり、移動したりすれば、自キャラクターを襲う敵キャラクターを演出することができます。
 角度を求めずに、座標の差分を定数で割った値で移動する形でも向かっては来るのですが、 斜め移動が縦横の移動より高速になってしまうのですよね。 まあ昔はそういうゲームも少なくなかったですが。

円周率を求める場合にも\(\arctan\)


 他にもこの記事で紹介した、円周率を求める場合にも、アークタンジェントを使用します。 詳細はリンク先をご確認ください。
[PR]

まとめ


 本記事では、三角関数、およびそのゲームプログラミングへの応用を解説しました。 円を描くプログラムと、サインカーブを描くプログラムを体験できるようにしています。
 三角関数に限らず数学とゲームプログラミングは親和性が高いので、 少しでも、興味をもっていただけたら幸いです。
 当ブログでも、また機会があれば紹介していきます。

補足

  • 直角三角形を使用した説明は\(\theta\)が\(0^\circ\)より大きく\(90^\circ\)より小さい場合です。実際にはすべての角度で\(\sin\)や\(\cos\)は求まります。
  • 今回使用しているプログラミング言語はWeb業界で一般的に使われる「JavaScript」です。
  • 二次元ゲームが主流の頃の家庭用ゲーム機はマシンパワーが低く、三角関数や逆三角関数を求めるのは現実的には難しいため、予め計算しておいた値を使用するなど工夫をしていました。そのため本記事の説明は概念的なものだと捉えてください。
  • \(\arctan( \frac{Py-Ey}{Px-Ex} )\) を使用するのは \(Px \neq Ex\)の場合です。 \(Px = Ex\)の場合は\(Py\)と\(Ey\)のどちらが大きいかで\(90^\circ\)か\(270^\circ\)になります。
  • 数式表現にMathJaxを使用しております。助かります!
  • 画像内のラスタライズ文字フォントにOpen Font LicenseZen Antiqueを使用しております。
  • (本記事公開後)現在の高校数学では行列は習わないとの情報をいただき改修しました。2012年度以降に消えたようですね。情報ありがとうございます。
  • (本記事公開後)初期に提示していたプログラム記述ギミックを2025年5月にセキュリティー上の都合で閉じました。
  • (本記事公開後)本記事公開後に公開された記事へのリンクを追加しております。
カテゴリー:プログラミング解説,絵を動かす
[PR]