AOYAMA Koji's PROGRAMMING BLOG

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

2020/05/05
プログラミングブログ / ゲームの数学 / 三角関数 / (本記事)

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

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


 独立学校法人 大学入試センターさんのページ内で、 過去問題が公開されていたので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\)(コサイン)


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

円の動きを三角関数で


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

ラジアン


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

円を動かすプログラミング


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

waiting...



 白い枠は書いたプログラムが動くようになっています。 白い枠に次のプログラムを記述して実行してみてください。
g.angle += 2*Math.PI/100;
g.ballX = 240+128*Math.cos(g.angle);
g.ballY = 128-128*Math.sin(g.angle);

 記述はキーボードで直接書いてもよいですし、コピー&貼り付けでも構いません。 実行はまず1回実行して、問題なければ10回実行100回実行とやってみてください。

角度はg.angle


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

100回で1周するg.angle


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

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

ボールの座標は(g.ballX,g.ballY)


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

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


g.ballX = 240+128*Math.cos(g.angle);

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

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

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


 今度は次のプログラムを記入、実行してみてください。
g.angle += 2*Math.PI/100;
g.ballX = Math.floor(
          (g.angle*480/(2*Math.PI))
        ) % 480;
g.ballY = 128-128*Math.sin(g.angle);

 この絵の感じの軌跡で動けば大成功です。 これはサインカーブと呼ばれるもので、うねうね動くときに使えます。

X座標は角度


g.ballX = Math.floor(
          (g.angle*480/(2*Math.PI))
        ) % 480;

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

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


g.ballY = 128-128*Math.sin(g.angle);

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

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

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


 そんなサインカーブを使えばうねうね演出が表現できます。 例えばこんなのですね。

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


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

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


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

まとめ


 本記事では三角関数と、そのゲームプログラミングへの応用を解説しました。
 三角関数に限らず数学とゲームプログラミングは親和性が高いので、 また機会があれば紹介していきたいと思います。

補足

・直角三角形を使用した説明は\(\theta\)が\(0^\circ\)より大きく\(90^\circ\)より小さい場合です。実際にはすべての角度で\(\sin\)や\(\cos\)は求まります。
・今回使用しているプログラミング言語はWeb業界で一般的に使われる「JavaScript」です。
・サインカーブの画像は実際にはSVGの機能で描いたベジェ曲線です。厳密にはサインカーブではありません。
・二次元ゲームが主流の頃のゲーム機はマシンパワーが低く三角関数や逆三角関数を求めるのは現実的には難しいため、 予め計算しておいた値を使用するなど工夫をしていました。 そのため本記事の説明は概念的なものだと捉えてください。
・\(\arctan( \frac{Py-Ey}{Px-Ex} )\) を使用するのは \(Px \neq Ex\)の場合です。 \(Px = Ex\)の場合は\(Py\)と\(Ey\)のどちらが大きいかで\(90^\circ\)か\(270^\circ\)になります。
・数式表現にMathJaxを使用しております。助かります!
・画像内のラスタライズ文字フォントにOpen Font LicenseZen Antiqueを使用しております。
・(公開後)現在の高校数学では行列は習わないとの情報をいただき改修しました。2012年度以降に消えたようですね。情報ありがとうございます。失礼しました。

カテゴリー:ゲームの数学
著者プロフィール
青山公士(あおやま こうじ)
中学2年生からゲームプログラミングに明け暮れる。ゲーム開発者としての代表作に「スーパー桃太郎電鉄II」(ハドソン)メインプログラマー、[PR]『ドラゴンクエストX オンライン』(スクウェア・エニックス)テクニカルディレクター/プロデューサーなどがある。[PR]「ドラゴンクエストXを支える技術」(技術評論社)著者。本ブログは今までの経験を活かしプログラミングが楽しいと感じる人が少しでも増えるようなものにしたい。 @kojibm
株式会社ロジック推し
推し情報を論理的にわかりやすく紹介することで「世の中をちょっと楽しく」をミッションに活動中。 HP X Instagram
privacy policy
ピックアップ
Loading...
最新記事
Loading...
関連記事
Loading...