RPGツクールと数学のブログ

RPGツクールと数学についてのブログです。

ゲーム作りに使える高校数学(前編)

高校数学はゲーム作りに非常に有用な数学だと思っています。

中学数学は使うにはまだ足りないものが多く、大学数学は難しすぎて使いどころが少ないので、高校ぐらいが丁度いいのです。もちろん数学を知っても知らなくてもゲーム作り自体は出来ますけれど、高校数学を知っていると出来ることが多くなって、作れる幅が広がるのは確かです。

今回は高校数学がゲーム作りに使える場面を紹介していきます。ぜひ勉強している方のモチベーションになってもらいたく思います。

長くなっちゃったので、前後編で分けてます。

高校数学の主な分野

本稿で取り上げる内容を太字、あまり使わない(or 使ったことがない)ことを理由に本稿では取り上げない内容を灰色文字+打ち消し線にしています。

私はRPG屋さんなので、割とRPGに肩入れした内容になってます^^;

n次方程式・n次関数
三角関数
指数関数
対数関数
微分
積分
集合
論理・証明
整数の性質
数列
・平面図形・ベクトル
式と曲線
複素平面
行列
・確率

確率分布
統計

n次方程式・n次関数

2次関数は、何にでも使えて特に便利です。ジャンプの軌道なんて代表的ですね。

f:id:fermiumbay13:20180105234149p:plain

頂点(p,q)を通る放物線は次の式で表すことができます。

\displaystyle y=a(x-p)^{2}+q

x=0のときy=0になるような放物線を考える、とすれば、定数aは次のようにして求められますね。

\displaystyle 0=a(0-p)^{2}+q
\displaystyle 0=ap^{2}+q
\displaystyle a=-\frac{q}{p^{2}}

こうすれば、頂点座標だけを使って放物線の関数が作れます。例えば、30フレームの時間を掛けて50ピクセルの高さまでジャンプする、ということを想定すると、xフレーム後のジャンプ高さyピクセルは、次式で求まることになります。

\displaystyle y=-\frac{50}{30^{2}}(x-30)^{2}+50

これをキャラクターの座標を決める式に入れてやればジャンプするようになります。

おおむねこのような動作関係で用いると便利ですが、他にもRPGのキャラクター成長曲線を考えるとき、n次関数は割と便利です。後述の微分と併用すると、いっそう自由度を持ったキャラクター成長パターンを作ることが可能です。

三角関数

シューティングゲームを作るなら、三角関数を欠かすことは出来ないでしょう。サインと、コサインと、アークタンジェントさえあれば、とりあえず困らないと思います。

f:id:fermiumbay13:20180106003038j:plain

三角関数
\displaystyle \sin \theta = \frac{a}{c},\quad \cos \theta = \frac{b}{c},\quad \tan \theta = \frac{a}{b}

三角関数
\displaystyle \theta = \sin^{-1} \frac{a}{c},\quad \theta = \cos^{-1} \frac{b}{c},\quad \theta = \tan^{-1} \frac{a}{b}

 角度\thetaに対する直角三角形の辺の比は一定で、計算で求めることができます。これを利用すれば、x座標とy座標の2次元座標と、角度\thetaとを相互変換することができます。角度から辺の比を求めるのが高校数学で習う三角関数ですが、逆に辺の比から角度を求める逆三角関数というのもあります。

シューティングゲームでは主に\sin\cosを使って角度に対する各座標の進行距離を計算し、逆に座標のズレから\tan^{-1}(アークタンジェント)を使って放射角度を求めることをします。

f:id:fermiumbay13:20180106021242p:plain

弾の速さv、角度\thetaで弾を撃つとき、横方向距離x、縦方向距離yは次式で求められます。

x=v\cos\theta
y=v\sin\theta

これらは三角関数の定義から一発ですね。逆に、横x、縦yだけ離れているキャラクターに向けて弾を放つ、という場合の角度\thetaは次式で求められます。

\displaystyle \theta=\tan^{-1}\frac{y}{x}

いわゆる自機狙い弾は、上式のようにアークタンジェントを使えば実装できます。

実際には象限を考えないとズレてしまうことがあるので、図示して検討してみるのがよいでしょう。

指数関数

あんまり使わないと思います^^; 動作関係や、複雑な計算の過程で使うのがほとんどであり、三角関数ほどはっきりした使い道は多分ないと思います。例えば三角関数と合わせた減衰振動は割と有用な動作です。

f:id:fermiumbay13:20180106022024p:plain

\displaystyle y=ae^{-bx} \sin(cx+d)

 定数a,b,c,dによって決まる振動です。三角関数のように振動しますが、指数関数が掛け算されている影響で、振幅が徐々に小さくなるというものです。グラフを見ると物々しいかもしれませんが、実際に動作させてみると割と可愛いです。

f:id:fermiumbay13:20180106024650g:plain

また、指数関数y=e^{-x}xを無限大にすると0に収束するので、値が大きくなると一定値に近づくような関数を作る際に指数関数は便利です。

例えば、「ためる」のコマンドを使えば使うほど攻撃力が上がるものの、ある一定値以上は上がらない、でも無限に上昇していく、というような変化をさせたい場合は、次のような関数にすると楽です。

y=a+(b-a)(1-e^{-x})

x=0のときはy=aになりますが、xが大きくなっていくと、だんだんy=bに近づいていきます。a=1,b=2とするとこうなります。

y=2-e^{-x}

これを上述の「ためる」コマンドを使った回数x、攻撃力の倍率yに当てはめてみると、未使用(x=0)のとき攻撃力はy=1より1倍、1回使うと1.63倍、2回使うと1.86倍、3回使うと1.95倍となります。だんだん2倍に近づいていきますね。

対数関数

ドラクエふっかつのじゅもんを実装する場合、対数関数が使えます。

ふっかつのじゅもんとは、かつてゲーム本体にセーブ機能がついていなかった時代に、ゲームのデータを文字列に変換して記憶するシステムのことでした。ゲームを中断する際に表示される文字列をメモしておいて、再度ゲームをする際にその文字列を入力してやれば、ゲームの状態を復帰できるというものです。

例えば次のパラメータを文字列に化かしてみましょう。

・主人公のレベル(1~99の、99通り)
・仲間Aのレベル(1~99の、99通り)
・仲間Bのレベル(1~99の、99通り)

これをアルファベットA~Zの26文字を使って表現しようと考えます。何文字あれば表現できるでしょうか。これを計算するのがまさに対数関数ですね。

パラメータの組み合わせは全部で99^{3}通りであり、アルファベット26通りをx文字並べてできる組み合わせは26^{x}通りなので、26^{x} \ge 99^{3}を満たす最小のxを求めればよい。

両辺底が26の対数をとると

x\ge 3\log_{26}99\fallingdotseq4.23

となるので、x=5が最小値となる。

 以上より、5文字あれば表現できることになります。

ふっかつのじゅもんは、進数変換で実装することができます。パラメータはすべて99通りのものから成っていて、アルファベットは26通りのものの羅列なので、99進数を26進数に変換すればOKです。

・主人公のレベルx
・仲間Aのレベルy
・仲間Bのレベルz

 とすると、これらは次式で一つの値として扱うことができます。

99^{2} x+99y+z 

 99進数です。これで一つの大きな値として各レベルを表現出来るようになったので、これを26進数に変換して、各桁の0~25をA~Zに置き換えれば完成です。(進数変換の方法は割愛です)

例えば主人公がレベル5、仲間Aがレベル4、仲間Bがレベル3であれば、

99^{2}\times 5+99\times 4+3=49404 

になり、これを5桁の26進数にすると(各桁を:で区切っています)

 0:2:21:2:4  

となって、0~25がA~Zとなるようにアルファベットを当てはめるとACVCEになります。これがふっかつのじゅもんです。

実際には上記のようにすべてのパラメータが99通りということはありえないでしょうから、各パラメータの上限値ごとに異なる位取りをする進数となりますが、考え方は同じです。

微分

関数の吟味に使います。例えば2次関数を使って、次のようなキャラクター成長曲線を作ったとします。レベルxのとき、攻撃力がyになるものです。

y=x^{2}-91x+100

レベル1のときに10、レベル99のときに900、ぐらいになるようにして作ったもので、確かにそれぞれの値を入れてみると大体そうなるのですが、グラフをかいてみるとこんなことになっています。

f:id:fermiumbay13:20180106051935p:plain

確かにレベル1と99のときは目的の値に近いのですが、間がわけのわからないことになっていますね。レベルの上昇とともに攻撃力が減少し、マイナスの値になってしまっています。

攻撃力なのですから、レベルの上昇とともに常に増加するような、単調増加関数でなければなりません。関数が単調増加かどうかを調べるのに、微分が有用です。

y=ax^{2}+bx+c
微分すると
y'=2ax+b

まず、2次関数は上記のようにして微分できます。そして、微分して出たy'常に正であれば、もとの関数yが単調増加である、と言えます。つまり、2次関数が単調増加である条件は次のようにして求められます。

y'=2ax+b\gt 0
\displaystyle x\gt -\frac{b}{2a}(a\gt 0)

さっきの場合はa=1,b=-91だったので、\displaystyle x\gt \frac{91}{2}=45.5より、レベル46以上にならないと増加が始まらないという状態になっていました。これはダメですね。bがマイナスだったからいけないのです。a,bがともに正であれば常にこの範囲を満たすので、そうなるようにa,b,cを考え直します。

y=0.08x^{2}+x+9

これなら条件の範囲を満たしますね。グラフを実際に描いてみると、こうなります。

f:id:fermiumbay13:20180106053412p:plain

こんな感じで、単調増加のグラフになっていることを確認できます。

このように、関数を考える際に通る点のことだけではなくて、どういう変化をする関数なのかを含めて決める必要がある場合は、微分を併用するのが良いでしょう。実際にはこれぐらいであれば、グラフを描きながら係数を色々試してみるという方法でも十分な気はしますが…

整数の性質

メニュー画面を作ろうと思って、同じ大きさのボタンを決められた領域に並べなければならない場面があるとします。下図は、高さ320ピクセルの領域の中にボタンを5つ配置する例です。

f:id:fermiumbay13:20180106061231p:plain

てきとうに配置するとどこかずれてしまうので、きれいに配置したくなるわけです。このスキマの高さxとボタンの高さyは次のような関係にあります。

6x+5y=320

x,yは整数なので、これは不定方程式を解く問題です。実際に解いてみると、整数nを用いてそれぞれ次のようになります。

\begin{cases}x=5n\\y=64-6n\end{cases}

あとは好きな組み合わせを選べばOKです。ここではスキマが最小になる場合ということで、n=1のときのx=5,y=58を選択します。ボタンの高さを58ピクセルにすれば、スキマを5ピクセルずつ開けることできれいに配置することができます。

数列

数列は、関数の離散バージョン(とびとびの値をとるもの)なので、離散的に変化するものを対象とした関数は、数列の考え方が有用です。

例えば、RPGの「次のレベルまでに必要な経験値」から、「そのレベルに至るまでの総経験値」を求めるときなどに階差数列の一般項を求める計算が使えます。

レベルがnのとき、次のレベルになるのに必要な経験値b_{n}が次式で表されているとします。

b_{n}=2n^{2}+7n+11

レベル1のときは経験値20ためればレベル2になり、そこからさらに経験値33ためればレベル3になる、という具合です。レベル1からレベル3になるまでに必要な経験値は20+33=53ということになりますね。レベル1からレベルnになるまでに必要な総経験値a_{n}は、階差数列の一般項として求められます。

階差数列の一般項a_{n}
\displaystyle a_{n}=a_{1}+\sum_{k=1}^{n-1}b_{k}
として求められるので、a_{1}=0,b_{k}=2k^{2}+7k+11を代入すると、
\displaystyle a_{n}=\sum_{k=1}^{n-1}(2k^{2}+7k+11)
\displaystyle =2\sum_{k=1}^{n-1}k^{2}+7\sum_{k=1}^{n-1}k+11(n-1)
\displaystyle =2\times\frac{1}{6}n(n-1)(2n-1)+7\times\frac{1}{2}n(n-1)+11(n-1)
\displaystyle =\frac{1}{6}(4n^{3}-6n^{2}+2n)+\frac{1}{6}(21n^{2}-21n)+\frac{1}{6}(66n-66)
\displaystyle =\frac{1}{6}(4n^{3}+15n^{2}+47n-66)

以上より、\displaystyle a_{n}=\frac{1}{6}(4n^{3}+15n^{2}+47n-66)となります。n=3を代入すればレベル3になるまでに必要な総経験値となりますが、ちゃんと53が出てきますね。

 

後編に続きます