JavaScriptでテトリスを作る!初心者向けゲーム開発入門
JavaScript、HTML5 Canvas、CSS を使ってテトリスゲームを一から作る方法を初心者向けに詳しく解説。ゲーム開発の基本からテトリミノの回転、ライン消去まで、段階的に実装していきます。
JavaScriptでテトリスを作る!初心者向けゲーム開発入門
「自分でゲームを作ってみたい」と思ったことはありませんか?
「ゲーム開発って難しそう」 「どこから始めればいいかわからない」
そんな不安を感じる方は多いと思います。 でも大丈夫です!
この記事では、JavaScriptを使ったテトリスゲームの作り方を初心者でも分かりやすく解説します。 HTML5 Canvasとシンプルなコードで、誰もが知っているテトリスゲームを段階的に実装していきます。
基本的なゲームループから、テトリミノの操作、ライン消去まで、一緒に作りながらゲーム開発の楽しさを体験してみませんか?
テトリス開発に必要な基礎知識
必要なスキルレベルを確認しよう
テトリス開発を始める前に、以下の知識があると理解がスムーズです。
// 必要な基礎知識の例const basicConcepts = { variables: "変数の宣言と使用", functions: "関数の定義と呼び出し", arrays: "配列の操作(二次元配列も含む)", objects: "オブジェクトの基本的な使い方", loops: "forループやwhile文", events: "イベントリスナーの基本"};
console.log("これらの概念が理解できていれば大丈夫です!");
これらの基本的なJavaScriptの知識があれば、十分についてこられます。 途中で分からないことがあっても、丁寧に説明していきますので心配いりません。
使用する技術スタックを理解しよう
今回のテトリス開発では、以下の技術を組み合わせて使用します。
- HTML5: ゲーム画面の基本構造
- CSS: スタイリングとレイアウト
- JavaScript: ゲームロジック全般
- Canvas API: グラフィックの描画
特別なライブラリは使わず、ブラウザの標準機能だけで完結させます。 そのため、環境構築も簡単で、すぐに始められますよ。
プロジェクトの基本構造を作ろう
HTMLファイルを作成しよう
まずは、ゲームの基盤となるHTMLファイルを作成します。
<!DOCTYPE html><html lang="ja"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript テトリス</title> <style> body { margin: 0; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #222; font-family: Arial, sans-serif; } .game-container { text-align: center; } canvas { border: 3px solid #fff; background-color: #000; } .score { color: #fff; font-size: 24px; margin-top: 20px; } </style></head><body> <div class="game-container"> <canvas id="gameCanvas" width="300" height="600"></canvas> <div class="score"> <div>スコア: <span id="score">0</span></div> <div>レベル: <span id="level">1</span></div> </div> </div> <script src="tetris.js"></script></body></html>
このHTMLでは、ゲーム画面となるCanvasとスコア表示エリアを配置しています。 シンプルですが、必要な要素はすべて含まれていますね。
基本的なJavaScript構造を作ろう
次に、ゲームの核となるJavaScriptファイル(tetris.js)の基本構造を作ります。
// キャンバスとコンテキストの取得const canvas = document.getElementById('gameCanvas');const ctx = canvas.getContext('2d');
// ゲームの基本設定const BOARD_WIDTH = 10;const BOARD_HEIGHT = 20;const BLOCK_SIZE = 30;
// ゲームボード(20x10のマス目)let board = [];
// ゲームの状態管理let gameState = { score: 0, level: 1, lines: 0, isGameOver: false};
ちょっと長いですね。でも大丈夫です! 一つずつ見ていきましょう。
まず、Canvasの取得から。
const canvas = document.getElementById('gameCanvas');const ctx = canvas.getContext('2d');
getElementById
でHTMLのCanvas要素を取得します。
getContext('2d')
で2D描画のコンテキストを取得しています。
次に、ゲームの基本設定です。
const BOARD_WIDTH = 10;const BOARD_HEIGHT = 20;const BLOCK_SIZE = 30;
テトリスのボードは横10マス、縦20マスが基本です。
BLOCK_SIZE
は1マスの大きさをピクセルで表しています。
ゲームボードの初期化も追加しましょう。
// ゲームボードの初期化function initBoard() { board = []; for (let row = 0; row < BOARD_HEIGHT; row++) { board[row] = []; for (let col = 0; col < BOARD_WIDTH; col++) { board[row][col] = 0; // 0は空のマス } }}
// ゲーム開始function startGame() { initBoard(); gameLoop();}
二次元配列でゲームボードを管理します。 0は空のマス、0以外の値はブロックが置かれたマスを表します。
テトリミノ(ブロック)を実装しよう
テトリミノの形状を定義しよう
テトリスの核となる7種類のテトリミノを定義しましょう。
// テトリミノの形状定義const TETROMINO_SHAPES = { I: [ [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ], O: [ [1, 1], [1, 1] ], T: [ [0, 1, 0], [1, 1, 1], [0, 0, 0] ], S: [ [0, 1, 1], [1, 1, 0], [0, 0, 0] ], Z: [ [1, 1, 0], [0, 1, 1], [0, 0, 0] ], J: [ [1, 0, 0], [1, 1, 1], [0, 0, 0] ], L: [ [0, 0, 1], [1, 1, 1], [0, 0, 0] ]};
// テトリミノの色定義const TETROMINO_COLORS = { I: '#00FFFF', // シアン O: '#FFFF00', // 黄色 T: '#800080', // 紫 S: '#00FF00', // 緑 Z: '#FF0000', // 赤 J: '#0000FF', // 青 L: '#FFA500' // オレンジ};
各テトリミノは二次元配列で形状を表現しています。 1が実際のブロック部分、0が空の部分を示しています。
色も伝統的なテトリスの色に合わせて設定しました。
現在のテトリミノクラスを作ろう
落下中のテトリミノを管理するクラスを作成します。
class CurrentTetromino { constructor() { this.shapes = Object.keys(TETROMINO_SHAPES); this.reset(); } reset() { // ランダムにテトリミノを選択 const randomShape = this.shapes[Math.floor(Math.random() * this.shapes.length)]; this.shape = TETROMINO_SHAPES[randomShape]; this.color = TETROMINO_COLORS[randomShape]; // 初期位置 this.x = Math.floor(BOARD_WIDTH / 2) - Math.floor(this.shape[0].length / 2); this.y = 0; }}
reset
メソッドで新しいテトリミノを生成します。
ランダムに選択して、画面上部の中央に配置しています。
回転機能も追加しましょう。
// テトリミノの回転rotate() { const rotated = []; const size = this.shape.length; for (let i = 0; i < size; i++) { rotated[i] = []; for (let j = 0; j < size; j++) { rotated[i][j] = this.shape[size - 1 - j][i]; } } // 回転後の位置が有効かチェック if (this.isValidPosition(rotated, this.x, this.y)) { this.shape = rotated; }}
90度回転のアルゴリズムです。 回転後の位置が有効かチェックしてから実際に回転させています。
位置の有効性をチェックする機能も重要です。
// 位置の有効性チェックisValidPosition(shape, x, y) { for (let row = 0; row < shape.length; row++) { for (let col = 0; col < shape[row].length; col++) { if (shape[row][col] && ( x + col < 0 || x + col >= BOARD_WIDTH || y + row >= BOARD_HEIGHT || (y + row >= 0 && board[y + row][x + col]) )) { return false; } } } return true;}
ボードの境界や他のブロックとの衝突をチェックしています。
ゲームループとユーザー操作を実装
メインゲームループを作ろう
ゲームの心臓部となるゲームループを実装します。
let currentTetromino = new CurrentTetromino();let dropTimer = 0;let dropInterval = 1000; // 1秒ごとに落下
function gameLoop() { if (gameState.isGameOver) { drawGameOver(); return; } // 背景をクリア ctx.fillStyle = '#000'; ctx.fillRect(0, 0, canvas.width, canvas.height); // ボードを描画 drawBoard(); // 現在のテトリミノを描画 drawTetromino(currentTetromino); // 落下処理 dropTimer += 16; // 16ms間隔で実行想定 if (dropTimer >= dropInterval) { dropTetromino(); dropTimer = 0; } // 次のフレームをスケジュール requestAnimationFrame(gameLoop);}
この関数が60FPSで繰り返し実行されて、ゲームが動きます。
落下処理の詳細も見てみましょう。
function dropTetromino() { if (currentTetromino.isValidPosition(currentTetromino.shape, currentTetromino.x, currentTetromino.y + 1)) { currentTetromino.y++; } else { // テトリミノを固定 placeTetromino(); clearLines(); currentTetromino.reset(); // ゲームオーバーチェック if (!currentTetromino.isValidPosition(currentTetromino.shape, currentTetromino.x, currentTetromino.y)) { gameState.isGameOver = true; } }}
下に移動できる場合は移動、できない場合はブロックを固定して新しいテトリミノを生成します。
キーボード操作を実装しよう
プレイヤーの操作を受け付ける機能を追加します。
// キーボードイベントの設定document.addEventListener('keydown', handleKeyPress);
function handleKeyPress(event) { if (gameState.isGameOver) return; switch(event.code) { case 'ArrowLeft': // 左移動 if (currentTetromino.isValidPosition(currentTetromino.shape, currentTetromino.x - 1, currentTetromino.y)) { currentTetromino.x--; } break; case 'ArrowRight': // 右移動 if (currentTetromino.isValidPosition(currentTetromino.shape, currentTetromino.x + 1, currentTetromino.y)) { currentTetromino.x++; } break; case 'ArrowDown': // 高速落下 dropTetromino(); break; case 'ArrowUp': case 'Space': // 回転 currentTetromino.rotate(); break; } event.preventDefault();}
矢印キーとスペースキーで直感的な操作が可能です。
操作方法をまとめると以下の通りです。
- ←→: 左右移動
- ↓: 高速落下
- ↑/スペース: 回転
描画システムを実装しよう
ボードとテトリミノの描画機能
ゲーム画面を美しく表示する描画機能を実装しましょう。
function drawBoard() { for (let row = 0; row < BOARD_HEIGHT; row++) { for (let col = 0; col < BOARD_WIDTH; col++) { if (board[row][col]) { drawBlock(col * BLOCK_SIZE, row * BLOCK_SIZE, board[row][col]); } } }}
function drawTetromino(tetromino) { for (let row = 0; row < tetromino.shape.length; row++) { for (let col = 0; col < tetromino.shape[row].length; col++) { if (tetromino.shape[row][col]) { const x = (tetromino.x + col) * BLOCK_SIZE; const y = (tetromino.y + row) * BLOCK_SIZE; drawBlock(x, y, tetromino.color); } } }}
ボードの固定されたブロックと、現在落下中のテトリミノを描画します。
どちらも同じdrawBlock
関数を使って描画しています。
美しいブロックの描画機能も作りましょう。
function drawBlock(x, y, color) { // ブロック本体 ctx.fillStyle = color; ctx.fillRect(x, y, BLOCK_SIZE, BLOCK_SIZE); // ブロックの枠線 ctx.strokeStyle = '#333'; ctx.lineWidth = 1; ctx.strokeRect(x, y, BLOCK_SIZE, BLOCK_SIZE); // 立体感のためのハイライト ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; ctx.fillRect(x, y, BLOCK_SIZE - 2, 2); ctx.fillRect(x, y, 2, BLOCK_SIZE - 2);}
ハイライトを追加することで、立体感のある美しいブロックが描画されます。 これでゲームがより魅力的に見えますね。
ライン消去とスコアシステムを作ろう
ライン消去機能を実装しよう
テトリスの醍醐味であるライン消去機能を実装します。
function clearLines() { let linesCleared = 0; // 下から上へチェック for (let row = BOARD_HEIGHT - 1; row >= 0; row--) { if (isLineFull(row)) { // ライン削除 board.splice(row, 1); // 空の行を上に追加 board.unshift(new Array(BOARD_WIDTH).fill(0)); linesCleared++; row++; // 同じ行を再チェック } } if (linesCleared > 0) { updateScore(linesCleared); updateLevel(); }}
function isLineFull(row) { return board[row].every(cell => cell !== 0);}
完成したラインを見つけて削除し、上から新しい空の行を追加します。 効率的にラインを検出して削除する仕組みです。
テトリミノを固定する処理も必要です。
function placeTetromino() { for (let row = 0; row < currentTetromino.shape.length; row++) { for (let col = 0; col < currentTetromino.shape[row].length; col++) { if (currentTetromino.shape[row][col]) { const boardRow = currentTetromino.y + row; const boardCol = currentTetromino.x + col; if (boardRow >= 0) { board[boardRow][boardCol] = currentTetromino.color; } } } }}
落下が止まったテトリミノをボードに固定します。
スコアとレベルシステムを実装
ゲームを盛り上げるスコア計算機能を追加します。
function updateScore(linesCleared) { // テトリスの伝統的なスコア計算 const scoreValues = [0, 100, 300, 500, 800]; const points = scoreValues[linesCleared] * gameState.level; gameState.score += points; gameState.lines += linesCleared; // スコア表示を更新 document.getElementById('score').textContent = gameState.score; console.log(`${linesCleared}ライン消去! +${points}点`);}
function updateLevel() { const newLevel = Math.floor(gameState.lines / 10) + 1; if (newLevel > gameState.level) { gameState.level = newLevel; // レベルアップで落下速度アップ dropInterval = Math.max(100, 1000 - (gameState.level - 1) * 100); document.getElementById('level').textContent = gameState.level; console.log(`レベルアップ! レベル${gameState.level}`); }}
同時に消したライン数によってスコアが変わります。 テトリス(4ライン同時消去)が最も高得点ですね。
レベルが上がるごとに落下速度も上昇するので、だんだん難しくなります。
ゲームの完成と動作確認
ゲームオーバー処理を実装
ゲーム終了時の表示を実装して完成させましょう。
function drawGameOver() { // 半透明の覆い ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; ctx.fillRect(0, 0, canvas.width, canvas.height); // ゲームオーバーテキスト ctx.fillStyle = '#fff'; ctx.font = 'bold 36px Arial'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 50); ctx.font = '20px Arial'; ctx.fillText(`最終スコア: ${gameState.score}`, canvas.width / 2, canvas.height / 2); ctx.fillText('F5キーでリスタート', canvas.width / 2, canvas.height / 2 + 50);}
// ゲーム開始console.log("テトリスゲームを開始します!");startGame();
ゲームオーバー時に美しい表示が出るようになりました。 F5キーでページをリロードして、再びゲームを楽しめます。
追加機能のアイデア
基本的なテトリスが完成したら、以下の機能を追加してみるのもおすすめです。
実装可能な追加機能を考えてみましょう。
- 次のピース表示: 次に来るテトリミノを表示
- ピースホールド機能: テトリミノを一時保存
- 落下予測表示: ゴーストピースの表示
- 効果音の追加: ライン消去やレベルアップの音
- ライン消去エフェクト: 派手な消去アニメーション
- 詳細統計情報: プレイ時間や各ピースの使用回数
これらの機能を追加することで、より本格的なテトリスゲームになります。 ぜひ挑戦してみてください!
まとめ:テトリス開発から学んだこと
この記事では、JavaScriptを使ってテトリスゲームを一から作る方法を解説しました。
ゲーム開発を通じて、以下のプログラミングスキルが身についたはずです。
- 二次元配列の操作: ゲームボードの管理
- オブジェクト指向設計: テトリミノクラスの実装
- イベント処理: キーボード操作の実装
- Canvas描画: リアルタイムグラフィック
- アルゴリズム思考: 回転やライン消去のロジック
最初は複雑に感じたかもしれませんが、段階的に実装することで立派なゲームが完成しましたね。
この経験を活かして、ぜひ他のゲームやWebアプリケーションの開発にも挑戦してみてください。 プログラミングの楽しさを実感できるテトリス開発、お疲れさまでした!