HTMLとJavaScriptを使ったテトリスゲームを作りたい方に向けて、今回は特に「Nextブロック表示機能」の実装方法を解説します。この機能は、ゲームプレイをより戦略的かつ楽しくするための重要な要素です。この記事では、テトリスゲームのバージョン1.4.6からバージョン1.5.15へのアップデート内容に焦点を当て、Nextブロックを表示するための具体的な手順やコードの変更点を初心者の方にもわかりやすく紹介します。ぜひ、この記事を参考にして、あなた自身のテトリスゲームにこの新機能を取り入れてみてください。
PCブラウザ版での操作方法はこれさ↓
Qキーは左回り、Wキーは右回りでブロックが回るよ。
左右の矢印キーでブロックを左右に動かせるよ!
下矢印キーはブロックが下に落ちます~。
最初に「START」ボタンを押してね~。
スマホ版はゲーム下のボタンで操作してください!
ver1.5.15での変更点 – Nextブロック表示機能の追加
バージョン1.5.15における重要な改良点として、「Nextブロック表示機能」が導入されました。この新機能は、ゲームプレイをより戦略的かつ楽しくするために設計されています。この記事では、HTMLとJavaScriptを使ったテトリスゲームで、このNextブロック表示をどのように実装するかを詳しく解説します。
まず、ver1.5.15
では、Nextブロックを表示するための新しいcanvas
要素がHTMLに追加されました。この要素は、メインのゲーム画面とは独立しており、次に落下するブロックを表示するための専用エリアとして機能します。また、プレイヤーオブジェクトに新たに追加されたnextPiece
変数は、次に登場するブロックを保持し、この新しい描画エリアに表示されます。
さらに、draw()
関数がアップデートされ、Nextブロックが新しいcanvas
に描画されるようになりました。この描画処理では、calculateCenterOffset()
関数を用いて、ブロックをcanvas
の中央に綺麗に配置します。
これらの改良により、プレイヤーは次に操作するブロックを事前に把握することができ、ゲームプレイの戦略性が大幅に向上しました。次のセクションでは、このNextブロック表示機能がどのように実装されているか、さらに詳しく見ていきます。
新しいcanvas要素の追加
バージョン1.5.15では、テトリスゲームにおいて「Nextブロック表示機能」を実現するために、新たにcanvas
要素がHTMLに追加されました。この新しいcanvas
要素は、nextPiece
というIDで定義されており、ゲームのメイン画面とは別の場所に配置されています。このcanvas
は、次にプレイヤーが操作するブロックを独立して描画するための専用領域となっており、ゲーム画面に表示されるメインブロックとは切り離された形で管理されます。
この変更により、プレイヤーは次に出現するブロックを事前に確認でき、より計画的なプレイが可能になります。新しいcanvas
要素は、ゲーム画面の右上に配置されており、視覚的にわかりやすく、ゲームプレイの戦略性を高めるために役立ちます。
具体的には、nextPieceContainer
というdiv
要素内に、nextPiece
というcanvas
とその下に配置されたNext
というラベルが含まれています。この構造により、次のブロックの表示エリアが明確になり、ユーザーにとって直感的なインターフェースが提供されます。
<div id="nextPieceContainer">
<canvas id="nextPiece" width="60" height="60"></canvas>
<div id="nextLabel">Next</div>
</div>
このcanvas
要素は、ゲームの操作性と戦略性を高めるために非常に重要な役割を果たしており、プレイヤーが次に何が来るかを予測して計画を立てるのに役立ちます。これにより、テトリスゲームはより魅力的で深みのある体験を提供することができます。
nextPiece変数の追加
バージョン1.5.15では、プレイヤーオブジェクトに新しい変数nextPiece
が追加されました。このnextPiece
変数は、次に落下するテトリミノ(ブロック)を保持するためのもので、Nextブロック表示機能の重要な要素となります。
この新しい変数は、次に画面に現れるテトリミノを事前に生成し、プレイヤーが次に何が来るかを予測できるようにします。これにより、プレイヤーは次の手を考えながら、より戦略的にゲームを進めることが可能になります。nextPiece
は、playerReset()
関数内で管理されており、現在のテトリミノが落下し終わった後に次のテトリミノとしてセットされます。
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
nextPiece: null, // 新しいnextPiece変数の追加
};
このコードによって、次のブロックがnextPiece
に保存され、プレイヤーが視覚的に確認できるようにnextPiece
用のcanvas
に描画されます。これにより、テトリスのプレイ体験が一層充実したものとなるでしょう。
描画関数の変更
今回のアップデートでは、ゲームの視覚的な体験を強化するためにdraw()
関数に大きな改良が施されています。特に注目すべきは、Nextブロックを表示するための描画処理が新たに追加された点です。この処理により、次に出現するテトリミノを、ゲームの進行中にプレイヤーが事前に確認できるようになっています。
draw()
関数では、まずメインのゲームキャンバス上に現在のブロックとゲームフィールドを描画しますが、それに加えて、Nextブロック専用のnextPiece
キャンバスにも描画を行います。この専用キャンバスは、ゲーム画面の右上に配置されており、プレイヤーが次に操作するブロックを視覚的に確認できるようになっています。
この描画処理には、calculateCenterOffset()
関数が活用されています。この関数は、Nextブロックをキャンバスの中央に配置するためのオフセット値を計算する役割を担っています。これにより、Nextブロックがキャンバス内で均等に配置され、視覚的にも美しく表示されます。
以下に示すのは、draw()
関数の一部で、Nextブロックを描画する処理が追加された部分です:
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width / scale, canvas.height / scale);
const xOffset = (canvas.width / scale - arena[0].length) / 2;
drawMatrix(arena, {x: xOffset, y: 0}, context);
drawMatrix(player.matrix, {x: player.pos.x + xOffset, y: player.pos.y}, context);
nextContext.fillStyle = '#000';
nextContext.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
const nextPieceOffset = calculateCenterOffset(player.nextPiece);
drawMatrix(player.nextPiece, nextPieceOffset, nextContext);
}
このコードの追加により、Nextブロックが次のフレームに表示され、プレイヤーは次の動きを予測しやすくなります。結果として、ゲームプレイの戦略性が向上し、より深みのあるゲーム体験が提供されることになります。
Nextブロック表示の仕組み
Nextブロック表示の仕組みは、テトリスゲームにおいてプレイヤーの戦略性を大きく向上させる重要な要素です。この機能により、プレイヤーは次に出現するブロックを事前に把握し、より計画的にゲームを進めることが可能になります。
まず、Nextブロックの生成はplayerReset()
関数で行われます。この関数は、テトリミノが落下し終わるたびに次のブロックをランダムに生成し、それをplayer.nextPiece
変数に格納します。これにより、次にどのブロックが登場するかが決定され、その情報が保持されます。
次に、このnextPiece
変数に格納されたブロックは、draw()
関数を通じて専用のキャンバス(nextPieceCanvas
)に描画されます。draw()
関数は、ゲームのメイン画面とは別にNextブロック専用の領域を描画するように設計されています。この際、calculateCenterOffset()
関数が用いられ、ブロックがキャンバスの中央に正確に配置されるように調整されています。これにより、表示されるブロックが視覚的にバランスの取れた位置に配置され、プレイヤーにとって直感的な表示が可能になります。
こうした一連のプロセスにより、Nextブロック表示は単なる視覚効果を超えて、プレイヤーが次の動きを予測し、戦略的にプレイするためのツールとして機能します。ゲームプレイのスムーズさと深みを増すためのこの機能は、テトリスの魅力をさらに引き立てる重要な改良点と言えるでしょう。
playerReset()関数でのNextブロック生成
playerReset()
関数は、テトリスゲームにおいて、次に操作するブロックを生成する重要な役割を担っています。この関数は、ゲーム開始時や現在のテトリミノが画面の下部に到達した際に呼び出され、次に落下するテトリミノをランダムに生成します。
具体的には、playerReset()
関数内で、まず次に表示するテトリミノをランダムに選択し、それをplayer.nextPiece
変数に格納します。これにより、次のブロックがどれになるのかが決定され、プレイヤーが次に操作するブロックとして表示されます。
例えば、次のコードはplayerReset()
関数の一部です:
function playerReset() {
if (player.nextPiece === null) {
player.nextPiece = createPiece('TJLOSZI'[Math.random() * 7 | 0]);
}
player.matrix = player.nextPiece;
player.nextPiece = createPiece('TJLOSZI'[Math.random() * 7 | 0]);
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
if (collide(arena, player)) {
gameOver = true;
displayFinalScore();
}
}
このコードでは、player.nextPiece
が次のブロックとして使用され、さらに新しいテトリミノが生成されて次のnextPiece
として格納されます。これにより、ゲームプレイが途切れることなく続行され、次のブロックが常に準備されている状態が保たれます。
この仕組みにより、プレイヤーは次にどのブロックが来るのかを視覚的に把握でき、次の動きを予測しながら戦略的にプレイできるようになります。これは、テトリスゲームの楽しさをさらに高める要素となっており、プレイヤーがゲームをより深く楽しむための重要な要素です。
draw()関数でのNextブロック描画
draw()
関数は、テトリスゲームにおいて、ゲームフィールドの描画を担当する非常に重要な役割を果たしています。この関数には、ver1.5.15で新たにNextブロックを描画する処理が追加されました。これにより、プレイヤーは次に出現するテトリミノを事前に確認できるようになり、戦略的なプレイが可能になります。
具体的には、draw()
関数の中でdrawMatrix()
関数が呼び出され、この関数を用いてNextブロックがnextPieceCanvas
に描画されます。drawMatrix()
関数は、テトリミノの形状をキャンバス上に正確に描画するためのもので、nextPiece
に格納されている次のブロックがこの専用キャンバスに表示されます。
以下は、draw()
関数の一部コードです:
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width / scale, canvas.height / scale);
const xOffset = (canvas.width / scale - arena[0].length) / 2;
drawMatrix(arena, {x: xOffset, y: 0}, context);
drawMatrix(player.matrix, {x: player.pos.x + xOffset, y: player.pos.y}, context);
nextContext.fillStyle = '#000';
nextContext.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
const nextPieceOffset = calculateCenterOffset(player.nextPiece);
drawMatrix(player.nextPiece, nextPieceOffset, nextContext);
}
このコードでは、calculateCenterOffset()
関数を用いてNextブロックをキャンバスの中央に配置し、プレイヤーが次の動きを考えるのに役立つように表示します。これにより、ゲームプレイはスムーズで戦略的なものとなり、プレイヤーにとって直感的なインターフェースが提供されます。
calculateCenterOffset()関数で中央配置
calculateCenterOffset()
関数は、Nextブロックを表示するキャンバス内でブロックを中央に配置するための重要な役割を果たしています。この関数は、Nextブロックのサイズを計算し、そのサイズに基づいてキャンバス内での適切なオフセットを算出します。この処理により、ブロックがキャンバスの中央に美しく配置され、視覚的にバランスの取れた表示が可能となります。
具体的には、calculateCenterOffset()
関数はブロックの幅と高さを取得し、キャンバスのサイズと比較することで、X軸とY軸の両方においてブロックを中央に配置するためのオフセット値を計算します。このオフセットは、drawMatrix()
関数内で適用され、ブロックが正確にキャンバスの中央に表示されるようになります。
以下は、この関数の一部コード例です:
function calculateCenterOffset(matrix) {
const matrixWidth = matrix[0].length;
const matrixHeight = matrix.length;
const canvasSize = nextCanvas.width / 10; // 10はscaleと一致させる
const offsetX = (canvasSize - matrixWidth) / 2;
const offsetY = (canvasSize - matrixHeight) / 2;
return {x: offsetX, y: offsetY};
}
このコードでは、Nextブロックがキャンバスの中央に配置されるよう、offsetX
とoffsetY
を計算しています。このオフセットが適用されることで、Nextブロックが常にキャンバスの中央に表示され、プレイヤーが次の動きを予測しやすくなります。
この中央配置の仕組みにより、Nextブロック表示は直感的かつ視覚的に美しいものとなり、ゲームプレイの戦略性と楽しさを向上させる重要な要素となっています。
コード解説 – 重要な部分の詳細
テトリスゲームのNextブロック表示機能を実装するにあたり、いくつかの重要なコードの部分があります。これらは、ゲームの機能を支える基盤となる要素であり、その理解は開発者にとって不可欠です。
まず、HTMLの構造として、Nextブロックを表示するキャンバスがnextPieceContainer
というdiv
要素内に配置されています。この要素には、Nextブロック専用のcanvas
(nextPiece
)と、そのブロックのラベルを表示するためのNext
というラベルが含まれています。これにより、ゲームのメイン画面とは独立して次のブロックが表示される仕組みが確立されています。
次に、JavaScriptでは、draw()
関数がNextブロックの描画を担当します。この関数は、ゲームフィールド上の現在の状態を描画するだけでなく、Nextブロック用のキャンバスにも描画を行います。特にdrawMatrix()
関数が重要で、これはブロックの形状をキャンバスに正確に描画するために使われます。
また、calculateCenterOffset()
関数が使用され、ブロックがキャンバスの中央に配置されるようにオフセット値を計算します。このようにして、Nextブロックは常にキャンバスの中央にバランスよく表示され、プレイヤーにとって視覚的にわかりやすいインターフェースが提供されます。
これらのコード部分は、テトリスゲームのプレイ体験を向上させるための重要な要素であり、Nextブロック表示機能の実装を理解するための鍵となります。
HTMLにおけるNextブロックcanvasの定義
テトリスゲームにおいて、Nextブロックを表示するためのHTML構造は、ゲームのユーザーインターフェースにおいて重要な役割を果たします。具体的には、Nextブロックを表示するキャンバス要素nextPiece
とそのラベル要素Next
が、nextPieceContainer
というdiv
要素内に配置されています。この配置により、ゲームのメイン画面とは独立したエリアに次のブロックが表示され、プレイヤーが次に何が来るのかを直感的に把握できるようになります。
nextPiece
キャンバスは、Nextブロックを表示するための専用領域であり、ゲームプレイ中に常に次のブロックを確認できるように設計されています。このキャンバスは、60px×60pxのサイズで定義されており、背景色には半透明の白が使われています。これにより、ゲーム背景とキャンバス内のブロックが明瞭に区別され、視覚的にわかりやすい表示が可能となっています。
また、nextLabel
要素は、Next
というラベルを表示するためのもので、Nextブロックの上に配置されています。このラベルにより、プレイヤーが次に表示されるブロックを瞬時に認識できるようになります。nextPieceContainer
というdiv
要素がこれらの要素を包み込み、全体のレイアウトが整えられています。この構造により、ゲーム画面においてNextブロックが適切に配置され、ゲームプレイをサポートする視覚的なガイドとして機能します。
このように、HTML内でのNextブロックキャンバスの定義は、プレイヤーにとってのゲーム体験を向上させるための重要な要素となっています。
JavaScriptにおけるNextブロックの描画処理
Nextブロックを表示するためのJavaScriptの処理は、ゲームの視覚的な情報提供において重要な役割を果たします。特にdraw()
関数内で、nextContext
を使ってnextPieceCanvas
にNextブロックを描画する処理が追加されています。この処理により、プレイヤーは次に落下するテトリミノを事前に視覚的に確認でき、戦略的なプレイが可能になります。
draw()
関数では、メインゲームキャンバスに現在のブロックやフィールドを描画するのと同時に、Nextブロック専用のキャンバスに次のブロックを描画します。この処理にはdrawMatrix()
関数が使用されており、テトリミノの形状をキャンバスに正確に描画します。
以下のコードはその一例です:
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width / scale, canvas.height / scale);
const xOffset = (canvas.width / scale - arena[0].length) / 2;
drawMatrix(arena, {x: xOffset, y: 0}, context);
drawMatrix(player.matrix, {x: player.pos.x + xOffset, y: player.pos.y}, context);
nextContext.fillStyle = '#000';
nextContext.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
const nextPieceOffset = calculateCenterOffset(player.nextPiece);
drawMatrix(player.nextPiece, nextPieceOffset, nextContext);
}
このコードでは、nextContext
を使ってNextブロックを描画する処理が行われています。まず、nextCanvas
をリセットし、その後にcalculateCenterOffset()
関数で得られたオフセットを使って、Nextブロックを中央に配置します。このようにして、Nextブロックが常にキャンバスの中央にバランスよく表示され、プレイヤーが次の動きを考える際の視覚的な参考になります。
この処理により、テトリスゲームはより戦略的で奥深いプレイ体験を提供し、プレイヤーの没入感を高めることができます。
Nextブロックの中央配置のロジック
テトリスゲームにおけるNextブロックの表示では、ブロックをキャンバスの中央に配置することが重要です。これにより、プレイヤーは次に来るブロックを視覚的に把握しやすくなり、ゲームプレイの戦略性が向上します。この中央配置を実現するためには、calculateCenterOffset()
関数が重要な役割を果たします。
calculateCenterOffset()
関数は、Nextブロックの幅と高さを基に、キャンバスの中央に配置するためのオフセットを計算します。具体的には、ブロックの幅と高さを取得し、キャンバスのサイズと比較することで、ブロックがキャンバスの中央に位置するようにX軸とY軸のオフセットを算出します。これにより、Nextブロックが常にキャンバスの中央に整然と配置され、視覚的に美しく、かつプレイヤーが次の動きを予測しやすくなります。
以下は、calculateCenterOffset()
関数の一例です:
function calculateCenterOffset(matrix) {
const matrixWidth = matrix[0].length;
const matrixHeight = matrix.length;
const offsetX = (nextCanvas.width / scale - matrixWidth) / 2;
const offsetY = (nextCanvas.height / scale - matrixHeight) / 2;
return {x: offsetX, y: offsetY};
}
このコードでは、Nextブロックの幅と高さを基に、キャンバスの中央に配置するためのオフセットを計算し、そのオフセットを用いてブロックをキャンバスに描画します。これにより、ブロックが視覚的に美しい形で表示され、プレイヤーのゲーム体験が向上します。
まとめ – Nextブロック表示でゲーム性を向上
Nextブロック表示機能は、テトリスゲームにおけるゲーム性を大きく向上させる重要な要素です。この機能により、プレイヤーは次に落下するテトリミノを事前に把握できるため、より戦略的なプレイが可能になります。Nextブロックを見ながら、次のブロックの配置を考えることで、プレイヤーは効率的にラインを消し、高得点を狙うことができるようになります。
この機能は、テトリスのようなパズルゲームにおいて、単なる運に頼るのではなく、プレイヤーの計画性や判断力がより強く求められるようになる点で、ゲームの深みを増す要素となります。Nextブロックの表示があることで、プレイヤーは次に来るブロックの形状を把握し、どの位置に置くべきかをあらかじめ考えることができるため、プレイのテンポが速くなり、より高度な戦略を展開することが可能になります。
また、Nextブロック表示は、ゲームの標準的な機能であり、プレイヤーにとって視覚的に理解しやすいインターフェースを提供します。この機能を適切に実装することで、テトリスの楽しさがさらに増し、プレイヤーのリピート率も高まるでしょう。
今後も、この機能を基にさらなる改良や新機能の追加が期待されます。テトリスゲームの進化は止まることなく、プレイヤーにとって常に新しい挑戦を提供し続けることが求められます。今回のNextブロック表示機能の実装は、その第一歩として非常に重要な意味を持ちます。
プレイヤーの戦略性向上
Nextブロック表示機能は、テトリスゲームにおいてプレイヤーの戦略性を大きく向上させる要素です。この機能により、プレイヤーは次に落下するテトリミノを事前に把握できるため、より計画的なブロック配置が可能となります。テトリスは、素早い判断力と計画性が求められるゲームですが、Nextブロック表示があることで、プレイヤーは次にどのブロックが来るのかを予測しながら現在のブロックを配置できるようになります。
例えば、現在のブロックを配置する際に、次に来るブロックの形状や大きさを考慮し、次のブロックを最適な場所に配置するための準備をすることができます。これにより、複数のラインを同時に消すための戦略を立てやすくなり、ゲームのスコアを効率よく上げることが可能となります。
また、この機能はプレイヤーのゲームプレイをより予測可能でコントロールしやすいものにし、テトリスの魅力を一層引き立てます。Nextブロックが表示されることで、プレイヤーは次にどう動くべきかを計画し、その計画を元に迅速かつ正確にブロックを配置することができます。これにより、テトリスのゲームプレイは単なる反射的な操作ではなく、より高度な戦略を駆使したプレイが求められるようになります。
結果として、Nextブロック表示はプレイヤーにさらなる挑戦と満足感を提供し、ゲームの深みを増す重要な機能となっています。この機能を活用することで、プレイヤーはより一貫性のある高スコアを目指し、テトリスの醍醐味を存分に味わうことができるでしょう。
ゲームの面白さ向上
Nextブロック表示機能は、テトリスゲームの面白さを大きく向上させる重要な要素です。この機能は、プレイヤーにとって次に落下するブロックを事前に視覚的に確認できるため、ゲームプレイに深みをもたらします。テトリスの魅力は、プレイヤーが次々と出現するブロックを素早く判断し、最適な位置に配置することでラインを消していく戦略性にありますが、Nextブロックの表示はその戦略性をさらに高める役割を果たします。
この機能により、プレイヤーは次にどのブロックが来るのかを予測しながら、現在のブロックを配置する場所を慎重に選ぶことができます。例えば、複数のラインを同時に消すための最適なブロック配置を計画し、それに基づいて現在のブロックを動かすことで、より高いスコアを狙うことが可能となります。このように、Nextブロック表示は、単なる反射的な操作から、計画的かつ戦略的なプレイへとゲーム体験を進化させる要素です。
また、この機能は、プレイヤーが自分のスキルを試し、さらなる挑戦を追求するモチベーションを高めることにもつながります。次のブロックを考慮しながらプレイすることで、ゲームのテンポは速くなり、難易度も適切に上がるため、プレイヤーにとって常に新鮮で刺激的なゲーム体験が提供されます。
Nextブロック表示は、テトリスを楽しむ上で欠かせない機能であり、その存在がゲームの面白さを一層引き立てています。この機能を活用することで、プレイヤーはより高度なプレイが可能となり、テトリスの本来の魅力を最大限に引き出すことができるでしょう。
今後のアップデートへの期待
今回のテトリスバージョン1.5.15でのNextブロック表示機能の追加は、ゲーム体験を大幅に向上させました。しかし、これはあくまでスタートに過ぎません。今後もこのゲームのさらなる進化が期待されます。次に来るアップデートでは、さらに新しい機能や改良が追加される可能性があります。
たとえば、Nextブロック表示に加えて、プレイヤーがカスタマイズできる設定が増えたり、より複雑なブロックパターンが導入されたりすることが考えられます。これにより、プレイヤーは自分に合った難易度やゲームスタイルを選ぶことができ、ゲームのリプレイ性がさらに高まるでしょう。
また、グラフィックやサウンドの強化も期待されます。視覚的に魅力的で、音響的に没入感のあるゲーム環境を提供することで、テトリスの魅力をさらに引き出すことが可能です。新しいエフェクトやアニメーションが加わることで、プレイヤーは一層楽しみながらゲームをプレイできるようになるでしょう。
さらには、オンラインマルチプレイ機能の改善や、他のプレイヤーとスコアを競い合うランキングシステムの導入なども考えられます。これにより、プレイヤー同士が交流し、競い合うことで、より長く楽しむことができるでしょう。
このように、次のアップデートには多くの期待が寄せられています。プレイヤーのフィードバックを基に、ゲーム開発者がどのような新しい要素を取り入れてくるのか、今後の展開が非常に楽しみです。テトリスは常に進化し続けるゲームであり、次のステップを踏み出すことで、さらに多くのファンを魅了し続けることでしょう。
まとめ
この記事では、HTMLとJavaScriptを用いたテトリスゲームにおいて、ver1.4.6からver1.5.15へのコード変更点を基に、Nextブロック表示機能の実装方法について解説しました。この機能の追加により、プレイヤーは次に落下するブロックを事前に把握でき、より戦略的なプレイが可能になります。また、ゲーム全体の面白さが向上し、プレイヤーに新たな挑戦と満足感を提供することができます。
Nextブロック表示は、テトリスの標準的な機能として、ゲームの深みを増す重要な要素です。プレイヤーが次のブロックを考慮しながら現在のブロックを配置することで、複数のラインを同時に消す戦略を立てやすくなり、ゲームのスコアを効率的に上げることが可能となります。さらに、今回のアップデートを基に、今後の機能追加や改良が期待されるため、テトリスゲームは今後も進化し続け、プレイヤーに新たな体験を提供し続けることでしょう。
このように、Nextブロック表示機能の実装は、ゲーム性を向上させるための重要なステップであり、テトリスゲームをより楽しむための鍵となります。この記事が、HTMLテトリス開発の参考になれば幸いです。今後のアップデートにも注目し、さらに多くのプレイヤーがテトリスを楽しむことができるよう、引き続き進化していくことを期待しています。
おまたせしました!!
じゃあテトリスのコードを全部みせますよ!
ごらんあれ~♬
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>テトリスゲーム Ver1.5.15</title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
background-image: url('https://tokodomo.xyz/wp-content/uploads/2024/08/tetris_ver1.5_background2.webp');
background-size: cover;
background-position: center;
}
#gameContainer {
position: relative;
width: 100%;
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.7);
z-index: 5;
}
#game {
background-color: #000;
}
#scoreContainer {
font-size: 20px;
background-color: rgba(255, 255, 255, 0.7);
padding: 10px;
position: fixed;
top: 0;
width: 100%;
display: flex;
justify-content: space-between;
box-sizing: border-box;
z-index: 10;
}
#nextPieceContainer {
position: absolute;
top: 100px;
right: 80px;
text-align: center;
}
#nextPiece {
width: 60px;
height: 60px;
background-color: rgba(255, 255, 255, 0.7);
margin-bottom: -5px; /* 枠とラベルの間隔を小さくする */
}
#nextLabel {
font-size: 14px;
color: white;
letter-spacing: 2px;
text-transform: uppercase;
font-weight: bold;
}
#finalScore {
font-size: 30px;
color: red;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.7);
padding: 10px;
display: none;
width: 100%;
text-align: center;
}
#controls {
position: fixed;
bottom: 0;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
box-sizing: border-box;
z-index: 10;
background-color: rgba(0, 0, 0, 0.5);
}
.control-button {
width: 60px;
height: 60px;
background-color: rgba(255, 255, 255, 0.7);
border: none;
border-radius: 50%;
font-size: 20px;
font-weight: bold;
color: black;
display: flex;
justify-content: center;
align-items: center;
}
#moveControl {
width: 140px;
display: flex;
justify-content: space-between;
}
.connected-button {
border-radius: 0;
flex-grow: 1;
margin: 0;
}
.connected-button:first-child {
border-top-left-radius: 50%;
border-bottom-left-radius: 50%;
}
.connected-button:last-child {
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
}
@media (min-width: 601px) {
#controls {
display: none;
}
}
</style>
</head>
<body>
<div id="scoreContainer">
<div id="score">Score: 0</div>
<button id="startButton" onclick="startGame()">START</button>
</div>
<div id="gameContainer">
<canvas id="game" width="240" height="400"></canvas>
<div id="finalScore">GAME OVER<br>SCORE: 0</div>
<div id="nextPieceContainer">
<canvas id="nextPiece" width="60" height="60"></canvas>
<div id="nextLabel">Next</div>
</div>
</div>
<div id="controls">
<div id="moveControl">
<button class="control-button connected-button" id="leftButton">←</button>
<button class="control-button connected-button" id="rightButton">→</button>
</div>
<button class="control-button" id="rotateButton">↻</button>
<button class="control-button" id="dropButton">⬇</button>
</div>
<script>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
const nextCanvas = document.getElementById('nextPiece');
const nextContext = nextCanvas.getContext('2d');
const scale = 20;
context.scale(scale, scale);
nextContext.scale(10, 10);
const arena = createMatrix(12, 20);
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
nextPiece: null,
};
const colors = [
null,
'#FF0D72',
'#0DC2FF',
'#0DFF72',
'#F538FF',
'#FF8E0D',
'#FFE138',
'#3877FF',
];
let gameOver = false;
let dropStart = false;
let dropSpeed = 50;
function createMatrix(w, h) {
const matrix = [];
while (h--) {
matrix.push(new Array(w).fill(0));
}
return matrix;
}
function createPiece(type) {
if (type === 'T') {
return [
[0, 0, 0],
[1, 1, 1],
[0, 1, 0],
];
} else if (type === 'O') {
return [
[2, 2],
[2, 2],
];
} else if (type === 'L') {
return [
[0, 3, 0],
[0, 3, 0],
[0, 3, 3],
];
} else if (type === 'J') {
return [
[0, 4, 0],
[0, 4, 0],
[4, 4, 0],
];
} else if (type === 'I') {
return [
[0, 5, 0, 0],
[0, 5, 0, 0],
[0, 5, 0, 0],
[0, 5, 0, 0],
];
} else if (type === 'S') {
return [
[0, 6, 6],
[6, 6, 0],
[0, 0, 0],
];
} else if (type === 'Z') {
return [
[7, 7, 0],
[0, 7, 7],
[0, 0, 0],
];
}
}
function drawMatrix(matrix, offset, context) {
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
context.fillStyle = colors[value];
context.fillRect(x + offset.x, y + offset.y, 1, 1);
}
});
});
}
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width / scale, canvas.height / scale);
const xOffset = (canvas.width / scale - arena[0].length) / 2;
drawMatrix(arena, {x: xOffset, y: 0}, context);
drawMatrix(player.matrix, {x: player.pos.x + xOffset, y: player.pos.y}, context);
nextContext.fillStyle = '#000';
nextContext.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
// 中央にブロックを配置するためにオフセットを調整
const nextPieceOffset = calculateCenterOffset(player.nextPiece);
drawMatrix(player.nextPiece, nextPieceOffset, nextContext);
}
function calculateCenterOffset(matrix) {
const matrixWidth = matrix[0].length;
const matrixHeight = matrix.length;
const canvasSize = nextCanvas.width / 10; // 10はscaleと一致させる
const offsetX = (canvasSize - matrixWidth) / 2;
const offsetY = (canvasSize - matrixHeight) / 2;
return {x: offsetX, y: offsetY};
}
function merge(arena, player) {
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
arena[y + player.pos.y][x + player.pos.x] = value;
}
});
});
}
function rotate(matrix, dir) {
for (let y = 0; y < matrix.length; ++y) {
for (let x = 0; x < y; ++x) {
[matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
}
}
if (dir > 0) {
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
function playerDrop() {
player.pos.y++;
if (collide(arena, player)) {
player.pos.y--;
merge(arena, player);
playerReset();
arenaSweep();
updateScore();
if (collide(arena, player)) {
gameOver = true;
displayFinalScore();
}
}
dropCounter = 0;
}
function playerMove(offset) {
player.pos.x += offset;
if (collide(arena, player)) {
player.pos.x -= offset;
}
}
function playerReset() {
if (player.nextPiece === null) {
player.nextPiece = createPiece('TJLOSZI'[Math.random() * 7 | 0]);
}
player.matrix = player.nextPiece;
player.nextPiece = createPiece('TJLOSZI'[Math.random() * 7 | 0]);
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
if (collide(arena, player)) {
gameOver = true;
displayFinalScore();
}
}
function playerRotate(dir) {
const pos = player.pos.x;
let offset = 1;
rotate(player.matrix, dir);
while (collide(arena, player)) {
player.pos.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if (offset > player.matrix[0].length) {
rotate(player.matrix, -dir);
player.pos.x = pos;
return;
}
}
}
function collide(arena, player) {
const [m, o] = [player.matrix, player.pos];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 0 &&
(arena[y + o.y] &&
arena[y + o.y][x + o.x]) !== 0) {
return true;
}
}
}
return false;
}
function arenaSweep() {
outer: for (let y = arena.length - 1; y > 0; --y) {
for (let x = 0; x < arena[y].length; ++x) {
if (arena[y][x] === 0) {
continue outer;
}
}
const row = arena.splice(y, 1)[0].fill(0);
arena.unshift(row);
++y;
player.score += 10;
}
}
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
function update(time = 0) {
if (!gameOver) {
const deltaTime = time - lastTime;
dropCounter += deltaTime;
if (dropCounter > (dropStart ? dropSpeed : dropInterval)) {
playerDrop();
}
lastTime = time;
draw();
requestAnimationFrame(update);
}
}
function updateScore() {
document.getElementById('score').innerText = `Score: ${player.score}`;
document.getElementById('finalScore').innerText = `GAME OVER\nSCORE: ${player.score}`;
}
document.addEventListener('keydown', event => {
if (event.keyCode === 37) {
playerMove(-1);
} else if (event.keyCode === 39) {
playerMove(1);
} else if (event.keyCode === 40) {
event.preventDefault();
playerDrop();
} else if (event.keyCode === 81) {
playerRotate(-1);
} else if (event.keyCode === 87) {
playerRotate(1);
}
});
document.getElementById('leftButton').addEventListener('click', () => playerMove(-1));
document.getElementById('rightButton').addEventListener('click', () => playerMove(1));
document.getElementById('rotateButton').addEventListener('click', () => playerRotate(1));
document.getElementById('dropButton').addEventListener('mousedown', (event) => {
event.preventDefault();
dropStart = true;
});
document.getElementById('dropButton').addEventListener('mouseup', (event) => {
event.preventDefault();
dropStart = false;
});
document.getElementById('dropButton').addEventListener('touchstart', (event) => {
event.preventDefault();
dropStart = true;
});
document.getElementById('dropButton').addEventListener('touchend', (event) => {
event.preventDefault();
dropStart = false;
});
function startGame() {
arena.forEach(row => row.fill(0));
player.score = 0;
gameOver = false;
document.getElementById('finalScore').style.display = "none";
playerReset();
updateScore();
update();
}
function displayFinalScore() {
document.getElementById('finalScore').style.display = "block";
document.getElementById('finalScore').innerText = `GAME OVER\nSCORE: ${player.score}`;
}
playerReset();
updateScore();
</script>
</body>
</html>
コメント