通常のJavaScriptと異なり、p5.jsやp5.play.jsの出来事はどれもキャンバスの中で起こります。したがって、p5.jsやp5.play.jsでは、何かをドラッグするというよりも、何かをドラッグしているように毎フレーム描画する、具体的には、マウスカーソルの位置に合わせ、スプライトの位置を更新して描画する、ということがドラッグ動作になります。これはまた、p5.jsやp5.play.jsには専門の関数やメソッドはなく、手計算で行う、ということでもあります。
ドラッグの論理は概ね、次の手順で作成します。
- 変数draggingやoffsetX、offsetYなどを用意する。
- マウスボタンを押したタイミングでdraggingをtrueにする。
- draw()関数内で、スプライトの位置をマウス位置やoffsetX、offsetY値に応じて設定する。
- マウスボタンを放したタイミングでdraggingをfalseにする。
ドラッグを可能にする方法はいくつもあります。以降では、矩形スプライトのドラッグと、円形スプライトのドラッグに見える方法を見ていきます。
目次
矩形スプライトのドラッグ
スプライトに限らず、コンピュータの描画物は基本的に矩形です。次の例では、p5.jsのmousePressed()関数でドラッグを開始し、mouseReleased()関数でドラッグを終了しています。
let anim = null;
let sp = null;
// スプライトの左上隅用変数
let x = 0;
let y = 0;
// スプライトの幅と高さ
let w = 0;
let h = 0;
let offsetX = 0;
let offsetY = 0;
let dragging = false;
function preload() {
anim = loadAnimation('assets/c.png');
}
function setup() {
createCanvas(500, 500);
sp = createSprite();
sp.addAnimation('normal', anim);
// スプライトの左上隅をキャンバス左上隅に合わせる
x = 192 / 2;
y = 144 / 2;
sp.position.x = x;
sp.position.y = y;
w = sp.width;
h = sp.height;
// カーソルを適宜変更するだけ
sp.onMouseOver = function() {
cursor(HAND);
}
sp.onMouseOut = function() {
cursor(ARROW);
}
}
function draw() {
background(200);
update();
drawSprite(sp);
}
function update() {
//マウスプレスした位置でスプライトをドラッグしているように見せる
if (dragging) {
x = mouseX + offsetX;
y = mouseY + offsetY;
}
sp.position.x = x;
sp.position.y = y;
}
// ページのどこかをマウスダウンしたとき呼び出される
function mousePressed() {
// スプライトの左上隅のxとy値
const spUpperLeftX = sp.position.x - w / 2;
const spUpperLeftY = sp.position.y - h / 2;
// マウスカーソルがスプライトの矩形内にあるなら
if (mouseX > spUpperLeftX && mouseX < spUpperLeftX + w && mouseY > spUpperLeftY && mouseY < spUpperLeftY + h) {
// ドラッグ開始
dragging = true;
cursor(HAND);
// スプライトの左上隅とマウス位置との距離
offsetX = x - mouseX;
offsetY = y - mouseY;
}
}
// ページのどこかをマウスリリースしたとき呼び出される
function mouseReleased() {
// ドラッグ終了
dragging = false;
cursor(ARROW);
}
円形スプライトのドラッグ
スプライトの”顔”に使用するPNG画像の背景を透明にすると、表示上は円形のスプライトが作成できます。しかし上記の方法をそのまま適用すると、イメージの透明部分をマウスでつまんでもドラッグできます。これを嫌う(見えている円形だけでドラッグできるようにしたい)場合には、何らかの工夫を行う必要があります。
重なりを利用する
キャンバスには、1drawサイクル(1回draw()関数が呼び出される間)で呼び出された順番に描画が行われ、後の描画は前の描画を上書きします。先に円を描画しそれをドラッグして、後に円とまったく同じ位置に円と同じ大きさの丸いスプライトを描画すると、見た目は丸いスプライトをドラッグしているように見えます。以下はその方法です。
let img = null;
let coinSp = null;
// コインPNGファイルの幅/高さ
const diameter= 50;
let offsetX = 0;
let offsetY = 0;
// 描画する円の座標用変数
let circleX = 0;
let circleY = 0;
let dragging = false;
function preload() {
img = loadImage('assets/coin.png');
}
function setup() {
createCanvas(500, 500);
// 円を最初に描画する位置
circleX = width / 2;
circleY = height / 2;
noStroke();
coinSp = createSprite();
coinSp.addImage(img);
// コインスプライトの位置は円にぴったり合わせる
coinSp.position.x = circleX;
coinSp.position.y = circleY;
}
function draw() {
background(200);
update();
drawSprite(coinSp);
}
function update() {
// ドラッグ中の円の位置
if (dragging) {
circleX = mouseX + offsetX;
circleY = mouseY + offsetY;
}
// // コインスプライトの位置を円にぴったり合わせる
coinSp.position.x = circleX;
coinSp.position.y = circleY;
// 円を描画 コインより少しだけ小さく
ellipse(circleX, circleY, diameter- 2, diameter- 2);
}
// ページのどこかをマウスダウンしたとき呼び出される
function mousePressed() {
print('押した');
// マウスカーソルが円内にあるかどうかを調べる
const distance = dist(mouseX, mouseY, circleX, circleY);
// 円内にあればドラッグ
if (distance < diameter) {
dragging = true;
offsetX = circleX - mouseX;
offsetY = circleY - mouseY;
// なければドラッグしない
}
else {
dragging = false;
}
// デフォルトイベントを回避
return false;
}
// ページのどこかをドラッグしたとき呼び出される
function mouseDragged() {
print('ドラッグ中');
if (dragging) {
circleX = mouseX;
circleY = mouseY;
}
return false;
}
// ページのどこかをマウスリリースしたとき呼び出される
function mouseReleased() {
print('放した');
dragging = false;
}
dist()は2点間の距離を計算するp5.jsの関数です。
スプライトのコライダーを設定する
スプライトのコライダー(Collider)は衝突判定に使用するp5.play.jsの機能ですが、マウスカーソルとの重なりにも利用できます。
let img = null;
let sp = null;
// コインPNGファイルの幅/高さ
const diameter= 50;
let draggedSprite = null;
let offsetX = 0;
let offsetY = 0;
function preload() {
img = loadImage('assets/coin.png');
}
function setup() {
createCanvas(500, 500);
sp = createSprite();
sp.addImage(img);
// 円形スプライトのドラッグにコライダーを利用
// コライダーを円形で設定。マウスイベントで使用できる
sp.setCollider('circle', 0, 0, diameter);
// コライダーを表示(円形)
sp.debug = true;
sp.position.x = width / 2;
sp.position.y = height / 2;
// スプライトがマウスプレスされたら
sp.onMousePressed = function() {
if (draggedSprite === null) {
// このスプライトをドラッグ
draggedSprite = this;
offsetX = draggedSprite.position.x - mouseX;
offsetY = draggedSprite.position.y - mouseY;
}
}
// スプライトマウスリリースされたら
sp.onMouseReleased = function() {
// draggedSpriteをnullにしてドラッグ終了
if (draggedSprite === this) {
draggedSprite = null;
}
}
}
function draw() {
background(200);
update();
drawSprite(sp);
}
function update() {
// nullでないdraggedSpriteをドラッグ
if (draggedSprite != null) {
draggedSprite.position.x = mouseX + offsetX;
draggedSprite.position.y = mouseY + offsetY;
}
}
下の実行画面で、コイン画像の緑の外枠線で示されているのがコライダーです。円形になっているのが分かります。
リファレンスメモ
Sprite.setCollider()
setCollider ( type offsetX offsetY width height )
スプライトのコライダーを設定する。p5.playのColliderオブジェクトは、そのスプライトに関して任意のサイズや位置が設定できる、不可視の円または矩形で、ほかのスプライトとの衝突や重なりの判定、マウスカーソルとの重なりの判定に使用する。
スプライトが衝突や跳ね返り、重なり、マウスイベントに関して調べられると、そのスプライトの作成時に渡されたwidthとheightパラメータから、またはアニメーションするスプライトの場合にはイメージのサイズから、そのスプライトのコライダーが自動的に作成される。
イメージの境界ボックスは多くの場合、衝突判定の領域として適切でないので、さまざまなサイズの円形や矩形のスプライトを設定し、スプライトのセンターからオフセット(有効範囲が設定)できる。
setCollider(“rectangle”)
setCollider(“rectangle”, offsetX, offsetY, width, height)
setCollider(“circle”)
setCollider(“circle”, offsetX, offsetY, radius)
パラメータ
type String – “rectangle”か”circle”
offsetX Number – スプライトのセンターからのColliderのx位置
offsetY Number – スプライトのセンターからのColliderのy位置
width Number – Colliderの幅か半径
height Number – Colliderの高さ