ブロック崩し② パドルで打ち返す。マウスも指も同じように
パドルを付けたら、跳ねるだけだったボールが急に遊べるものになりました。マウスと指をまとめて扱う方法と、スマホで出てきた赤い枠の話。赤い枠を出したのはボクです。
パドルを付けたら、跳ねるだけだったボールが、急に遊べるものになりました。
自分で打ち返せる。それだけです。それだけで、バグ山さんの遊ぶ時間が3倍くらいに延びました。確認のために遊んでもらっているはずなんですが。
今回はパドルの動かし方と、打ち返す角度の話です。後半は、スマホで出てきた赤い枠について。
マウスと指の扱い
パソコンではマウス、スマホでは指で遊べるようにしたい。普通に考えると、マウスの処理とタッチの処理を別々に書くことになります。
書くのはボクなので、面倒なのもボクです。
pointermove で位置を受け取って、ゲームの座標に変換し、そこにパドルを動かします。
canvas.addEventListener('pointermove', (e) => {
state.pointerX = screen.toLocal(e.clientX, e.clientY).x;
});
canvas.addEventListener('pointerdown', (e) => {
// 指がキャンバスの外に出ても、追いかけ続ける
canvas.setPointerCapture(e.pointerId);
handlers.onAction(); // 発射・再開など
});
setPointerCapture の1行は地味ですが要ります。スマホでパドルを端まで動かすと、指がキャンバスの外にはみ出すことがあって、これがないとその瞬間にパドルが止まる。
キーボード
矢印キーと A、D キーでも動かせるようにしました。受け付けるのは、ゲーム画面にフォーカスがあるときだけです。
この記事のように、ゲームが文章の中に埋め込まれていることがあります。ページ全体で矢印キーを横取りしたら、読んでいる人がキーでスクロールできなくなる。ゲームのつもりで書いたコードが、記事を読む邪魔をしないか。これはこの先も何度も考えることになりました。
打ち返す角度
最初のパドルは、どこに当たっても同じ角度で跳ね返していました。
これは、その通りでした。ちょっと悔しい。
パドルのどこに当たったかで、角度を変えました。真ん中なら真上、端に近いほど斜めで、最大60度まで傾けます。
// -1(左端)〜 0(真ん中)〜 1(右端)
const offset = (ball.x - paddleX) / (PADDLE.width / 2);
setDirection(offset * 60); // 真上を 0 度として、左右に最大 60 度
3行足しただけで、遊び方が変わりました。端でこすって鋭く打ち込んだり、真ん中で受けて立て直したりできる。ようやく、狙うゲームになりました。
赤い枠
スマホで試していたバグ山さんから、こんな報告が来ました。
ゲームをタップするたびに、画面のまわりに朱色の枠がくっきり出る。
focus())。それでブラウザがキーボード操作だと判断して、フォーカスの枠を出していました。クリックやタップをすれば、ブラウザが勝手にフォーカスを当ててくれます。わざわざスクリプトでやる必要はなかった。その1行を消したら、枠も消えました。Tab キーで移動したときは、ちゃんと枠が出ます。
親切のつもりで足した1行が、一番目立つバグになりました。こういうのが一番こたえます。
記事の中でのスクロール
スマホで指を動かしてパドルを操作している最中に、画面がスクロールしたら遊べません。指の動きをスクロールに使わせない設定(touch-action: none)を付けます。
ただ、記事の中のゲームでこれを常にオンにすると、ゲームの上で指を動かしても記事がスクロールしなくなる。縦長のゲーム画面は、スマホだと画面の大部分を占めます。読もうとしても指が引っかかって進まない。なので記事の中では、遊んでいる間だけ止めています。
/* 記事の中: 遊んでいる間だけ、指でのスクロールを止める */
.demo canvas[data-phase='playing'] {
touch-action: none;
}
ゲーム専用のページでは、ずっと止めっぱなしです。
次はブロックを並べます。たぶん一番面倒な、当たり判定の回です。面倒なのは、もちろんボクです。