落下ブロックを移動させる関数
const moveBlock = (event) => {
const movedBlock = structuredClone(block);
// 移動ボタンのvalueの値で移動方向を決める
switch (event.target.value) {
case 'left': movedBlock.position.x += -1;
case 'right': movedBlock.position.x += 1;
case 'front': movedBlock.position.z += 1;
case 'back': movedBlock.position.z += -1;
// 壁や固定ブロックと衝突していないかチェックする
if (!checkCollision(movedBlock)) {
// 衝突していなければ現在の落下ブロックと移動後の落下ブロックを交換する
replaceBlock(droppedBlock);
この関数はclickのイベントリスナーとして登録するので、引数にはclickイベントを受け取ります。
そして登録された移動ボタンのvalue属性には方向が設定されてます。
もし、それがright(右方向)であれば落下ブロックの位置をx軸方向に1増やし、front(手前方向)であれば位置をz軸方向に1減らしたりします。
落下ブロックを移動するには、まず移動したあとの落下ブロックを壁や固定ブロックと衝突しないかチェックしなければなりません。衝突しなければ移動させ、衝突するようであれば何もしません。
block
をコピーした movedBlock
に移動後の位置を設定します。それを使って衝突するか否かのチェックをおこないます。
衝突していなければ現在の落下ブロックと移動後の落下ブロックを交換します。
こうすることで落下ブロックが1マスずれて表示されるようになります。
や固定ブロックと衝突するかチェックする関数
const checkCollision = (block) => {
const isMobable = block.coordinates.some((coordinate) => {
const { x, y, z } = convertCoordinate(coordinate, block.position);
// x, y, zが壁と衝突している、または固定ブロックと衝突しているばtrueとなる
return checkWallCollision(x, y, z) || checkBlockCollision(x, y, z);
引数に移動後の落下ブロックを受け取ります。
some
関数を使い、落下ブロックの座標が一つでも壁か固定ブロックに衝突していたら、この関数はtrueを返します。
座標が壁と衝突しているかチェックする関数
const checkWallCollision = (x, y, z) => {
return x < 0 || x >= widthLength || z < 0 || z >= widthLength || y < 0;
引数で渡されたx, y, zの座標が衝突しているか、つまりステージの外に出てしまっているのであればtrueを返します。
座標が固定ブロックと衝突しているかチェックする関数
const checkBlockCollision = (x, y, z) => {
return squares[y][z][x].visible && squares[y][z][x].material === fixedBlockMaterial;
引数で渡されたx, y, zの座標にブロックが存在しており、それが固定ブロックであればtrueを返します。
三次元のブロックを回転させる方法
次のようなブロックがあったとします。
このブロックのマス目の座標は次の通りです。
これを真ん中のマス目を中心にして奥側に90°回転させると次のような形になります。
このブロックのマス目の座標は次の通りです。
これを一般化すると次のようになります。
回転方向 | 回転後のx | 回転後のy | 回転後のz |
---|
奥 | x | z | |y-a-1| |
手前 | x | |z-a-1| | y |
左 | |z-a-1| | y | x |
右 | z | y | |x-a-1| |
- 表中のx、y、zは回転前の座標です。
- aは落下ブロックの1辺のマス目の数です(
blockSideLength
の値) - |...|は絶対値を表しています。
上の画像のケースでは、回転前の座標 { x:1, y:1, z:2 }
から回転後の座標を上の表を使って計算してみると、 { x:1, y:2, z:1 }
となることが確認できます。
落下ブロックを回転させる関数
const rotateBlock = (event) => {
const rotatedBlock = structuredClone(block);
const tmp = blockSideLength - 1;
rotatedBlock.coordinates = block.coordinates.map((coordinate) => {
event.target.value === 'left' ? Math.abs(coordinate.z - tmp)
: event.target.value === 'right' ? coordinate.z : coordinate.x;
event.target.value === 'front' ? Math.abs(coordinate.z - tmp)
: event.target.value === 'back' ? coordinate.z : coordinate.y;
event.target.value === 'back' ? Math.abs(coordinate.y - tmp)
:event.target.value === 'right' ? Math.abs(coordinate.x - tmp)
: event.target.value === 'left' ? coordinate.x : coordinate.y;
return { x: rotatedX, y: rotatedY, z: rotatedZ };
// 壁や固定ブロックと衝突しているかチェックする
if (!checkCollision(rotatedBlock)) {
// 衝突していなければ現在の落下ブロックと回転後の落下ブロックを交換する
replaceBlock(rotatedBlock);
この関数もclickのイベントリスナーとして登録します。
上記の moveBlock
関数と同じ流れで衝突するか否かをチェックして、しなければ回転させます。
イベントリスナーとして登録する
// クラス名がmove-buttonのボタンすべてにmoveBlockを登録する
document.querySelectorAll('.move-button').forEach((button) => {
button.addEventListener('click', moveBlock);
// クラス名がrotate‐buttonのボタンすべてにrotateBlockを登録する
document.querySelectorAll('.rotate-button').forEach((button) => {
button.addEventListener('click', rotateBlock);
クラス名が move-button
の各ボタンをクリックすると moveBlock
を実行し、 rotate-button
の各ボタンをクリックすると rotateBlock
を実行します。
ここまでのプログラムを試そう
ここまで書いてきたプログラムを試してみましょう。
ここまで書き込んだらindex.htmlをブラウザから開いてみてください。
移動ボタンを押したら落下ブロックが前後左右に動き、回転ボタンを押して回転したら成功です!(壁と衝突するようであれば動きません)
確認したら上記のコードは消してください。