今回の内容
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 です。
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);
}