見出し画像

数学の力で、新しいイージング関数を作ってみた

こんにちは、ChocolaMintです。今回はイージング関数と数学の話です。

こういうの話だよ~

はじめに

「イージング関数」とは、何かの「変化率」を指定する関数のことです。ゲームでよく使われてる関数の一種なんです。詳しい紹介はこちらのサイトを参考してください。

何で新しいイージング関数を作るの?

少なくともゲーム業界では、大体「easeInQuad」など有名なイージング関数しか使いません。確かに多くの場合はチートシートからいい感じの関数を選べばいいんですが、特定の関数しか選べない時点で微調整は難しくなるのも当然ですね。

例えば、easeInQuadより遅い、easeInCubicより速い関数を選びたいとき、どうすればいいでしょうか。10個のミサイルを順番通りに加速度を段々上げたい(easeOut)とき、そもそもチートシートから選べるイージング関数の種類は7つしかないから、やっぱり無理でしょうか。

でも、実はパラメーターを増やせば、イージング関数だって微調整できるように作れるんです。興味のある方はぜひ続きを見てください。

セットアップ

今回はdesmosという無料アプリを使って、いろんなイージング関数を可視化させながら調整します。

イージング関数を作る前に、まずはイージング関数をちゃんと定義しないといけませんよね。ここでは以下の定義を使います:

  • パラメーター:

    • x:実数、範囲は0~1

    • p:実数、調整用のパラメーター、範囲はあとで別々に定義する

  • 戻り値:実数、範囲は0~1

では、始めましょうか。

easeInを作ってみよう

easeInの形は大体こんな感じでしょう:

easeInQuadeaseInCubiceaseInQuart

「指数」をパラメーターに入れ替われば、もっと細かい調整ができるようになります。(p>1)

指数関数からできているeaseIn

もう一つの作り方は、「二つのイージング関数の補間」です。シンプルな線形補間で十分です。(0≦p≦1)

x^2とx^10の線形補間からできているeaseIn

形さえ守れば、ちょっとアレンジしてもいいです。例えばさっきの関数の指数部分をこう変えても全然ありなんです:

ただの線形補間よりもっといい形になった

easeOutを作ってみよう

さっき使った指数関数は実はeaseOutに近い形にもなれるんです。(0<p≦1)

指数関数からできているeaseOut

でも、この関数は実はまだイージング関数として使えないんです。なぜかと言うと、こちらに注目してください:

この関数は、xが1の時、実は「速度が0になっていない」からです!

ここの「速度」(又は「変化率」)は、この関数の1回導関数のことです。

x=1の時、この導関数の値はp^(p-1)、つまり0じゃない

さっき作ったeaseIn関数たちは、実は皆「x=0の時、1回導関数の値が0になる」という条件を満たしているんです。

じゃあ、どうすればいいでしょうか。

何かしらの方法で「x=0」の部分を「x=1」の部分に入れ替えられますかね…

xを1-xに変えると、元の関数はx=0.5を中心に、左右逆転して、新しい関数になった。でもx=0の時の値は1で、x=1の時の値は0だから、今のままじゃまだ使えない
マイナスを入れたら、y=0を中心に上下逆転することができる。でもこの関数の値の範囲は-1~0。まだ使えない
1を足せて、範囲を0~1に移動することができる。でもこの形はどっちかというと、easeInのほうに近いかな…どうしよう…
p>1を指定すれば完成!

もちろん、easeInと同じく、ここでも線形補間からeaseOutが作れるんです:

数式長いな…

easeIn/Outを作ってみよう

さっきの関数、惜しかったですね。調整する前はx=1の時の速度が0になれなかったけど、調整した後は逆にx=0の時の速度が0になれなくなりました。一つの関数で、パラメーターをいじってるだけでeaseInとeaseOutのどっちかにすぐに変えることができる、そんな関数は本当に存在するんですか?

例えば…こういう関数はどうでしょう?

0<p<1の時はeaseIn、p>1の時はeaseOut

形は一見あってるけど、本当に速度が0になってるかどうかは、数学で分析しないとわかりません。1回微分したら、このような導関数が導けます:

xを0に代入すると、こんな感じになります:

p>1の時は解なし、0<p<1の時は値が常に0×1=0

xを1に代入すると、こんな感じになります:

0<p<1の時は解なし、p>1の時は値が常に1×0=0

これで言い切れるんですね!このちょっと見た目が変な関数は、pの範囲により、easeInとeaseOut両方の属性を持つことができるんです。

easeInOutを作ってみよう

easeInOutも作ってみましょう!easeInOutって、「easeInからのeaseOut」のイメージがあります。こんな感じです:

easeInOutSine

今まで指数関数しか使っていないので、気分転換にコサインも入れてみましょう:

p<1の時はeaseInよりのeaseInOut、p>1の時はeaseOutよりのeaseInOut
p=1の時はただのeaseInOutSine
導関数はこんな感じ。p≧1の時はx=0とx=1の時の値が0になるけど、0<p<1の時はx=1の時だけ値が0になる

easeOutInを作ってみよう

もう一つのeaseInOutまたはeaseOutInの作り方は、二つのイージング関数を繋ぐことです。

例えばeaseInOutQuadは、二つの二次関数からできている関数です:

前半の関数
後半の関数
ふたりはプリキュアeaseInOutQuad!

注意しなければならないところは、繋いで生み出した関数も連続微分可能な関数じゃないとイージング関数として使えません。速度が途中に急に変わったらスムーズじゃなくなるからです。

さっき言ったように、easeOutの特性の一つは、x=1の時の1回導関数が0になります。そして、easeInのほうは逆にx=0の時の1回導関数が0になります。つまりこの二つのポイントから繋ぐと、連続で微分可能な関数になります。

さっき作ったeaseOutのxを2倍にして、関数の末端をx=0.5に移動させる
そして値を半分にして、関数の末端をy=0.5に移動させる
最初作ったeaseInを2倍にして、それから1を引いて、関数の先端をx=0.5に移動させる
それで値を半分にしてから0.5を足して、関数の先端をy=0.5に移動させる
出来上がり!(p>1)

easeInOut/OutInを作ってみよう

さっきはeaseOutからのeaseOutだったけど、順番が変わったらどうなるでしょうか。試してみましょう!

数式はこんな感じ
おお、さっきよりきれいになった気がする!
0<p<1の時はeaseOutIn、p>1の時はeaseInOut

…が、ここでx=0.5の時の可微分性を証明するとちょっとややこしくなるので、細かい証明はここで割愛します(確かこれは大学の微分積分学の範囲なので、興味のある方はそちらの教科書を参考してください)。

最後に

いかがでしょうか。意外と知ってる数式を組み合わせてるだけで色んなイージング関数が作れるんですね。数学の知識もちゃんと応用できて、素晴らしいことだと思います。

今回は指数とコサインしか使わなかったけど、今まで学校で学んだ関数は全部イージング関数に使えるんです。例えばこういう化け物イージング関数も全然ありなんです:

p>0.6の時はeaseIn

ただ、ゲームに入れる時は、パフォーマンスの配慮もしなければなりません。イージング関数を使ってるオブジェクトが多くなったら(例えば弾幕シューティングに使ってる時)、なるべく軽い関数を選んだほうがいいと思います。ベンチマーキングしてもいいと思います。

あと、イージング関数の種類はeaseIn、easeOut、easeInOut、easeOutIn以外にもあります(easeInBackとか)。いつかそういう特殊なイージング関数の微調整できるバージョンも作ってみたいですね。

たまに暇つぶしにイージング関数を作ってみませんか?ご覧いただきありがとうございました。

あとがき

今回の数学の証明はそこまで厳密ではありません。特に関数の値域と連続性そのあたりの説明は省略しました。良い子のみんな、大学の授業ではああいう雑な証明を書いてはいけませんよ!

この記事が気に入ったらサポートをしてみませんか?