5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

記事投稿キャンペーン 「2024年!初アウトプットをしよう」

p5.js でのループアニメーションで「frameCount」と「剰余演算 or 三角関数」を使う :2D・3D のそれぞれで x方向に往復する動きを例に +α

Last updated at Posted at 2024-01-02

今回の内容

p5.js でループするアニメーションを作る際に、frameCount と剰余演算を組み合わせたものをよく使います。それに関連する話では以下の記事を書いていました。

●frameCount・剰余演算を使った p5.js でのループアニメーション:アニメーションの最初と最後に空白の時間を作る - Qiita
 https://qiita.com/youtoy/items/68f9dc64cb6cbe912bc4

そして今回は、「値の増加 ⇒ 値の減少 ⇒ 値の増加 ⇒ ...」という内容のループアニメーションを作る時の話です。それについて、「frameCount と 剰余演算」を組み合わせた方法について書きつつ、「frameCount と 三角関数」を組み合わせた方法も合わせて書いてみます。

2D

まずは 2D の描画で、横方向の位置座標を変化させる場合の例を書いてみます。

実装内容

最初に、例として実装した 3つの内容を掲載します。
他の実装方法もあると思いますが、試してみた内容のうち 3つをピックアップしてみました。

function setup() {
  createCanvas(450, 350);

  fill(190, 160, 250);
  noStroke();
}

function draw() {
  background(30);

  const maxCount = 200;
  const f = frameCount % maxCount;

  // 例1
  const value =
    f < maxCount / 2
      ? map(f, 0, maxCount / 2, 0, 1)
      : map(f, maxCount / 2, maxCount, 1, 0);
  const x1 = width * value;
  circle(x1, 0.2 * height, 20);

  // 例2
  const f2 = f / (maxCount / 2);
  const x2 = width * min(f2, 2 - f2);
  circle(x2, 0.4 * height, 20);

  // 例3
  const x3 = (width * (1 + cos(frameCount / 30 + PI))) / 2;
  // const x3 = (width * (1 + sin(frameCount / 30 - HALF_PI))) / 2;
  circle(x3, 0.6 * height, 20);
}

処理内容の補足

上に掲載した 3つの方法について、それぞれを簡単に補足します。

例1・例2 は、以下の frameCount・剰余演算を使った処理で計算した f の値をもとにアニメーションさせます。

  const maxCount = 200;
  const f = frameCount % maxCount;

例1 について

例1 では、剰余演算で作られた 0 から 199 の値を前半分・後ろ半分に分けて、それらを x方向の右移動・x方向の左移動にマッピングしています。マッピングには、 p5.js の関数の map() を使いました。
余談ですが、「map()」は今回の例に限らず、値の範囲を変換するのに重宝しています。

例2 について

例2 では、剰余演算で作られた 0 から 199 の値を、0 から 1.99 までの値にして、さらにそれを処理した値を使います。最後に書いた処理について、具体的な処理は min(f2, 2 - f2) の部分です。
ここで、1 を超える値になったところで 2-f2 のほうが選択されるようになり、1 から 0 へ値が動く部分が作れます。

例3 について

例3 は、frameCount を三角関数(ここでは cos()・sin())の角度の部分で使っています。
どちらも、-1 から 1 の値をとるので、それが 0 から 1 の値になるような計算を入れています。

三角関数を使った場合、その前の 2つとは違って線形な変化ではなくなります。その違いがあるのはご留意ください。

別の書き方

map() を使った処理内容は、特定の範囲の値を 0 から 1 の範囲にするものです。
これは p5.js の norm() を使って、以下のように書きかえることもできます。

書きかえ前
  const value =
    f < maxCount / 2
      ? map(f, 0, maxCount / 2, 0, 1)
      : map(f, maxCount / 2, maxCount, 1, 0);
書きかえ後
  const value =
    f < maxCount / 2
      ? norm(f, 0, maxCount / 2)
      : norm(f, maxCount, maxCount / 2);

また、以下の書き方でも OK です。

書きかえ後2
  const [lower, upper] =
    f < maxCount / 2 ? [0, maxCount / 2] : [maxCount, maxCount / 2];
  const value = norm(f, lower, upper);

3D

次は WEBGLモードを使った、3D描画の場合です。この場合、画面中央が原点になるため、その原点を起点にする形(x方向の画面中央を起点とした形)にしてみます。

function setup() {
  createCanvas(450, 350, WEBGL);

  fill(190, 160, 250);
  noStroke();
}

function draw() {
  background(30);

  const maxCount = 200;
  const f = (maxCount / 4 + frameCount) % maxCount;

  // 例1
  const value1 =
    f < maxCount / 2
      ? map(f, 0, maxCount / 2, -1, 1)
      : map(f, maxCount / 2, maxCount, 1, -1);
  const x1 = 0.3 * width * value1;
  circle(x1, -0.2 * height, 20);

  // 例2
  const f2 = f / (maxCount / 2);
  const value2 = f2 < 1 ? 2 * f2 - 1 : -2 * f2 + 3;
  const x2 = 0.3 * width * value2;
  circle(x2, 0, 20);

  // 例3
  const x3 = 0.3 * width * cos(frameCount / 35 - HALF_PI);
  // const x3 = 0.3 * width * sin(frameCount / 30);
  circle(x3, 0.2 * height, 20);
}

基本的に、2D用に作ったものと同じような処理にしました。
画面中央の原点を起点にするための内容を加えています。

値の変化のスピードを変える

上で書いた例で、値の変化のスピードを変えたい場合もあります。

その場合は、 f の値を算出している部分の frameCount に、何らかの係数を乗算(または何らかの値で除算)してやれば OK です。もしくは、maxCount の値を変化させても良いです。

上の 3D のほうの 例1・例2 に関し、maxCount の値はそのままで変化のスピードを早くしたい場合は、例えば以下のようにします。

  const f = (maxCount / 4 + frameCount * 3) % maxCount;

例3 についても同様です。上で書いたものでは、frameCount を 35 で除算していましたが、この除算する値を大きくしたり小さくしたりすることで、変化のスピードを変えられます(また、何らかの値を乗算しても良いです)。

  const x3 = 0.3 * width * cos(frameCount / 35 - HALF_PI);

おわりに

他にも p5.js でループアニメーションを作るやり方はありますが、今回は往復する動きを「frameCount と剰余演算を組み合わせたもの」か、または「frameCount と三角関数を組み合わせたもの」で作ってみました。

frameCount と rotate()・translate() を組み合わせて作る方法もありますが、それは、さらに他の要素も加えた内容にして、別の記事に書こうと思います。

【追記】 余談

最後に書いている「frameCount と rotate()・translate() の組み合わせ」という内容について、少しトリッキーな感じになっている気がしますが、コードを紹介してみます。

let a = 0;
let b = 0.2;

function setup() {
  createCanvas(450, 350);

  fill(190, 160, 250);
  noStroke();
}

function draw() {
  background(30);

  const maxCount = 100;
  const f = frameCount % maxCount;

  if (f === 0) {
    a += PI;
    b = 1 - b;
  }
  const value = f / maxCount;
  const x1 = width * value;

  translate(width / 2, height / 2);
  rotate(a);
  translate(-width / 2, -height / 2 + b * height);
  circle(x1, 0, 20);
}
5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?