2017年1月11日 | ブログ記事

Combinatorial Easing : 動かし方を分解する

phi16

こんにちはphi16です。traPの内部wikiに書いた記事をなんとなく公開することにしました。Easingの話です。


Easingとは、物をふわっと動かす動かし方のことです。
ぐいーんって動かしたり、もにょーんって動かしたり、ぽよぽよって動かしたり、いろいろあります。
基本的にこれは関数で指定されるのですが、種類もまぁいろいろあるし、実装方法もいろいろだし、自分で1から考えるのが難しい部分も結構あります。
というわけで、私の作ったEasing処理の書き方・作り方を書いていきます。
ソースコードはJS。

一般的な実装

Web系だと色々サイトは有るんですけどね・・・

これらに載っている方法を使うだけでは更に複雑なEasingを作ることはできません。ちゃんと理解しないと。
とりあえず色々見ると気持ちはわかるとおもいます。

EasingはEasing Functionというもので指定されています。これは何か、というと :

と、「現在の場所」を返してくれるというものです。なんかその辺のサイトは4引数取っていることが多いですね :

例えばこんなのがあります :


function easeInQuad(t, b, c, d){
t /= d;
return c*t*t + b;
};

これは等加速度で目標まで行くやつですね。段々はやくなる。

さて。

Easing Functionの本質 (引数編)

本当に必要なEasing Functionは何なんでしょうね。使うときは4引数とってもいいとおもいますけど動かし方自体にそれはいらないとおもうわけです。

例えば「d」いらないですよね?tを元々 [0,1][0,1] でとるようにしてしまえば最初からt/dを渡せば良い話です。
また、実は「b,c」もいりません。だってb=0, c=1の場合を返すEasingを作っておけば、その返り値rをつかってr*c+bと表せるからです。線形性。

というわけで本当にEasing Functionに必要な引数はtのみであることがわかります。即ち、[0,1][0,1] の実数を受け取って(だいたい) [0,1][0,1] の実数を返す関数です。
今まで通りに使いたければこんな関数をつくれば良いでしょう :


function ease(f){
return function(t, b, c, d){
return f(t/d)*c+b;
};
}
function inQuad(t){
return t*t;
}

これを使えば、easeInQuad(t,b,c,d)ease(inQuad)(t,b,c,d) と書けます。Easingの本質であるinQuad関数は非常に簡潔になりました。

ということで、「1つの関数で万能Easingにする」のではなく「関数を組み合わせてEasing」する、ということで Combinatorial Easing という名前をつけました。コンビネータ指向っていうんかなぁ?あまり聞かない。

こうすることで様々な利点があります :

Easing Functionの本質 (In/Out編)

Easing Functionには大まかに3種類あります。

例えばquad系には次の3つがあります。


function inQuad(t){
return t*t;
}
function outQuad(t){
return -t*(t-2);
}
function inOutQuad(t){
if(t<0.5)return 2*t*t;
else return -2*t*t+4*t-1;
}

こんな感じ。(http://easings.net/ja より)

一応補足すると3つどれもf(0)=0, f(1)=1を満たし、またin系はf'(0)=0out系はf'(1)=0を満たすはずです。

さてさて。

気づく人は多いとおもうんですけど、outはinを180度ひっくり返した形です。数式で表せば out(t)=1in(1t)\displaystyle \mathrm{out}(t) = 1 - \mathrm{in}(1 - t) です。さらに、inoutは最初の半分をin、残りをoutとしてくっつけた形です。数式で表せば inout(t)={in(2t)2ift12out(2t1)+12otherwise.\displaystyle \mathrm{inout}(t) = \begin{cases}\mathrm{in}(2t) \over 2 & \mathrm{if}\; t \leq {1 \over 2} \\\\\mathrm{out}(2t-1)+1 \over 2 & \mathrm{otherwise.}\end{cases} となります。

つまり、inの定義さえあれば他は導出できるということです。なら分離しちゃおうね。

次のようにin用のEasing Functionを作成します。


function quad(t){
return t*t;
}

これからin,out,inoutを導出する関数を作ります。


function In(f){
return f;
}
function Out(f){
return function(t){
return 1 - f(1-t);
};
}
function InOut(f){
return function(t){
if(t<0.5)return f(2*t)/2;
else 1 - f(2-2*x)/2;
};
}

あとは使うだけです。
ease(inQuad)(t,b,c,d)ease(In(quad))(t,b,c,d)と書けるようになりました。いいかんじ。

Easing集

まぁ欲しいのはこれですよね・・・でも今までの結果により、easeInをひたすら書けば十分ということになったので。ちょっと楽。
ばーっと書いちゃいますね。ちなみに大体はPoyoEngine (進捗どうですか?を作ったときの自作ゲームエンジン) のコードです。


e.linear = (x)=>x;
e.smooth = (x)=>x*x*(3-x)/2;
e.quad = (x)=>x*x;
e.cubic = (x)=>x*x*x;
e.quart = (x)=>x*x*x*x;
e.quint = (x)=>x*x*x*x*x;
e.sine = (x)=>1-Math.cos(x*Math.PI/2);
e.circ = (x)=>1-Math.sqrt(Math.max(0,1-x*x));
e.exp = (x)=>Math.pow(2,-(1-x)*10);
e.back = (x)=>x*x*(2.70158*x-1.70158);
e.softBack = (x)=>x*x*(2*x-1);
e.elastic = (x)=>56*x*x*x*x*x-105*x*x*x*x+60*x*x*x-10*x*x;
e.bounce = (x)=>{
var pow2, bounce = 4;
while ( x < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - x, 2 );
};

これらはとりあえず私が使うから書いているもので、新しいEasingを追加するのは簡単だとおもいます。
まぁ、これを元に「使いやすいインターフェース」をつくるのはみなさん次第ってことで。

さらなる合成

まだこれは実験段階なのですけど。Easingをさらに抽象化するとおもしろいかなとおもって。

例えば


forM [0..9] $ \i -> ease 0 100 $ sequent i 10 0.3 . easeInOut . cubic

とかやると

まぁ、こういう特殊なEasingもすぐつくれるかも、みたいな。これが元々のCombinatorial Easingなんですけど、ちゃんと紹介するのはまた今度で。

まとめ

easeInQuadease/inQuad に、そして ease/in/quad にするという話でした。
Generics, templateのような、元々は「データ構造×型」だったものを「データ構造+型」にするような感覚と近いですね。
既存のEasingはあまりにも冗長すぎるとおもうわけです。

まぁまぁ、Easingを導入するとまぁいろいろ幅がひろがるのでよいです。
でもやみくもに導入するのではなく綺麗な?コードにできると良いなとおもいます。

ジャンプみたいな加速度的動きはquad系で出来たりするし、簡単なアニメーションを全てEasingで済ますのもありかもとかおもってたり。


というわけで部内wikiのコピペでした。他にも技術系の記事をいろんな人が(自主的に)書いてたりします。知見を集めていこうな。
ちょうど今Easingをいっぱいつかったゲームをつくっています。完成したら技術報告もかこうかなとおもっているのでおたのしみに。

この記事を書いた人
phi16

Haskellとかレンダリングとか数学とか。

この記事をシェア

このエントリーをはてなブックマークに追加

関連する記事

2018年12月1日
🏁🍇😀🔤Emojicodeはエモい🔤❗🍉
Double_oxygeN
2018年11月3日
ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】
Azon
2018年11月1日
Crystal lang
MENTOS
2018年4月1日
そばやのウキ☆ウキ物理ベースレンダリング〜パストレ編〜
sobaya007
2017年12月25日
ネタがないので迷路を自動生成してみた
tsukatomo
2017年12月14日
Project Eulerのすすめ
Ark

活動の紹介

カテゴリ

タグ