p5.jsでシンセサイザーを作る 第4話 ノブ制作 回転の計算
Javascriptとp5.jsを使って、オリジナルなシンセサイザーを作るプログラミングの記事です。とりあえず何を作るのかを手っ取り早くお伝えしたいので、第0話で公開している完成品もチェックしてみてください。
ノブの動作を制御する
前回までにノブの描画に関する準備をしてきました。ここからはノブの動作そのものを制御するように制作していきます。
前回までの描画状況
本来、ノブの赤色マーカーはノブベースの円周上を回り込むように回転させていきたいところです。このため、今回は赤のマーカー位置を指定して、マウスがドラッグされるごとにその位置が反映されるようにプログラムを書いていきます。
変数の追加
let mousePosY;
let angle = -1.0;
let speed = 0;
const radius = knobSize/2-6;
mousePosYはノブがクリックされた時にマウスポインタのY軸座標を入れる変数
angleはノブのマーカーが回転して表示されるために計算する角度の変数
speedはマウスがドラッグされた時にどれくらいの回転を加えるかの変数
ということで宣言しています。
radiusはノブの中心からノブマーカーを描画する為の回転半径だと考えてください。
クリックされたノブの当たり判定処理
function touchStarted() {
if (
mouseX > knobX - knobSize/2 &&
mouseX < knobX + knobSize/2 &&
mouseY > knobY - knobSize/2 &&
mouseY < knobY + knobSize/2
) {
mousePosY = mouseY;
}
}
前回まで、マウスクリックされた場合、当たり判定に入るとknobStatがtrueになるようにしていました。
この直後に、マウスポインタのY軸であるmouseYを先ほど宣言したmousePosYに代入します。
function touchStarted() {
if (
mouseX > knobX - knobSize / 2 &&
mouseX < knobX + knobSize / 2 &&
mouseY > knobY - knobSize / 2 &&
mouseY < knobY + knobSize / 2
) {
knobStat = true;
mousePosY = mouseY; ////// <---ここです!!
}
}
ここからはマウスがドラッグされた時にノブの赤色マーカーを移動させるため、mousePosYをその起点にするため、マウスクリックが開始されて、当たり判定が成立した場所を確定しています。
ノブのマーカーを回転させる
function touchMoved() {
if(knobStat == true){
speed = (mousePosY - mouseY)/30;
angle += speed;
}
}
touchMoved()はクリックされたマウスがドラッグされた時に呼び出される関数です。
ドラッグが発生した時、(knobStat == true)であれば
ドラッグ開始地点であるmousePosYと現在の位置であるmouseYの差分を計算することで、ノブのマーカーの移動量を割り出します。
しかし、そのまま開始地点と現在の位置を計算すると、ユーザー目線ではノブの回転が早すぎてしまうため、ここでは30分の1のスピードになるように割り算して、speedに代入しています。
その後は角度を決定する変数angleにその移動量であるspeedを加算しています。
回転処理
function knobDraw() {
const sinValue = sin(angle);
const cosValue = cos(angle);
const knobVX = knobX + cosValue * radius;
const knobVY = knobY + sinValue * radius;
fill(250, 0, 0);
circle(knobVX, knobVY, 10)
}
実際に代入された赤マーカーの座標を代入します。ここで定義したknobDraw()は動きがある度にknobXとknobYを中心にして、knobVXとknobVYの位置にマーカーを描画するようにしています。
私はこのような回転運動の式を以下の3ステップで覚えるようにしています。
指定角度のサイン値とコサイン値を代入する
表示座標X = 中心 + コサイン値 * 半径
表示座標Y = 中心 + サイン値 * 半径
draw()で反映させる
function draw() {
background(250); //背景の色
noStroke(); // 枠線を消す
fill(100, 100, 100); //
circle(knobX - 3, knobY + 3 ,knobSize); //影の描画
stroke(150, 100, 100); //
strokeWeight(strokeValue); // 枠線の太さ
fill(200, 200, 200); //
circle(knobX, knobY ,knobSize); // ノブのベース
fill(250, 250, 250); //
circle(knobX, knobY, knobSize / 2); // ノブのトップ
/////ここは削除します
/*fill(250, 0, 0); //
circle(knobX, knobY, 10); // ノブのマーカー*/
/////ここは削除します
if (knobStat == true){ // マウスクリックでオレンジ色でノブトップを塗り替える
mousePosY = mouseY; /// <---ここを追加します
fill(255, 125, 0);
circle(knobX, knobY, knobSize / 2);
}
knobDraw() /// <---ここを追加します
}
draw()の中身を書き換えました。変更点はコード内のコメントに記述しています。
mousePosY = mouseY;を追加しています。これにより、draw()が実行されたとき(knobStat == true)であればmousePosYの値がマウスの現在地であるmouseYで上書きされ、ます。
その後、最後に追加したknobDraw()を実行することで、新しいangleに更新される事になります。
赤色のマーカーとオレンジのノブトップはknobDraw()の実行時に描画するようにしていますので、”ここは削除します” と囲ってある部分は不要。削除します。
ひとまず動作確認
これでノブを縦方向にドラッグすることでマーカーが回転するようになりました。
しかし、これだとノブはドラッグする限り永遠に回転し続けますので、最大値と最小値を設定したいところです。
ノブの可動域を制限する
仕上げにノブの最大値と最小値を制限していきます。
変数の追加
let angleMax = 0.8;
let angleMin = -3.95;
角度の最大値と最小値を宣言しました。
とりあえず目分量でいきます。
touchMoved()を追記する
function touchMoved() {
if(knobStat == true){
if (angle <= angleMax && angle >= angleMin){
speed = (mousePosY - mouseY)/30;
angle += speed;
}
if (angle >= angleMax) {
angle = angleMax;
}
if (angle <= angleMin) {
angle = angleMin;
}
}
}
angleの値が最大値と最小値の間だけ加算されるようにif文の中で制御します。
またangleが最大値を超える場合は最大値を代入し続ける、最小値を下回る場合は最小値を代入し続けることで、指定範囲の外に出ないように制御しています。
最後に動作確認
これでひとまずノブの制作は完了です。
このプログラムの全文
let knobX = 100;
let knobY = 100;
let knobSize = 50;
let strokeValue = 2;
let knobStat = false;
let mousePosY;
let angle = -1.0;
let speed = 0;
let angleMax = 0.8;
let angleMin = -3.95;
const radius = knobSize/2-6;
function setup() {
createCanvas(500, 400);
}
function draw() {
background(250); //背景の色
noStroke(); // 枠線を消す
fill(100, 100, 100); //
circle(knobX - 3, knobY + 3 ,knobSize); //影の描画
stroke(150, 100, 100); //
strokeWeight(strokeValue); // 枠線の太さ
fill(200, 200, 200); //
circle(knobX, knobY ,knobSize); // ノブのベース
fill(250, 250, 250); //
circle(knobX, knobY, knobSize / 2); // ノブのトップ
/////ここは削除します
/*fill(250, 0, 0); //
circle(knobX, knobY, 10); // ノブのマーカー*/
/////ここは削除します
if (knobStat == true){ // マウスクリックでオレンジ色でノブトップを塗り替える
mousePosY = mouseY; /// <---ここを追加します
fill(255, 125, 0);
circle(knobX, knobY, knobSize / 2);
}
knobDraw() /// <---ここを追加します
}
function knobDraw(){
const sinValue = sin(angle);
const cosValue = cos(angle);
const knobVX = knobX + cosValue * radius;
const knobVY = knobY + sinValue * radius;
fill(255, 0, 0);
circle(knobVX, knobVY, 10);
}
function touchStarted() {
if (
mouseX > knobX - knobSize/2 &&
mouseX < knobX + knobSize/2 &&
mouseY > knobY - knobSize/2 &&
mouseY < knobY + knobSize/2
) {
knobStat = true;
mousePosY = mouseY;
}
}
function touchMoved() {
if(knobStat == true){
if (angle <= angleMax && angle >= angleMin){
speed = (mousePosY - mouseY)/30;
angle += speed;
}
if (angle >= angleMax) {
angle = angleMax;
}
if (angle <= angleMin) {
angle = angleMin;
}
}
}
function touchEnded() {
knobStat = false;
}
実際にp5.jsのエディターにコピペして、試してみてください。