座標の移動とイージング

主観的な記事が続いたのでちゃんとしたプログラミングの話でも。非初心者向け。
文字や画像を左から右に移動させるとき、一番簡単なのは等速直線運動なのだが、なんとも味気ない。

dx = 3;
x += dx;

そこで使うのがイージング。イージングとは速度に重みを持たせることで、だんだんと加速したり、目的地に近づくにつれて減速させたりといった効果を出す必殺技だ。dxの値をちょっとだけ弄ってあげるだけで、見た目は何倍も良い感じになる。

距離から速度を算出

まずは簡単なイージング。目的地に近づくにつれて減速する、イーズ・アウトの一つである。

dx = (dst - x)/10.0;
x += dx;

dxとxはdoubleで持つのを忘れないように。dstは目的地の座標で、この座標に近づくにつれてdxは減少していく。10.0、の値を小さくすると素早く、大きくするとゆっくり移動する。
これを応用すると、例えばメニュー画面で選択した項目に緩やかにカーソルを合わせる、といった演出が簡単に実装できる。

y += (Menu*Menu_Distance - y)/6.0;

驚くほど簡単である。Menuは選択しているメニューの番号、Menu_Distanceはメニューとメニューの間隔だ。
ただし、この方法はとても単純で簡単だが幾つかの欠点がある。一つは、移動までに要する時間が読みにくいこと。大体10.0で割ったときの100px移動する時間が10F程度だったような気がする。もう一つは、2Dの場合画像の位置はint型で指定するため、速度が極小になったときに1.00000→0.99999という座標移動で移動がカクッとなってしまうことである。それを防ぐために速度が一定以下の時に0にする、という処理を入れると、今度は位置が微妙にずれてしまう。

イージング関数を使う

そこで登場するのがイージング関数である。これらは上の簡易イージングよりずっと重いし複雑だが、どうしてもかくつきが取れなかったり、タイミングが重要な部分にはこちらを使ったほうが良い。
(ちなみに数式はココ(PDF)*1から取ってきている。BSDライセンス

class EASE {
public:
  static double InQuad(double change,double base,double duration,double time) {
    time/=duration;
    return change * time * time + base;
  };
  static double OutQuad(double change,double base,double duration,double time) {
    time/=duration;
    return -change * time *(time-2) + base;
  };
  static double InOutQuad(double change,double base,double duration,double time) {
    time/=duration/2;
    if (time < 1) return change/2*time*time + base;
    return -change/2 * ((--time)*(time-2) - 1) + base;
  };
};

使い方はやや煩雑だ。今度は経過時間によってその時の速度を算出するため、それをパラメータとして持たせる必要がある。changeは移動量。baseは最初の位置。durationは移動時間で、timeが現在の経過時間。このように使う。

// x = 300から400まで20フレームで移動(イーズアウト)
if (Cnt<20) Cnt++;
x = EASE::OutQuad(100, 300, 20, Cnt);

Cntが増えた代わりにdxが消えたのでトントンか。イーズのかかり具合は別途式を用意してあげる必要があるので参照元から自分で作るべし。

まとめ

適当にイーズさせたい時や、移動先が臨機応変に変わったりする時(baseが不定の時)は、距離から速度方式を使うと良い。逆に移動位置がきちんと決まっていてちゃんとイーズさせたい時はイージング関数。まあ基本前者でよいと思う。
この他にも式を弄ってあげると色んな挙動をするので、使い方次第で面白い効果が得られる。どんどん使ってUIを華々しくしよう。

*1:Robert Penner's Programming Macromedia Flash MX by Robert Penner