操作バーに当たったときの角度の変更
今のままだとボールの反射角度は常に一定で変化がありません。
そこで操作バーの当たる場所によってボールの反射角度を変化するようにします。
操作バーの右端に当たったら操作バーとの角度が40°、左にいくにつれ角度が大きくなり、左端の角度は140°とします。
bar.jsの clideBar
を変更します。
const clideBar = (ball) => {
const clidedPosition = (bar.rightX - ball.x) / barWidth;
const degree = clidedPosition * 100 + 40;
const { dx, dy } = calcDeltaXY(degree);
ボールが右端に当たった場合、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.sin
や Math.cos
が使えますが、角度はラジアンという単位で渡す必要があります。
角度をラジアンに変換するには、角度×π÷180と計算します
これらを踏まえて角度からdx、dyを計算する関数 calcDeltaXY
を作ります。
const calcDeltaXY = (degree) => {
const radian = degree * Math.PI / 180;
dx: Math.cos(radian) * speed,
dy: -Math.sin(radian) * speed,
最初に引数で受け取った角度をラジアンに変換しています。それのサインとコサインを計算して、settings.jsで設定した speed
を掛けています。 speed
の値が大きいほど移動距離が大きくなる、つまり移動スピードが速くなったようにみえます。
これで操作バーの当たる場所によってボールの反射角度を変化できるようになりました。
ボールの生成時の角度
balls.jsの createBall
を変更してボールの生成時に角度も指定できるようにします。
const createBall = (x, y, degree) => {
const { dx, dy } = calcDeltaXY(degree);
balls.push({ x, y, dx, dy, __proto__: ballProto });
ボールが操作バーに乗ってる状態から発射するときの角度を設定するため initBall
を変更します。
createBall(bar.x, bar.y - ballRadius, 80);
ここでは発射角度を80°にしていますが、自由に変更してもかまいません。
ブロックと衝突したときの反射方向の修正
今のままだとボールが左上に進行中にボールの右端だけがブロックと衝突した場合、右上に進行方向が変わってしまいます。
この場合は右下に反転したほうが自然です。よってボールのdxが0以下、つまり左方向に進行中の場合はdyの符号を逆にします。同じようにボールの上下左右の頂点で条件分けします。
ball.jsの checkBlockCollision
を修正します。
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]);
} else if (blocks[bottomRowIndex] && blocks[bottomRowIndex][centerColumnIndex]) {
clideBlock(ball, blocks[bottomRowIndex][centerColumnIndex]);
} else if (blocks[centerRowIndex] && blocks[centerRowIndex][leftColumnIndex]) {
clideBlock(ball, blocks[centerRowIndex][leftColumnIndex]);
} else if (blocks[centerRowIndex] && blocks[centerRowIndex][rightColumnIndex]) {
clideBlock(ball, blocks[centerRowIndex][rightColumnIndex]);