落下ブロックを落とす流れ
落下ブロックを落とす流れをフローチャートにすると次のようになります。
落下ブロックを落していく関数
const dropBlock = () => {
timeoutId = setTimeout(dropBlock, dropInterval);
const droppedBlock = structuredClone(block);
droppedBlock.position.y--;
// 落下後、床や固定ブロックと衝突しているかのチェックをする
if (!checkCollision(droppedBlock)) {
replaceBlock(droppedBlock);
// 衝突していた場合、ゲームオーバーになっているかチェックする
// ゲームオーバーになっていない場合、落下ブロックを固定ブロックの色に変える
changeBlockMaterial(fixedBlockMaterial);
const completedLayerIndexes = findCompleteLayers();
if (completedLayerIndexes.length !== 0) {
// 一面そろった層があったらそれらの層を色付けしたあと消す
hilightAndRemoveLayers(completedLayerIndexes);
// 一面そろった層がなかったら新しい落下ブロックを生成する
この関数は dropInterval
の間隔で繰り返し実行されます。 dropInterval
は1000にしているので、1000ミリ秒(1秒)間隔でブロックが落ちるようになります。
落下ブロックのマテリアルを入れ替える関数
const changeBlockMaterial = (material) => {
block.coordinates.forEach((coordinate) => {
const { x, y, z } = convertCoordinate(coordinate, block.position);
squares[y][z][x].material = material;
落下ブロックのマテリアルを引数で受け取ったマテリアルに交換します。
こうすることで落下ブロックの色が変わります。
ゲームオーバーになっているかチェックする関数
const checkGameover = () => {
const isGameover = block.coordinates.some((coordinate) => {
const { y } = convertCoordinate(coordinate, block.position);
// ブロックのy座標がステージの縦幅以上であればtrueとなる
return y >= heightLength;
some
関数を使い、ブロックのマス目のy座標が一つでもステージの縦幅を越えていたら、この関数はtrueを返します。
ゲームオーバーになったら実行する関数
changeBlockMaterial(overflowMaterial);
ゲームオーバーになったら clearTimeout
で dropBlock
の繰り返しを止めます。
一面がそろった層を探す関数
const findCompleteLayers = () => {
const completedLayerIndexes = [];
for (let y = 0; y < heightLength; y++) {
if (checkCompleteLayer(y)) {
completedLayerIndexes.push(y);
return completedLayerIndexes;
この関数は一面がそろった層の配列番号が入っている配列を返します。一面がそろった層がなければ空の配列を返します。
層が一面がそろっているかチェックする関数
const checkCompleteLayer = (y) => {
for (let z = 0; z < widthLength; z++) {
for (let x = 0; x < widthLength; x++) {
// ブロックが存在しないマス目があればfalseを返す
if (!squares[y][z][x].visible) {
引数で渡されたy座標の層が一面そろっているかチェックします。
1つでもブロックが存在しないマス目があればfalseを返し、すべてのマス目にブロックが存在していればtrueを返します。
一面がそろっている層の色を変える関数
const highlightLayers = (completedLayerIndexes) => {
completedLayerIndexes.forEach((y) => {
for (let z = 0; z < widthLength; z++) {
for (let x = 0; x < widthLength; x++) {
squares[y][z][x].material = completeMaterial;
引数で一面がそろっている層のy座標が配列を受け取ります。
その層のすべてのマス目のマテリアルを入れ替えて色を変えます。
一面がそろっている層を消す関数
層を消すときはその層のマス目の visible
を一つ上のマス目の visible
で入れ替えます。
こうすることで層が消えて、それより上の層が一段下がったように見えます。
また、消すそうが複数あるときはy座標が大きいほうから消していきます。
たとえば、1層目と3層目を消すとした場合、必ず3層を消してから1層を消さないといけません。
1層を先に消すと、各層をずらしたことによりもともと3層があった場所には4層が存在していることになりおかしなことになります。
const removeLayers = (completedLayerIndexes) => {
// 大きいほうから消すためreverseで配列を逆にする
completedLayerIndexes.reverse().forEach((removingY) => {
// 消す層とそれより上の層のマス目のmaterialとvisibleを入れ替えていく
for (let y = removingY; y < heightLength; y++) {
for (let z = 0; z < widthLength; z++) {
for (let x = 0; x < widthLength; x++) {
squares[y][z][x].material = fixedBlockMaterial;
squares[y][z][x].visible = squares[y + 1][z][x].visible;
一面がそろっている層の色を変えたあと層を消す関数
const hilightAndRemoveLayers = (completedLayerIndexes) => {
highlightLayers(completedLayerIndexes);
timeoutId =setTimeout(() => {
removeLayers(completedLayerIndexes);
timeoutId = setTimeout(dropBlock, dropInterval);
ここまでのプログラムを試そう
ここまで書いてきたプログラムを試してみましょう。
ここまで書き込んだらindex.htmlをブラウザから開いてみてください。
落下ブロックが落ちて固定ブロックがどんどん積みあがっていきます。
そして固定ブロックがステージからあふれると、ブロックの色が変わり動作が停止したら成功です!
確認したら上記のコードは消してください。