ロゴシュミグラム

ゲームを進行していく関数の作成

落下ブロックを落とす流れ

落下ブロックを落とす流れをフローチャートにすると次のようになります。

落下ブロックを落とすフローチャート

落下ブロックを落していく関数

index.html

const dropBlock = () => {
// この関数を繰り返し実行させる
timeoutId = setTimeout(dropBlock, dropInterval);
// blockをコピー
const droppedBlock = structuredClone(block);
// 落下ブロックを1マス落とす
droppedBlock.position.y--;
// 落下後、床や固定ブロックと衝突しているかのチェックをする
if (!checkCollision(droppedBlock)) {
// 衝突していない場合、落下ブロックを落とす
replaceBlock(droppedBlock);
} else {
// 衝突していた場合、ゲームオーバーになっているかチェックする
if (checkGameover()) {
gameover()
} else {
// ゲームオーバーになっていない場合、落下ブロックを固定ブロックの色に変える
changeBlockMaterial(fixedBlockMaterial);
// 一面そろった層の配列番号を得る
const completedLayerIndexes = findCompleteLayers();
if (completedLayerIndexes.length !== 0) {
// 一面そろった層があったらそれらの層を色付けしたあと消す
hilightAndRemoveLayers(completedLayerIndexes);
} else {
// 一面そろった層がなかったら新しい落下ブロックを生成する
initBlock();
}
}
}
};

この関数は dropInterval の間隔で繰り返し実行されます。 dropInterval は1000にしているので、1000ミリ秒(1秒)間隔でブロックが落ちるようになります。

落下ブロックのマテリアルを入れ替える関数

index.html

const changeBlockMaterial = (material) => {
block.coordinates.forEach((coordinate) => {
const { x, y, z } = convertCoordinate(coordinate, block.position);
squares[y][z][x].material = material;
});
};

落下ブロックのマテリアルを引数で受け取ったマテリアルに交換します。

こうすることで落下ブロックの色が変わります。

ゲームオーバーになっているかチェックする関数

index.html

const checkGameover = () => {
const isGameover = block.coordinates.some((coordinate) => {
const { y } = convertCoordinate(coordinate, block.position);
// ブロックのy座標がステージの縦幅以上であればtrueとなる
return y >= heightLength;
});
return isGameover;
};

some 関数を使い、ブロックのマス目のy座標が一つでもステージの縦幅を越えていたら、この関数はtrueを返します。

ゲームオーバーになったら実行する関数

index.html

const gameover = () => {
// 落下ブロックの色を変える
changeBlockMaterial(overflowMaterial);
// ゲームの状態を変える
gameState = "gameover";
// 落下ブロックを落とすのを止める
clearTimeout(timeoutId);
};

ゲームオーバーになったら clearTimeoutdropBlock の繰り返しを止めます。

一面がそろった層を探す関数

index.html

const findCompleteLayers = () => {
// 一面がそろった層の配列番号を入れていく配列
const completedLayerIndexes = [];
for (let y = 0; y < heightLength; y++) {
// 一面がそろっているかチェックする
if (checkCompleteLayer(y)) {
completedLayerIndexes.push(y);
};
}
return completedLayerIndexes;
};

この関数は一面がそろった層の配列番号が入っている配列を返します。一面がそろった層がなければ空の配列を返します。

層が一面がそろっているかチェックする関数

index.html

const checkCompleteLayer = (y) => {
for (let z = 0; z < widthLength; z++) {
for (let x = 0; x < widthLength; x++) {
// ブロックが存在しないマス目があればfalseを返す
if (!squares[y][z][x].visible) {
return false;
}
}
}
return true;
};

引数で渡されたy座標の層が一面そろっているかチェックします。

1つでもブロックが存在しないマス目があればfalseを返し、すべてのマス目にブロックが存在していればtrueを返します。

一面がそろっている層の色を変える関数

index.html

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 で入れ替えます。

3Dテトリスのブロックの消し方

こうすることで層が消えて、それより上の層が一段下がったように見えます。

また、消すそうが複数あるときはy座標が大きいほうから消していきます。

たとえば、1層目と3層目を消すとした場合、必ず3層を消してから1層を消さないといけません。

1層を先に消すと、各層をずらしたことによりもともと3層があった場所には4層が存在していることになりおかしなことになります。

index.html

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;
}
}
}
});
};

一面がそろっている層の色を変えたあと層を消す関数

index.html

const hilightAndRemoveLayers = (completedLayerIndexes) => {
// 落下ブロックを落とすのと止める
clearTimeout(timeoutId);
// 層の色を変える
highlightLayers(completedLayerIndexes);
// 一定時間後に以下を実行する
timeoutId =setTimeout(() => {
// 層を消す
removeLayers(completedLayerIndexes);
// 新たに落下ブロックを生成する
initBlock();
// 落下ブロックを落とすのを再開する
timeoutId = setTimeout(dropBlock, dropInterval);
}, dropInterval);
};

ここまでのプログラムを試そう

ここまで書いてきたプログラムを試してみましょう。

index.html

// お試し用。確認したら削除する
init3D()
initSquares();
initBlock();
dropBlock()

ここまで書き込んだらindex.htmlをブラウザから開いてみてください。

落下ブロックが落ちて固定ブロックがどんどん積みあがっていきます。
そして固定ブロックがステージからあふれると、ブロックの色が変わり動作が停止したら成功です!

確認したら上記のコードは消してください。