Processingで滑らかなアニメーションを実現する(1)

子供のスキー教室が雪のため延期になりました。雪が降りすぎてバスがスキー場までたどり着けないからだそうです。何事もほどほどが肝心ですね。

今回はProcessingで安定したアニメーションを実現する方法について書いてみたいと思います。

Processingのアニメーションの問題点

Processingでアニメーションを行う場合、通常draw関数の中で座標や色などの更新処理と各種図形の描画処理を記述します。一応このやり方で絵は動くのですが、いくつか問題があります。

問題① アニメーションが安定しない

Processingでdraw関数の中にアニメーション処理を書くと、再生時にアニメーションが時々ひっかかって見えることがあります。これはProcessing内部の処理で定期的にJava(Processing.jsの場合はJavaScript)のGCガベージコレクション)が発生し、その際に画面の更新が遅れるためです。

GCを抑制する工夫はいろいろあるのですが、Processing本体のコードを修正する必要があり、また言語仕様上避けられない部分もあるため、完全になくすことは困難です。

問題② 高負荷時にアニメーションが遅延する

また、draw関数の中の処理に時間がかかるとアニメーションの再生速度が遅くなることがあります。これは、一回の描画にかかる時間が所定の時間(frameRateが60の場合は1/60≒16.7msec)を超えた場合に、画面の更新が本来のタイミングに間に合わなくなるために起きる現象です。

各種処理の負荷が高くなりすぎないよう、あらかじめ調整することである程度の対応は可能ですが、スマートフォンやブラウザなども含めた全ての動作環境で同じアニメーションを実現しようとすると限界が出てきます。

安定しないアニメーションの例

サンプルとしてアニメーションが安定しないコードを書いてみます。複数の円を等速で動かし、円の数を徐々に増やしていくプログラムです。

final int OBJ_NUM = 500; // 円の最大数
float[] x = new float[OBJ_NUM];
float[] y = new float[OBJ_NUM];
float[] vx = new float[OBJ_NUM];
float[] vy = new float[OBJ_NUM];

void setup() {
  size(400, 200);

  for (int i = 0; i < OBJ_NUM; i++) {
    x[i] = random(width);
    y[i] = random(height);
    vx[i] = random(1) > 0.5 ? 1 : -1;
    vy[i] = random(1) > 0.5 ? 1 : -1;
  }
}

void draw() {
  background(0);

  // 新しく追加された円が奥側に描画されるようループを逆に回す
  for (int i = (frameCount * 5) % OBJ_NUM; i >= 0; i--) {
    if (x[i] < 0 || x[i] > width) { vx[i] *= -1; }
    if (y[i] < 0 || y[i] > height) { vy[i] *= -1; }

    x[i] += vx[i];
    y[i] += vy[i];

    fill(255 - i % 128);
    ellipse(x[i], y[i], 15, 15);
  }
}

実際に動かしてみると、アニメーションが時々ひっかかったり、徐々に遅くなっていくことがわかります。

思った以上に前振りが長くなってしまいました。次回はこの問題の解決方法をコード例を交えて解説していきたいと思います。