ロゴシュミグラム

マス目とブロックの関数の作成

落下ブロックの形の設定

次のような2つの落下ブロックの形を作ります。

3Dテトリスの落下ブロック形1

3Dテトリスの落下ブロック形1

これを三次元配列で表します。ブロックがあるマス目は1、ないマス目は0とします。

そして、これらの落下ブロックを blockShapeList という配列に入れていきます。

index.html

// 落下ブロックのリスト
const blockShapeList = [
// 一つ目の落下ブロック
[
[
// 一番下の層
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
[
// 真ん中の層
[0, 0, 0],
[0, 1, 1],
[0, 1, 0],
],
[
// 一番上の層
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
],
// 二つ目の落下ブロック
[
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
[
[1, 1, 0],
[1, 1, 0],
[0, 0, 0],
],
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
],
];

全体のマス目を初期化する関数

全体のマス目も三次元配列で表します。 squares に三次元配列を作成していき、すべての要素にThree.jsで作った立方体のオブジェクトを入れます。

index.html

const initSquares = () => {
// マス目の三次元配列を作成していく
squares = [];
for (let y = 0; y < fullHeightLength; y++) {
squares[y] = [];
for (let z = 0; z < widthLength; z++) {
squares[y][z] = [];
for (let x = 0; x < widthLength; x++) {
// マス目となるメッシュ
const square = new THREE.Mesh(squareGeometry, fixedBlockMaterial);
// マス目の位置を調整する
square.position.set(x + 0.5, y + 0.5, z + 0.5);
// 初期状態は透明にする
square.visible = false;
// 3D描画するためシーンに追加する
scene.add(square);
squares[y][z][x] = square;
}
}
}
};

ブロックが存在するマス目は visible = true とするとマス目に色がつきます。

たとえば、次のような状態を作るとします。

3Dテトリスのステージ

右下に固定ブロックがあります。これは squares[0][0][2].visible = true とすることで表示できます。

また、上部には落下ブロックがあります。これを表示するにはまず、 squares[5][2][1].material = blockMaterial としてマテリアルを入れ替え、色を変えます。そして squares[5][2][1].visible = true とすることで落下ブロックの1マスを表示できます。

落下ブロックを表示させる方法

まず、落下ブロックのマス目の座標と、 squares のマス目の座標を別々に考えることにします。

変数 block には、落下ブロックの座標と位置を入れておきます。

たとえば、次の落下ブロックを作成するとします。

3Dテトリスの落下ブロック形1

このブロックのマス目の座標は次の3つです。


[
{ x:1, y:1, z:1 },
{ x:1, y:1, z:2 },
{ x:2, y:1, z:1 },
]

この配列を block.coordinates に入れます。

この落下ブロックが次の位置にあるとします。

3Dテトリスの落下ブロックの位置

この落下ブロックの位置は、 { x:0, y:4, z:0 } です。

これを block.position に入れておきます。

そして、落下ブロックのマス目の座標に位置を足すことで、表示すべき squares の座標がわかります。

この場合は、次のようになります。


[
{ x:1+0, y:1+4, z:1+0 },
{ x:1+0, y:1+4, z:2+0 },
{ x:2+0, y:1+4, z:1+0 },
]

つまり、 squares


[
{ x:1, y:5, z:1 },
{ x:1, y:5, z:2 },
{ x:2, y:5, z:1 },
]

マス目を visible = true にすることで落下ブロックを表示することができます。

なぜこんな回りくどい方法で表示させるかと思われるかもしれません。

理由はこのように座標と位置を別々に管理したほうが、落下ブロックを移動させたり回転させたりしやすくなるためです。

落下ブロックの座標を squares の座標に変換する関数を作っておきます。

index.html

const convertCoordinate = (coordinate, position) => {
return {
x: coordinate.x + position.x,
y: coordinate.y + position.y,
z: coordinate.z + position.z
};
};

落下ブロックを初期化する関数

index.html

const initBlock = () => {
block = {
// 落下ブロックのマス目の座標を入れる配列
coordinates: [],
// 落下ブロックの位置
position: { x: 0, y: heightLength, z: 0 }
};
// 落下するブロックをランダムに選ぶ
const randomIndex = Math.floor(Math.random() * blockShapeList.length);
// 新しい落下ブロックの形
const blockShape = blockShapeList[randomIndex];
// 落下ブロックの形を座標に変換して配列に入れていく
for (let y = 0; y < blockSideLength; y++) {
for (let z = 0; z < blockSideLength; z++) {
for (let x = 0; x < blockSideLength; x++) {
if (blockShape[y][z][x]) {
block.coordinates.push({ x, y, z });
}
}
}
};
// 落下ブロックを画面に表示させる
displayBlock(block);
};

落下ブロックを表示させる関数

index.html

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

引数で渡された落下ブロックを表示させます。

落下ブロックを非表示にする関数

index.html

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

引数で渡された落下ブロックを非表示にします。

落下ブロックを新しいものに入れ替える関数

index.html

const replaceBlock = (newBlock) => {
hideBlock(block);
displayBlock(newBlock);
block = newBlock;
};

引数で新しい落下ブロックを受け取ります。

現在の落下ブロックを非表示にして、新しい落下ブロックを表示させます。

こうすることで落下ブロックが動いているように見えます。

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

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

index.html

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

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

落下ブロックが表示されていたら成功です!

また、ブラウザを更新するたびに落下ブロックの形が2つ作成したものからランダムに選ばれます。

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