p5.jsでシンセサイザーを作る 第7話 ホイール制作の前編
Javascriptとp5.jsを使って、オリジナルなシンセサイザーを作るプログラミングの記事です。とりあえず何を作るのかを手っ取り早くお伝えしたいので、第0話で公開している完成品もチェックしてみてください。
シンセサイザーのホイール制作
キーボードの左側にあるピッチホイールを制作します。
ホイールの回転は、ノブの回転と全く異なるアプローチで実装します。
ノブの場合は、コサインやサインなどを使って左右の回転運動の計算をするのですが、あれも実は結構苦労して実装していました。
左右の回転でも色々思案したのに、ホイール回転では上から見たときに手前と奥に回転させるわけですから、なかなか難しそうです。ハマり必至か。。。と思いましたが、ここではもっと単純な方法で、ホイールの動きを表現する方法を考えます。
ホイールの場合UIとしては真上から見た時に回転している”ように見える”動作があれば十分です。
素材を見てみます。
どういうアプローチにするか
素材になるパーツを描画する事に関しては、もはや新しいことは無いです。しかし、このUIを作るにあたっては、画像右側の横線で描かれたシマシマ模様。
これがキモになります。この横線パーツは”目盛”と呼ぶことにします。
目盛だけを動かしている様子を見て下さい
横線の集まりを、上下に動かしています。そして、決まった高さよりも高くなると線は見えなくなります。決まった高さよりも低い場合でも、線は見えなくなります。
どうやって表現するのか
あまり複雑な実装はいりません。線の描画座標の上限、下限をif文で制限しているだけです。決まった範囲に入った線だけを描画しているのです。
for文で線を描画していきます。
let x = 100;
let y = 50;
let lineSize = 50;
let lineInterval = 10;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(200);
fill(250,125,0);
for (let i = 0; i < 10; i++){
line (x, y + i * lineInterval, x + lineSize, y + i * lineInterval);
}
}
シンプルなコードで中身を解説します。ここではdraw()の中でforループが記述してあり、10本の線を描画しています。p5.jsのline()では引数が4つあり、左端のX,Yと右端のX,Y座標を指定する事で線を描画します。
for (let i = 0; i < 10; i++){
line (x, y + i * lineInterval, x + lineSize, y + i * lineInterval);
}
このループで記述したforループ内にあるline()の引数を日本語で表現すると
第一引数:左端のxはx
第二引数:左端のyはy プラス iのlineInterval倍
第三引数:右端のxはx プラス lineSize (つまりこれが横線の長さ)
第四引数:右端のyはy プラス iのlineInterval倍 (左端と同じ高さ)
ループ文の中でiが増えるごとに変数lineIntervalの10ピクセル分したにズレて線が描画される。これが10回繰り返されている。という仕組みになっています。
指定した上下の範囲内だけ、線を描画する
それでは、横線の高さがある範囲を越えたら、線の描画を行わない。という処理を付け加えます。ある範囲、というのは、このプログラムでいうと、ホイールの上下範囲から外に出ている部分ですね。
言い換えると、ホイールの上下範囲のみ、線を描画するとも言えます。
これはif文を使って制御しますが、変数として、線の上限と下限も宣言しておきます。
変数の追加
let slideMax = 150; //指定範囲の最大値
let slideMin = 100; //指定範囲の最小値
forループにif文を追加する
for (let i = 0; i < 10; i++){
if(y + i * lineInterval <= slideMax && y + i * lineInterval >= slideMin){
line (x, y + i * lineInterval, x + lineSize, y + i * lineInterval);
}
}
for文の中にif文を追加しました。
ここでは、
y + i * lineIntervalが、slideMax以下で、
なおかつ
y + i * lineIntervalが、slideMin以上の時
line()を実行する
という表現になっています。
簡単にいうと、線の高さがslideMaxとslideMinの間に入っていれば線を描画する。
という感じです。
この制限によって、10本あった線が5本まで減りました。つまり、slideMaxとslideMinの間には5本だけが該当していたという結果になります。
もう一度、ホイールの全体像を見てみる
あらためて動作している様子を見ると、forループとif文による範囲指定がうまく応用されて、ホイール内の目盛が上下に動いているという仕組みが見て取れると思います。
長くなるので、後編に続きます
線の描画範囲を指定することで、目盛が上下に移動した時にホイールが上下に動いているように表現できそうです。次回は、このロジックをもとに実際にホイールがマウスで操作できるようにプログラムを制作していきます。
この記事のコード全文
let x = 100;
let y = 50;
let lineSize = 50;
let lineInterval = 10;
let slideMax = 150; //指定範囲の最大値
let slideMin = 100; //指定範囲の最小値
function setup() {
createCanvas(400, 400);
}
function draw() {
background(200);
fill(250,125,0);
for (let i = 0; i < 10; i++){
if(y + i * lineInterval <= slideMax && y + i * lineInterval >= slideMin){
line (x, y + i * lineInterval, x + lineSize, y + i * lineInterval);
}
}
}
完成版ホイールのコード全文
let strokeValue = 2;
let mousePosX = 0.0;
let mousePosY = 0.0;
let slideX = 70;
let slideY = 170;
let slideSizeX = 22;
let slideSizeY = 100;
let slideKnobX = slideX + 8;
let slideKnobY = slideY + slideSizeY / 2 - 5;
let slideKnobCenterY = slideY + slideSizeY / 2 - 5;
let slideStat = false;
let slideMax = 176;
let slideMin = 254;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(250);
fill(110,110,110);
rect(slideX,slideY,slideSizeX,slideSizeY,2);
fill(180,180,180);
rect(slideX + 2 ,slideY + 4, 17 ,slideSizeY - 8,2);
fill(150,150,150);
stroke(150, 100, 100);
noStroke();
triangle(slideX + 2 ,slideY + 4,
slideX + 2 ,slideY + slideSizeY - 4,
slideX + 17,slideY + slideSizeY - 4);
stroke(strokeValue);
slideLineDraw();
slideKnobDraw();
slideLineDraw();
}
function slideKnobDraw(){
fill(250, 250, 250);
rect(slideX + 1,slideKnobY,18,10);
if (slideStat == true){
fill(255, 125, 0);
rect(slideX + 1,slideKnobY,18,10);
}
}
function slideLineDraw(){
fill(255, 125, 0);
for (let i = 0; i < 10; i++){
if (slideKnobY - i * 8 >= slideMax){
line(slideX + 3 ,slideKnobY - i * 8,slideX + 17,slideKnobY - i * 8);
}
}
for (let i = 0; i < 10; i++){
if (slideKnobY + i * 8 <= slideMin){
line(slideX + 3,slideKnobY + 10 + i * 8,slideX + 17 ,slideKnobY + 10 + i * 8);
}
}
}
function touchStarted() {
if (
mouseX > slideX &&
mouseX < slideX + 30 &&
mouseY > slideY &&
mouseY < slideY + slideSizeY
) {
slideStat = true;
//slideY= 0
mousePosY = mouseY;
}
}
function touchMoved() {
if(slideStat == true){
slideKnobY = mouseY - mousePosY + slideKnobCenterY;
if (slideKnobY <= slideMax) {
slideKnobY = slideMax;
}
if (slideKnobY >= slideMin) {
slideKnobY = slideMin;
}
slideOct = map(slideKnobY, slideMin, slideMax,0.5,2);
}
}
function touchEnded() {
slideStat = false;
slideKnobY = slideY + slideSizeY / 2 - 5;
}
実際にp5.jsのエディターにコピペして、試してみてください。
なんとか次回までにホイールは実装したいですね。