p5.play.jsでは、アニメーションが楽に作成できるようにするため、特にSpriteオブジェクトのプロパティやメソッドに多くの工夫がなされています。たとえばSprite.velocityは、draw()関数が1回呼び出される間に移動したい量を与えられるだけで、そのスプライトの次の位置を変更します。
目次
キーボード操作による移動制御と追跡
次のサンプルでは、Sprite.setSpeed()メソッドを使って、プレイヤースプライトを上下左右キーで移動させ、別のスプライトでSprite.attractionPoint()メソッドを使って、プレイヤースプライトを追いかけさせています。またキーボード操作でポイントになるkey変数の値をキャンバス画面左上に描画しています。
let playerAnim;
let playerSprite;
const speed = 1;
let birdAnim;
let birdAnimatedSprite;
const magnitude = 0.01;
let backImage;
function preload() {
playerAnim = loadAnimation('assets/enemy/000.png', 'assets/enemy/007.png');
birdAnim = loadAnimation('assets/monster/fly/fly/000.png', 'assets/monster/fly/fly/007.png');
backImage = loadImage('assets/background.png');
}
function setup() {
createCanvas(500, 300);
// 剣を持ったプレイヤー
playerSprite = createSprite();
playerSprite.addAnimation('walk', playerAnim);
setSpriteProperty(playerSprite, width / 2, height / 2);
// 追いかけて来るバードモンスター
birdAnimatedSprite = createSprite();
birdAnimatedSprite.addAnimation('fly', birdAnim);
setSpriteProperty(birdAnimatedSprite, 50, 50);
// 摩擦係数
birdAnimatedSprite.friction = 0.01;
// キャンバス左上に表示するkey値の文字の大きさと色
textSize(20);
fill(255);
}
function draw() {
// 背景にはイメージを全面に描く
image(backImage, 0, 0);
// 左上隅にkey値を表示
text('key : ' + key, 20, 25);
// バードモンスターにプレイヤーを追いかけさせる
const pointX = playerSprite.position.x;
const pointY = playerSprite.position.y;
birdAnimatedSprite.attractionPoint(magnitude, pointX, pointY);
// 全スプライトを描画
drawSprites();
}
function setSpriteProperty(sp, xpos, ypos) {
print(sp.width);
print(sp.height);
sp.position.x = xpos;
sp.position.y = ypos;
// sp.debug = true;
// sp.onMouseOver = (() => { });
}
// プレイヤーの移動を上下左右矢印キーで制御
function keyPressed() {
//print(keyCode)
//print(key)
const sp = playerSprite;
// 右矢印キーで0度の方向へspeedのスピードで移動
if (keyCode === RIGHT_ARROW) {
sp.setSpeed(speed, 0);
sp.mirrorX(1);
// 下矢印キーで90度の方向へspeedのスピードで移動
}
else if (keyCode === DOWN_ARROW) {
sp.setSpeed(speed, 90);
// 左矢印キーで180度の方向へspeedのスピードで移動
}
else if (keyCode === LEFT_ARROW) {
sp.setSpeed(speed, 180);
sp.mirrorX(-1);
// 上矢印キーで270度の方向へspeedのスピードで移動
}
else if (keyCode === UP_ARROW) {
sp.setSpeed(speed, 270);
// スペースキーで停止
}
else if (key === ' ') {
sp.setSpeed(0, 0);
}
}
剣を持った盗人のプレイヤースプライトは上下左右矢印キーで上下左右に移動し、スペースキーで止まります。バードモンスタースプライトはプレイヤースプライトを追いかけてきます。キーボードで操作できるようにするには、一度下の画面をクリックしてフォーカスを移す必要があります。
バードモンスターにプレイヤーを追いかけさせているのは、次のSprite.attractionPoint()メソッドです。attractionPoint()は指定された力の大きさで、指定されたポイントの方向にそのスプライトを向かわせます。これをdraw()内で使っているので、毎フレーム変わる可能性のあるプレイヤーに向かって、バードモンスターは移動をつづけます。ここでは変数magnitudeに代入した0.01を使っているので、”つきまとう”感じが出ています。0.03では”手下”の感じがします。いろいろ変えて試してみると面白いです。
const pointX = playerSprite.position.x;
const pointY = playerSprite.position.y;
birdAnimatedSprite.attractionPoint(magnitude, pointX, pointY);
上下左右キーによるスプライトの操作はp5.jsのkeyPressed()関数で行っています。ここで使っているkeyCodeはp5.jsの変数で、特殊なキー(BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, OPTION, ALT, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW)の判定に使用されます。スペースキーはこの対象ではないので、p5.jsの変数keyを使って判定しています。
プレイヤースプライトを移動させているのは、Sprite.setSpeed()メソッドです。このメソッドは指定されたスピードで、指定された方向にスプライトを移動させます。今の場合は変数speedの1を代入しているので、次の場合には、下矢印キーが押されると、1のスピードで90度の方向に移動することになります。
// 下矢印キーで90度の方向へspeedのスピードで移動
}
else if (keyCode === DOWN_ARROW) {
sp.setSpeed(speed, 90);
リファレンスメモ
Sprite.friction
friction Number
摩擦要因。スプライトの速度を減じる。摩擦は0に近くすべき(0.01など)。0は摩擦なしで、止まらない。1は完全な摩擦で、動かない。デフォルトは0。
Sprite.attractionPoint()
attractionPoint ( magnitude pointX pointY )
スプライトをポイントに向けて押す。力は現在のvelocityに追加される。
パラメータ
magnitude Number – 追加するスピード、スカラー値
pointX Number – 方向のx座標
pointY Number – 方向のy座標
Sprite.setSpeed()
setSpeed ( speed angle )
スプライトのスピードと方向を設定する。この呼び出しは現在の速度(velocity)を上書きする。方向が指定されない場合には、現在の方向が維持される。方向が指定されず現在の速度もない場合には、方向には現在の回転角度が使用される。
パラメータ
speed Number – スピード。スカラー値。
[angle] Number オプション – 度単位の方向
次のサンプルでは、車のスプライトが、上矢印キーで前進、下でバック、右で右旋回、左で左旋回できます。車の旋回をrotationプロパティで行うので、車のスプライトは、下図のように、X軸の正方向が前向き(車は真右を向くように)にします。
let carAnim;
let carSp;
let backImg;
const speed = 0.5;
function preload() {
carAnim = loadAnimation('assets/car/car1.png');
backImg = loadImage('assets/carPark.png')
}
function setup() {
createCanvas(500, 360);
carSp = createSprite();
carSp.addAnimation('car', carAnim);
// スピードが出るのを抑制する
carSp.maxSpeed = 2;
carSp.position.x = width / 2;
carSp.position.y = height / 2;
}
function draw() {
image(backImg, 0, 0);
drawSprite(carSp);
}
function keyPressed() {
const sp = carSp;
// 上キーで前進
if (keyCode === UP_ARROW) {
moveCar(sp, 0);
// notUseSetSpeed(sp, 1);
// 下キーでバック
}
else if (keyCode === DOWN_ARROW) {
moveCar(sp, 180);
// notUseSetSpeed(sp, -1);
// 右キーで右旋回
}
else if (keyCode === RIGHT_ARROW) {
rotateCar(sp, 1);
// 左キーで左旋回
}
else if (keyCode === LEFT_ARROW) {
rotateCar(sp, -1);
// スペースキーで急停止
}
else if (key === ' ') {
sp.setSpeed(0, 0);
}
}
// 前進/バック
function moveCar(sp, d) {
// 摩擦なし => スピードが出る
sp.friction = 0;
// 今の回転を調べる(バックのときは180を足す)
let r = sp.rotation + d;
// rの方向に1だけ移動
sp.setSpeed(1, r);
}
// 右/左旋回
function rotateCar(sp, r) {
// 曲がるときはスピードを落とす
sp.friction += 0.01;
// 10度だけ右か左へ回転
sp.rotation += r * 10;
}
// Sprite.setSpeed()を使わない方法
function notUseSetSpeed(sp, d) {
// 摩擦なし => スピードが出る
sp.friction = 0;
// rotation値をラジアンに変換
const angle = radians(sp.rotation);
// 角度angleのときの(x, y)座標を求める
const x = cos(angle);
const y = sin(angle);
// x軸方向にx分、y軸方向にy分進む
// dは1か-1。10は移動量を10倍にする
sp.velocity.x = d * x * 10;
sp.velocity.y = d * y * 10;
}
キーボードで操作できるようにするには、スケッチ画面を一度クリックしてフォーカスを移す必要があります。
前進とバックを行うのはmoveCar()関数です。この関数には引数として、車のスプライトと、rotationプロパティに加える角度の数値を与えます。前進時には何も加える必要はありません(つまり0)が、バック時には前進の正反対の方向に進めたいので、180を加えます。後はスプライトのsetSpeed()にスピードと角度を指定するだけです。
上記のnotUseSetSpeed()関数は、setSpeed()を使わないときの方法です。進みたい方向の角度が分かればサインとコサインの計算で具体的な(x, y)座標が求まるので、そのx値とy値を個別にスプライトのvelocityに足します。数学的な原理は「7_6:円運動 p5.js JavaScript」で述べています。
リファレンスメモ
Sprite.maxSpeed
maxSpeed Number
方向に関係なく、スプライトのスカラースピードに制限を設定する。値は正のみ。-1に設定すると、制限がなくなる。デフォルトは-1。
スプライトの頭を進行方向に向ける
上記サンプルでは、左右キーで車を旋回させているので、スプライトの進行方向はユーザーが制御することになります。スプライトはまたrotateToDirectionプロパティを持っており、これをtrueに設定すると、スプライトの”頭”(前)を自動的に進む方向に回転させることができます。これは次の例のように、前と後ろのあるスプライトでattractionPoint()を使うときに力を発揮します。
次の例では、駐車場のどこかをクリックすると、車のスプライトがその方向に向かって旋回し、移動します。またスプライトが出て行かない、簡単な境界も設定しています。
let carAnim;
let carSp;
let backImg;
const speed = 0.5;
function preload() {
carAnim = loadAnimation('assets/car/car2.png');
backImg = loadImage('assets/carPark.png')
}
function setup() {
createCanvas(500, 360);
carSp = createSprite();
carSp.addAnimation('car', carAnim);
// 回転方向に頭を向ける
carSp.rotateToDirection = true;
// 最大スピードを制限する
carSp.maxSpeed = 3;
carSp.friction = 0.01;
carSp.position.x = 40;
carSp.position.y = 150;
// マウスの状態を追跡する。=> これによりデバッグのコライダーの外枠表示が有効になる
carSp.mouseActive = true;
carSp.debug = true;
}
function draw() {
image(backImg, 0, 0);
update(carSp);
drawSprite(carSp);
}
function update(sp) {
// マウスプレスした位置にスプライトを向かわせる
if (mouseIsPressed) {
sp.attractionPoint(0.2, mouseX, mouseY);
}
// 境界の設定
if (sp.position.x > width - sp.width / 2) {
sp.position.x = width - sp.width / 2;
}
else if (sp.position.x - sp.width / 2 < 0) {
sp.position.x = sp.width / 2;
}
if (sp.position.y > height - sp.height / 2) {
sp.position.y = height - sp.height / 2;
}
else if (sp.position.y - sp.height / 2 < 0) {
sp.position.y = sp.height / 2;
}
}
スプライトのmouseActiveプロパティをtrueに設定すると、マウスの状態が追跡されるようになり、debugプロパティをtrueに設定することで、コライダーの外枠表示が有効になります。これは前の「5_2:p5.play スプライトの外見」などで、スプライトのonMouseOverプロパティに関数を設定していた方法に代わる、コライダーの外枠を表示する、より簡単な方法です。
// スプライトのコライダーの外枠を表示するための設定
paImageSprite.onMouseOver = (() => {});
リファレンスメモ
Sprite.mouseActive
mouseActive Boolean
trueに設定すると、スプライトはマウスの状態を追跡する。プロパティmouseIsPressedとmouseIsOverが更新される。注意:onMouseReleased関数またはonMousePressed関数が設定されている場合には、自動的にtrueに設定さる。デフォルトはfalse。
落下と跳ね返り
Sprite.addSpeed()メソッドは、指定された方向にスピードを追加します。たとえば、落下するスプライトspは次のコードで表現できます。gravityは重力をシミュレーションする変数で、90は90度、つまり真下を意味します。これをdraw()関数内で呼び出すことで、スプライトは毎フレームgravity分だけ、下方向に移動します。
const gravity = 1;
sp.addSpeed(gravity, 90);
上記コードは次のコードと同じです。
sp.velocity.y += gravity;
次のサンプルは、[スタート]ボタンのクリックで、カミナリ小僧が雲から落下し、下にあったトランポリンで跳ね返ります。[スタート]ボタンをクリックした後は、カミナリ小僧はクリックしたキャンバスのy位置から落下します。
let kaminariSprite;
let kaminariImage;
let cloudImage;
let trampolineImage;
let cloudPos;
let trampolinePos;
let bounceY;
const gravity = 1;
let startButton;
let isStart = false;
function preload() {
kaminariImage = loadImage('assets/kaminari.png');
cloudImage = loadImage('assets/cloud.png');
trampolineImage = loadImage('assets/trampoline.png');
}
function setup() {
createCanvas(300, 500);
// カミナリ小僧スプライトを1イメージで作成
kaminariSprite = createSprite();
kaminariSprite.addImage('kaminari', kaminariImage);
setSpriteProperty(kaminariSprite, width / 2, 80);
// 雲の位置 = キャンバスセンター => キャンバスの幅の半分 - 雲イメージの幅の半分
cloudPos = {
x: width / 2 - cloudImage.width / 2,
y: 90
};
// トランポリンの位置 = キャンバスセンター
trampolinePos = {
x: width / 2 - trampolineImage.width / 2,
y: 450
};
// カミナリ小僧を跳ね返らせたいY位置
// キャンバスの高さ - (カミナリスプライトの高さの半分) - (トランポリンイメージの高さの半分) + 微調整分
bounceY = height - (kaminariSprite.height / 2) - (trampolineImage.height / 2) + 10;
startButton = createButton('スタート');
startButton.position(220, 470);
startButton.size(80, 30);
startButton.mouseClicked(() => {
isStart = true;
});
}
function draw() {
// 背景色の描画
background(17, 177, 255);
// 雲イメージの描画
image(cloudImage, cloudPos.x, cloudPos.y);
// トランポリンイメージの描画 => カミナリ小僧はトランポリンの手前に置きたいので、
// カミナリスプライトより先に描画する
image(trampolineImage, trampolinePos.x, trampolinePos.y);
// [スタート]ボタンがクリックされたら
if (isStart) {
// カミナリスプライトの移動の論理(位置関係のプロパティの操作)
updateSprite();
}
// カミナリスプライトを描画
drawSprite(kaminariSprite);
}
function updateSprite() {
const sp = kaminariSprite;
// スプライトが既定高以上になったら(キャンバス外に出たら)
if (sp.position.y >= bounceY) {
// 垂直方向のスピードを反転 => 上に移動する
sp.velocity.y *= -1;
// 位置を既定高に設定して、突き抜けないようにする
sp.position.y = bounceY;
}
// スプライトを落下させる
sp.addSpeed(gravity, 90);
}
function setSpriteProperty(sp, xpos, ypos) {
sp.position.x = xpos;
sp.position.y = ypos;
//sp.debug = true;
//sp.onMouseOver = (() => { });
}
// スタート後は、クリックしたy位置からカミナリスプライトが落下する
function mousePressed() {
if (isStart) {
const sp = kaminariSprite;
sp.position.y = mouseY;
}
}
キャンバスにはカミナリ小僧と雲、トランポリンを描画しています。スプライトはカミナリ小僧だけで、後の2つはスプライトの機能を使わないので、イメージで済ませています。
カミナリ小僧の落下は前述したSprite.addSpeed()メソッドで行っていますが、トランポリンでの跳ね返りはifステートメントを使ったスプライトのプロパティの操作で行っています。スプライトのy位置がbounceY以上になったら、 sp.velocity.y *= -1 によって、スプライトは上に移動するようになります。しかし、ifステートメントの外でずっと sp.addSpeed(gravity, 90) を実行しているので、跳ね返りは落下分だけ差し引かれ、カミナリ小僧のジャンプは徐々に小さくなります。
ifステートメント内最後の sp.position.y = bounceY は重要です。カミナリ小僧のジャンプはやがてなくなりますが、 sp.addSpeed(gravity, 90) によってずっと落下しつづけるので、最終的な位置をしっかり決めておく必要があるのです。
Sprite.addSpeed()
addSpeed ( speed angle )
スプライトを、angleで定義された方向を押す。力は現在のvelocityに追加される。
パラメータ
speed Number – 追加するスピード、スカラー値
angle Number – 度単位の方向