テトリスは誰もが一度は遊んだことがある名作ゲームです。この記事では、HTMLとJavaScriptを使ってテトリスをゼロから作成する方法を詳しく解説します。プログラミング初心者の方でも安心して取り組めるよう、ステップバイステップで説明しますので、ぜひチャレンジしてみてください。
操作方法(パソコンブラウザ専用です!)
このゲームの操作方法はとても簡単です、
- 左矢印キー: ブロックを左に移動
- 右矢印キー: ブロックを右に移動
- 下矢印キー: ブロックを下に落とす
- Qキー: ブロックを左に回転
- Wキー: ブロックを右に回転
この操作で、誰でもすぐに楽しむことができます。操作性をシンプルに保つことで、気軽に遊べるようにしています。
はじめに
テトリスを作成する前に、必要な基本知識と準備事項について説明します。このセクションでは、テトリスを作成する理由や目的、必要な開発環境とツールの準備、そして基本的な技術知識の確認を行います。これにより、テトリスを効果的に作成するための基盤を築きましょう。
テトリスを作る理由と目的
テトリスは1984年に生まれたパズルゲームで、シンプルながら奥深いゲーム性が多くの人々を魅了してきました。このテトリスを自分の手で作り上げることで、ゲーム開発の基本を学ぶことができます。具体的には、JavaScriptを使ったロジック構築、キャンバス要素を利用した描画、ユーザー入力の処理など、Web開発における重要な技術を実践的に学ぶことができます。また、テトリスのようなゲームを作成することで、アルゴリズムやデータ構造の理解が深まり、プログラミングスキルの向上にもつながります。
必要な環境とツールの準備
テトリスを作成するためには、まず開発環境を整える必要があります。基本的には、テキストエディタとウェブブラウザがあれば十分ですが、より効率的に開発を進めるために、以下のツールをインストールすることをお勧めします。
- Visual Studio Code: 無料で使える高機能なテキストエディタです。拡張機能を追加することで、コード補完やデバッグが簡単に行えます。
- Google Chrome: 開発者ツールが充実しており、JavaScriptのデバッグやパフォーマンスの確認が容易です。
- Node.js: JavaScriptの実行環境で、各種ライブラリの管理やローカルサーバの立ち上げに便利です。
これらのツールをインストールし、開発に必要な準備を整えましょう。
基本知識の確認
テトリスを作成する前に、HTML、JavaScript、キャンバス要素の基本を復習しておきましょう。まず、HTMLはWebページの骨格を作るマークアップ言語です。JavaScriptはそのHTMLに動的な動作を付与するスクリプト言語で、特にゲームロジックの実装に用います。キャンバス要素は、ブラウザ上で図形を描画するためのHTML5の新しい要素で、テトリスのブロックを表示するのに使います。
さらに、JavaScriptにはvar、let、constといった変数の宣言方法があり、それぞれの違いを理解しておくことが重要です。特に、letとconstはブロックスコープを持ち、コードの予測可能性を高めるために推奨されます。また、キャンバスに描画するためには、2Dコンテキストを取得し、そのプロパティやメソッドを使用して図形を描画する方法を理解する必要があります。
これらの基本知識をしっかりと確認してから、テトリスの作成に取り掛かりましょう。
キャンバス要素の準備
ゲームの描画に使用するキャンバス要素を用意し、その基本的な使い方を学びます。キャンバス要素は、HTML5の強力な機能の一つで、グラフィックスやアニメーションを描画するための領域を提供します。これを使用することで、テトリスのようなゲームの画面を効果的に作成することができます。このセクションでは、キャンバス要素の基本的な設定方法から2Dコンテキストの取得、そして簡単な図形の描画までをステップバイステップで解説します。
キャンバス要素の設定方法
まずは、HTMLにキャンバス要素を追加する方法を見てみましょう。キャンバス要素は、HTMLの<canvas>
タグを使って定義されます。このタグに幅(width)と高さ(height)の属性を設定することで、描画領域のサイズを指定します。例えば、320×640ピクセルのキャンバスを作成するには、以下のように記述します。
<canvas id="gameCanvas" width="320" height="640"></canvas>
次に、キャンバスのスタイルを設定します。CSSを使ってキャンバスの背景色や表示位置を調整することで、見栄えを良くすることができます。
#gameCanvas {
background-color: #000;
display: block;
margin: auto;
}
このようにして、基本的なキャンバス要素の設定を行います。
2Dコンテキストの取得と設定
キャンバス要素を設定した後は、JavaScriptを使って描画の準備をします。まず、キャンバス要素を取得し、その2Dコンテキストを取得します。2Dコンテキストは、キャンバスに描画するためのメソッドやプロパティを提供するオブジェクトです。
const canvas = document.getElementById('gameCanvas');
const context = canvas.getContext('2d');
このcontext
オブジェクトを使って、キャンバスに描画を行います。初期設定として、描画のスケールや色を設定することができます。
context.scale(20, 20);
context.fillStyle = '#FFF';
キャンバスへの簡単な描画
最後に、キャンバスに簡単な図形を描画してみましょう。例えば、四角形を描画するには、fillRect
メソッドを使用します。
context.fillRect(1, 1, 1, 1);
このコードは、キャンバスの位置(1,1)から幅1、高さ1の白い四角形を描画します。これでキャンバスが正しく設定され、基本的な描画ができることを確認できます。
以上の手順で、キャンバス要素を設定し、2Dコンテキストを取得して簡単な描画を行う方法を学びました。次のステップでは、さらに複雑な図形やアニメーションを描画して、テトリスのゲーム画面を作成していきます。
テトリスの基本構造
テトリスゲームの基本構造を理解し、各パーツの役割を学びます。テトリスはシンプルなパズルゲームですが、その背後には多くの技術的な要素が含まれています。特に重要なのは、ゲーム盤を表す二次元配列、テトリミノと呼ばれるブロックの生成と管理、そしてブロックの移動や回転時の衝突判定です。これらの要素を正しく理解することで、テトリスをスムーズに作成し、ゲームとしての完成度を高めることができます。
二次元配列の初期化
テトリスのゲーム盤は、二次元配列を用いて表現されます。二次元配列とは、行と列で構成されるデータ構造で、各セルにはブロックの有無を示す値が格納されます。ゲーム開始時には、すべてのセルが空(0)で初期化されます。JavaScriptでは、以下のようにして二次元配列を初期化できます。
function createMatrix(width, height) {
const matrix = [];
while (height--) {
matrix.push(new Array(width).fill(0));
}
return matrix;
}
この関数は、指定された幅と高さを持つ二次元配列を作成し、すべてのセルを0で埋めます。これがゲーム盤の基本構造となります。
テトリミノの生成と管理
テトリミノは、テトリスにおける落下するブロックのことを指します。各テトリミノは異なる形状を持ち、それぞれ異なる配列で表現されます。例えば、T字型のテトリミノは以下のように表現されます。
const T_SHAPE = [
[0, 1, 0],
[1, 1, 1],
[0, 0, 0],
];
テトリミノをランダムに生成するためには、いくつかの形状を用意し、それをランダムに選択する仕組みが必要です。以下のようにして、テトリミノを生成する関数を実装します。
function createPiece(type) {
switch (type) {
case 'T':
return [
[0, 1, 0],
[1, 1, 1],
[0, 0, 0],
];
// 他の形状も同様に定義
}
}
この関数は、指定された形状タイプに応じて対応する配列を返します。
衝突判定の実装
テトリミノが移動や回転する際に、他のブロックやゲーム盤の端に衝突しないようにするための判定が必要です。衝突判定は、テトリミノの各セルがゲーム盤の既存のブロックや境界に重ならないことを確認することで行います。以下の関数は、衝突を判定します。
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;
}
この関数は、プレイヤーのテトリミノが現在の位置で衝突するかどうかを判定し、衝突する場合はtrue
を返します。この判定を用いて、テトリミノの移動や回転を適切に制限します。
以上の基本構造を理解することで、テトリスのゲームロジックを効果的に実装できます。次に進むべきステップは、これらの要素を組み合わせて、実際のゲームプレイを構築することです。
ブロックの操作と描画
プレイヤーがブロックを操作するための方法と、ブロックを描画するための技術を学びます。テトリスのようなゲームでは、プレイヤーの操作によってブロックを自由に動かし、適切に配置することが求められます。このセクションでは、キーボード操作の実装方法、ブロックの回転や移動のロジック、そして描画の最適化技術について詳しく解説します。これらの技術を習得することで、滑らかで直感的なゲームプレイを実現できます。
キーボード操作の実装
テトリスでは、プレイヤーがキーボードを使ってブロックを操作します。JavaScriptでは、keydown
イベントを使ってキー入力を検出し、それに応じてブロックの動作を制御します。以下のコードは、矢印キーと回転キーの操作を実装する例です。
document.addEventListener('keydown', event => {
if (event.keyCode === 37) {
playerMove(-1); // 左矢印キーで左に移動
} else if (event.keyCode === 39) {
playerMove(1); // 右矢印キーで右に移動
} else if (event.keyCode === 40) {
playerDrop(); // 下矢印キーで下に落とす
} else if (event.keyCode === 81) {
playerRotate(-1); // Qキーで左に回転
} else if (event.keyCode === 87) {
playerRotate(1); // Wキーで右に回転
}
});
このコードにより、プレイヤーのキー操作に応じてブロックが動くようになります。各操作は専用の関数(playerMove
、playerDrop
、playerRotate
)で処理され、ゲームのロジックに従ってブロックを適切に移動させます。
ブロックの回転と移動
ブロックを回転させたり移動させたりするためのロジックは、テトリスのゲームプレイにおいて重要な役割を果たします。以下は、ブロックを回転させる関数の例です。
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;
}
}
}
この関数は、ブロックの回転時に衝突が発生しないようにするためのロジックを含んでいます。また、ブロックの移動に関しては、プレイヤーの入力に応じてブロックの位置を更新し、衝突判定を行うことで適切に動作します。
描画の最適化
ゲームの描画を効率的に行うためのテクニックを理解することも重要です。JavaScriptのrequestAnimationFrame
メソッドを使用することで、スムーズなアニメーションを実現できます。
function update(time = 0) {
const deltaTime = time - lastTime;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
playerDrop();
}
lastTime = time;
draw();
requestAnimationFrame(update);
}
この関数は、ゲームの描画と更新を効率的に行うためのループを提供します。requestAnimationFrame
を使うことで、ブラウザのリフレッシュレートに合わせたスムーズな描画が可能になります。
以上の技術を駆使して、プレイヤーの操作に応じたブロックの動作と描画を実現し、快適なゲームプレイ体験を提供しましょう。
ゲームの進行管理
ゲームの進行を管理するための技術を学び、ゲームがスムーズに動作するようにします。テトリスのようなリアルタイムのゲームでは、ゲームループやレベルアップ、スコアリング、そしてゲームオーバーの処理といった要素が重要です。これらの要素を適切に実装することで、プレイヤーにとって一貫性のある魅力的なゲーム体験を提供することができます。
ゲームループの実装
ゲームループは、ゲームの進行を制御するための中心的な仕組みです。ゲームループは、一定の間隔でゲームの状態を更新し、画面を再描画するサイクルを繰り返します。JavaScriptでは、requestAnimationFrame
関数を使って滑らかなゲームループを実現できます。
let lastTime = 0;
function update(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
// ゲームの状態を更新
updateGameState(deltaTime);
// ゲームの描画
drawGame();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
このコードは、requestAnimationFrame
を使って次のフレームの更新をスケジュールします。updateGameState
関数ではゲームの状態を更新し、drawGame
関数では現在のゲームの状態を画面に描画します。
レベルアップとスコアリング
テトリスでは、ゲームが進むにつれてレベルが上がり、ブロックの落下速度が速くなります。また、プレイヤーのスコアは、ブロックを消去することで増加します。これらの機能を実装することで、ゲームに挑戦的な要素を追加します。
let score = 0;
let linesCleared = 0;
let level = 0;
const linesPerLevel = 10;
function updateScore(lines) {
const points = [0, 40, 100, 300, 1200];
score += points[lines];
linesCleared += lines;
if (linesCleared >= linesPerLevel) {
level++;
linesCleared -= linesPerLevel;
increaseDifficulty();
}
updateScoreDisplay();
}
function increaseDifficulty() {
// ブロックの落下速度を速くする処理
}
このコードは、消去された行数に応じてスコアを更新し、一定の行数を消去するとレベルが上がる仕組みを実装しています。
ゲームオーバーの処理
ゲームオーバーの処理は、ゲームが終了したときの状態を管理し、リスタートの機能を提供します。ゲームオーバーの判定は、テトリミノがゲーム盤の上端に到達したときに行います。
function checkGameOver() {
for (let x = 0; x < arena[0].length; x++) {
if (arena[0][x] !== 0) {
return true;
}
}
return false;
}
function handleGameOver() {
if (checkGameOver()) {
// ゲームオーバー処理
displayGameOverScreen();
resetGame();
}
}
function resetGame() {
arena.forEach(row => row.fill(0));
player.reset();
score = 0;
level = 0;
linesCleared = 0;
updateScoreDisplay();
}
このコードは、ゲームオーバーの判定と、ゲームオーバー時に実行される処理、そしてリスタート機能を実装しています。
これらの要素を組み合わせることで、スムーズかつ一貫性のあるゲーム進行を実現し、プレイヤーに楽しさと挑戦を提供することができます。
さらなる改善と応用
基本的なテトリスが完成した後、さらに楽しめるようにするための改善点と応用例を紹介します。ゲーム開発は、一度完成したら終わりではなく、プレイヤーの体験を向上させるための継続的な改良が重要です。このセクションでは、スコアボードの追加、サウンドエフェクトの導入、そして難易度設定とカスタマイズの方法について詳しく説明します。これらの改善を加えることで、ゲームの魅力をさらに高めることができます。
スコアボードの追加
スコアボードを追加し、プレイヤーのスコアを表示することで、競争心を煽り、ゲームの楽しさを増すことができます。まず、HTMLにスコアを表示する要素を追加します。
<div id="scoreBoard">Score: 0</div>
次に、JavaScriptでスコアを管理し、ゲームの進行に応じてスコアを更新します。
let score = 0;
function updateScore(points) {
score += points;
document.getElementById('scoreBoard').innerText = `Score: ${score}`;
}
// 例: ブロックが消えたときにスコアを更新
function clearLines() {
let linesCleared = 0;
for (let y = arena.length - 1; y > 0; --y) {
if (arena[y].every(cell => cell !== 0)) {
arena.splice(y, 1);
arena.unshift(new Array(arena[0].length).fill(0));
linesCleared++;
}
}
updateScore(linesCleared * 100); // ラインを消した数に応じてスコアを加算
}
このようにして、スコアボードをゲームに統合することで、プレイヤーの達成感を高めることができます。
サウンドエフェクトの導入
サウンドエフェクトを追加することで、ゲームの臨場感を向上させることができます。JavaScriptのAudioオブジェクトを使って、ブロックが落下したり、ラインが消えたりする際のサウンドを再生します。
const dropSound = new Audio('drop.mp3');
const clearSound = new Audio('clear.mp3');
function playDropSound() {
dropSound.play();
}
function playClearSound() {
clearSound.play();
}
// 例: ブロックが落下したときに音を再生
function playerDrop() {
player.pos.y++;
if (collide(arena, player)) {
player.pos.y--;
merge(arena, player);
playerReset();
playDropSound();
clearLines();
updateScore();
}
}
このようにして、ゲームにサウンドエフェクトを追加することで、プレイヤーの体験をより豊かにすることができます。
難易度設定とカスタマイズ
難易度設定を行い、ゲームをカスタマイズすることで、初心者から上級者まで楽しめるようにします。ゲームの進行に応じて難易度を調整し、プレイヤーが飽きないように工夫します。
let dropInterval = 1000;
let level = 0;
function increaseDifficulty() {
level++;
dropInterval = Math.max(100, dropInterval - 100);
}
function updateGame(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
playerDrop();
dropCounter = 0;
}
drawGame();
requestAnimationFrame(updateGame);
}
// レベルアップ時に難易度を調整
function clearLines() {
let linesCleared = 0;
for (let y = arena.length - 1; y > 0; --y) {
if (arena[y].every(cell => cell !== 0)) {
arena.splice(y, 1);
arena.unshift(new Array(arena[0].length).fill(0));
linesCleared++;
}
}
if (linesCleared > 0) {
increaseDifficulty();
}
updateScore(linesCleared * 100);
}
これにより、ゲームの進行に応じて難易度が上がる仕組みを実装できます。難易度設定を工夫することで、プレイヤーのスキルレベルに合わせた挑戦を提供し、ゲームのリプレイ性を高めることができます。
まとめ
この記事の内容を総括し、今後の学習やプロジェクトに役立てるためのポイントをまとめます。テトリスのようなゲームを自分で作成することで、JavaScriptやHTML5のキャンバス要素を使ったプログラミングの基本から応用までを実践的に学ぶことができます。この経験を通じて、ゲーム開発の基礎を理解し、さらなるプロジェクトへの自信を深めましょう。
まず、テトリスの制作を通して学んだ二次元配列の初期化やブロックの生成、衝突判定といった基本的な技術は、他の多くのゲームやアプリケーション開発にも応用可能です。これらの技術は、複雑なデータ構造の操作やリアルタイムなユーザーインターフェースの実装に役立ちます。
次に、キーボード操作の実装や描画の最適化といった操作性の向上技術を学ぶことで、ユーザーエクスペリエンスを意識した開発が可能になります。特に、requestAnimationFrame
を使ったスムーズなアニメーションは、ゲームに限らず、動的なWebアプリケーション全般で有用です。
さらに、スコアボードの追加やサウンドエフェクトの導入、難易度設定といったゲームの進行管理技術は、プロジェクトの魅力を大幅に向上させます。これらの機能を実装することで、ユーザーにとって魅力的で挑戦的な体験を提供できるようになります。
今回の記事で紹介した内容をもとに、ぜひ自分だけのオリジナルゲームを作成し、楽しんでください。また、さらなる学習やプロジェクトに挑戦する際には、今回の経験を活かして、より高度な技術や複雑な機能を取り入れることを目指しましょう。継続的な学習と実践を通じて、プログラミングスキルを磨き、素晴らしいプロジェクトを作り上げていってください。
HTMLファイル(tetris_ver1.0.html)をWordPressのメディアライブラリにアップロードし、以下のコードを使ってiframeで呼び出してください:
<iframe src="URL_OF_YOUR_TETRIS_HTML_FILE" width="320" height="640"></iframe>
これで、ウェブサイト上でいつでもテトリスを楽しめます。ぜひ遊んでみてください!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>テトリスゲーム</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333;
color: #fff;
font-family: Arial, sans-serif;
}
canvas {
background-color: #000;
display: block;
}
</style>
</head>
<body>
<canvas id="game" width="320" height="640"></canvas>
<div id="score" style="position: absolute; top: 10px; left: 10px;">Score: 0</div>
<script>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
context.scale(20, 20);
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;
}
}
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 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) {
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, canvas.height);
drawMatrix(arena, {x: 0, y: 0});
drawMatrix(player.matrix, player.pos);
}
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();
}
dropCounter = 0;
}
function playerMove(offset) {
player.pos.x += offset;
if (collide(arena, player)) {
player.pos.x -= offset;
}
}
function playerReset() {
const pieces = 'TJLOSZI';
player.matrix = createPiece(pieces[pieces.length * Math.random() | 0]);
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) -
(player.matrix[0].length / 2 | 0);
if (collide(arena, player)) {
arena.forEach(row => row.fill(0));
player.score = 0;
updateScore();
}
}
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;
}
}
}
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
function update(time = 0) {
const deltaTime = time - lastTime;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
playerDrop();
}
lastTime = time;
draw();
requestAnimationFrame(update);
}
function updateScore() {
document.getElementById('score').innerText = `Score: ${player.score}`;
}
const colors = [
null,
'#FF0D72',
'#0DC2FF',
'#0DFF72',
'#F538FF',
'#FF8E0D',
'#FFE138',
'#3877FF',
];
const arena = createMatrix(12, 20);
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
};
document.addEventListener('keydown', event => {
if (event.keyCode === 37) {
playerMove(-1);
} else if (event.keyCode === 39) {
playerMove(1);
} else if (event.keyCode === 40) {
playerDrop();
} else if (event.keyCode === 81) {
playerRotate(-1);
} else if (event.keyCode === 87) {
playerRotate(1);
}
});
playerReset();
updateScore();
update();
</script>
</body>
</html>
参考文献(天才たちの外部リンク)
テトリス制作の各リンクはこちらだよ!
コメント