ロゴシュミグラム

ボールの反転角度を改善

操作バーに当たったときの角度の変更

今のままだとボールの反射角度は常に一定で変化がありません。

そこで操作バーの当たる場所によってボールの反射角度を変化するようにします。

操作バーの右端に当たったら操作バーとの角度が40°、左にいくにつれ角度が大きくなり、左端の角度は140°とします。

ブロック崩しのボールの反射角度

bar.jsの clideBar を変更します。

bar.js

const clideBar = (ball) => {
// 操作バーの当たった位置(0~1)
const clidedPosition = (bar.rightX - ball.x) / barWidth;
// 反射後の角度(40~140)
const degree = clidedPosition * 100 + 40;
// ボールのdxとdy計算する
const { dx, dy } = calcDeltaXY(degree);
ball.dx = dx;
ball.dy = dy;
};

ボールが右端に当たった場合、clidedPosition は0で、左に行くほど値は大きくなり、左端の場合は1となります。

clidedPosition* 100 + 40 とすることでボールが右端に当たった場合の角度は40、左に行くほど値は大きくなり、左端の場合は140となります。

calcDeltaXY は角度からボールのdxとdyを計算する関数で次で作成します。

ボールの進行方向を三角関数で計算する

ここでは簡単な三角関数の知識が必要となります。三角関数がわからない場合はプログラムをコピペするだけでも大丈夫です。

ボールのdxは三角関数のコサイン、dyはサインで計算できます。

たとえば、ボールの進行方向の角度が60°だとします。このときdxはcos 60°=0.5、dyは-sin 60°=-0.866...となります。

canvasのy軸は下に行くほど値が大きくなるため、dyはsinの符号が逆になることに注意してください。

ブロック崩しの角度を三角関数で求める

Javascriptで三角関数を計算するときは Math.sinMath.cos が使えますが、角度はラジアンという単位で渡す必要があります。

角度をラジアンに変換するには、角度×π÷180と計算します

これらを踏まえて角度からdx、dyを計算する関数 calcDeltaXY を作ります。

bar.js

const calcDeltaXY = (degree) => {
// 角度をラジアンに変換
const radian = degree * Math.PI / 180;
return {
dx: Math.cos(radian) * speed,
dy: -Math.sin(radian) * speed,
};
};

最初に引数で受け取った角度をラジアンに変換しています。それのサインとコサインを計算して、settings.jsで設定した speed を掛けています。 speed の値が大きいほど移動距離が大きくなる、つまり移動スピードが速くなったようにみえます。

これで操作バーの当たる場所によってボールの反射角度を変化できるようになりました。

ボールの生成時の角度

balls.jsの createBall を変更してボールの生成時に角度も指定できるようにします。

balls.js

const createBall = (x, y, degree) => {
const { dx, dy } = calcDeltaXY(degree);
balls.push({ x, y, dx, dy, __proto__: ballProto });
};

ボールが操作バーに乗ってる状態から発射するときの角度を設定するため initBall を変更します。

balls.js

const initBall = () => {
balls = [];
createBall(bar.x, bar.y - ballRadius, 80);
};

ここでは発射角度を80°にしていますが、自由に変更してもかまいません。

ブロックと衝突したときの反射方向の修正

今のままだとボールが左上に進行中にボールの右端だけがブロックと衝突した場合、右上に進行方向が変わってしまいます。

ブロック崩しのボールの反射方向

この場合は右下に反転したほうが自然です。よってボールのdxが0以下、つまり左方向に進行中の場合はdyの符号を逆にします。同じようにボールの上下左右の頂点で条件分けします。

ball.jsの checkBlockCollision を修正します。

ball.js

const checkBlockCollision = (ball) => {
const topRowIndex = Math.floor(ball.topY / blockHeight);
const centerRowIndex = Math.floor(ball.y / blockHeight);
const bottomRowIndex = Math.floor(ball.bottomY / blockHeight);
const leftColumnIndex = Math.floor(ball.leftX / blockWidth);
const centerColumnIndex = Math.floor(ball.x / blockWidth);
const rightColumnIndex = Math.floor(ball.rightX / blockWidth);
if (blocks[topRowIndex] && blocks[topRowIndex][centerColumnIndex]) {
clideBlock(ball, blocks[topRowIndex][centerColumnIndex]);
if (ball.dy < 0) {
ball.dy = -ball.dy;
} else {
ball.dx = -ball.dx;
}
} else if (blocks[bottomRowIndex] && blocks[bottomRowIndex][centerColumnIndex]) {
clideBlock(ball, blocks[bottomRowIndex][centerColumnIndex]);
if (ball.dy > 0) {
ball.dy = -ball.dy;
} else {
ball.dx = -ball.dx;
}
} else if (blocks[centerRowIndex] && blocks[centerRowIndex][leftColumnIndex]) {
clideBlock(ball, blocks[centerRowIndex][leftColumnIndex]);
if (ball.dx < 0) {
ball.dx = -ball.dx;
} else {
ball.dy = -ball.dy;
}
} else if (blocks[centerRowIndex] && blocks[centerRowIndex][rightColumnIndex]) {
clideBlock(ball, blocks[centerRowIndex][rightColumnIndex]);
if (ball.dx > 0) {
ball.dx = -ball.dx;
} else {
ball.dy = -ball.dy;
}
}
};