2025 08 . 04 MON
HTML CSS JavaScriptで作るテトリス - 初心者でも楽しく学べる実装方法
Web開発・フロントエンド

HTML CSS JavaScriptで作るテトリス - 初心者でも楽しく学べる実装方法

2025.07.22

このサイトはアフィリエイト広告を利用しています。

はじめに

こんにちは!Web技術を使って自分だけのゲームを作ってみたい、と思ったことはありませんか?

この記事では、HTML、CSS、そしてJavaScriptという基本的なWeb技術だけで、あの有名なパズルゲーム「テトリス」を作成する方法を、ゼロから丁寧に解説していきます。

プログラミング学習の素晴らしい点は、実際に「動くもの」を作りながら学べることです。テトリスは、ゲーム開発の基本的な要素(描画、ユーザー操作、ルール実装など)が詰まっており、初心者の方が楽しみながらスキルアップするのに最適な題材です。

「百聞は一見に如かず」 ということで、まずは完成品がどのようなものか、実際に遊んでみてください!

完成版テトリス - まずは遊んでみよう!

こちらが今回作成するテトリスゲームの完成版です。キーボードの矢印キーで操作できます。ぜひ一度プレイして、どのようなゲームを作るのかイメージを掴んでください。

完成版テトリス - プレイ可能なデモ

実行結果

ゲーム作りのステップバイステップ解説

いかがでしたか?ここからは、このテトリスゲームをどのように作っていくのか、その手順を一つずつ解説していきます。

Step 1: 準備 - 必要なもの

ゲーム開発というと難しそうですが、今回のテトリス作りに特別なツールは必要ありません。

  • テキストエディタ: Visual Studio Code、Atomなど、お好みのエディタでOKです。
  • Webブラウザ: Google ChromeやFirefoxなど、普段お使いのもので十分です。

💪 前提知識について

HTML、CSS、JavaScriptの基本的な知識(変、配列、if文、for文など)があるとスムーズですが、分からなくても大丈夫!作りながら学んでいけるように、丁寧に解説します。

Step 2: HTMLでゲームの骨格を作る

まず、ゲームの土台となるHTMLファイルを用意します。完成版では、ゲーム画面とサイド情報を並べて表示する構造になっています。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML CSS JavaScript テトリス</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="tetris-game">
<h2>テトリス</h2>
<div class="game-container">
<!-- ゲーム画面 -->
<canvas id="tetris-canvas" width="150" height="300"></canvas>
<!-- ゲーム情報パネル -->
<div class="game-info">
<p>スコア: <span id="score-display">0</span></p>
<p>ライン: <span id="lines-display">0</span></p>
<p>レベル: <span id="level-display">1</span></p>
<!-- 操作説明 -->
<div class="controls">
<p><strong>操作:</strong></p>
<p>← → : 移動</p>
<p>↓ : 高速落下</p>
<p>↑ : 回転</p>
<p>Space : 即座に落下</p>
</div>
<!-- リスタートボタン -->
<button id="restart-btn">リスタート</button>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
柴犬アイコン

完成版では、ゲーム画面とサイド情報を横並びにするため、.game-container でフレックスレイアウトを使用しています。また、操作説明も含めることで、初めてプレイする人にも分かりやすくしています。

HTMLの重要なポイント

1. セマンティックな構造

<div id="tetris-game"> <!-- ゲーム全体のコンテナ -->
<h2>テトリス</h2> <!-- タイトル -->
<div class="game-container"> <!-- ゲーム画面とサイド情報の親要素 -->
<canvas id="tetris-canvas"> <!-- ゲーム描画エリア -->
<div class="game-info"> <!-- サイド情報パネル -->

2. Canvas要素の設定

<canvas id="tetris-canvas" width="150" height="300"></canvas>
  • width="150" height="300": ゲーム画面のピクセルサイズ
  • この比率は、10×20のゲーム盤に15ピクセルブロックを使った計算

3. 動的表示用のspan要素

<p>スコア: <span id="score-display">0</span></p>
  • JavaScriptからtextContentを更新するためのspan要素

💪 viewport meta tagの重要性

<meta name="viewport" content="width=device-width, initial-scale=1.0">は、モバイルデバイスでの表示を最適化するために必須です。これがないと、スマートフォンでの表示が崩れる可能性があります。

Step 3: CSSで魅力的なデザインを作る

次に、CSSを使ってゲーム画面を美しく、そして使いやすくデザインします。完成版では、グラデーション背景、フレックスボックスレイアウト、レスポンシブデザインを採用しています。

1. ベーススタイルとレイアウト

まず、全体のレイアウトとベースとなるスタイルを定義します。

/* メインコンテナのスタイル */
#tetris-game {
text-align: center;
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 15px;
color: white;
}
/* ゲーム画面とサイド情報を並べるコンテナ */
.game-container {
display: flex;
justify-content: center;
align-items: flex-start; /* 上揃え */
gap: 20px; /* 要素間の間隔 */
margin: 20px 0;
}

グラデーション背景の解説

background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  • 135deg: 右下方向への斜めグラデーション
  • #667eea: 青紫色(開始色)
  • #764ba2: 深い紫色(終了色)

2. Canvas(ゲーム画面)のスタイル

ゲーム画面となるCanvas要素に、枠線と影を付けて立体的に見せます。

#tetris-canvas {
border: 3px solid #fff; /* 白い枠線 */
background: #000; /* 黒い背景 */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); /* ドロップシャドウ */
}
柴犬アイコン

box-shadowを使うことで、ゲーム画面に奥行きを与え、より魅力的な見た目にしています。

3. サイド情報パネルのデザイン

スコアや操作説明を表示するサイド情報パネルを、半透明の背景でエレガントにデザインします。

.game-info {
background: rgba(255, 255, 255, 0.1); /* 半透明の白背景 */
padding: 20px;
border-radius: 10px;
min-width: 150px;
text-align: left;
}
.game-info p {
margin: 10px 0;
font-size: 16px;
}
/* 操作説明部分 */
.controls {
margin-top: 20px;
font-size: 14px;
}
.controls p {
margin: 5px 0;
}

半透明背景の効果

background: rgba(255, 255, 255, 0.1);
  • rgba(255, 255, 255, 0.1): 白色の10%透明度
  • 背景のグラデーションが透けて見える美しい効果

4. ボタンのデザインとホバー効果

リスタートボタンに、ホバー時の色変化を加えてユーザビリティを向上させます。

#restart-btn {
background: #4caf50; /* 緑色の背景 */
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-top: 10px;
transition: background-color 0.3s ease; /* スムーズな色変化 */
}
/* ホバー時のスタイル */
#restart-btn:hover {
background: #45a049; /* より深い緑色 */
}
/* フォーカス時のスタイル(キーボード操作時) */
#restart-btn:focus {
outline: 2px solid #fff;
outline-offset: 2px;
}

💡 transition プロパティ

transition: background-color 0.3s ease;により、ホバー時の背景色変化が滑らかになり、より洗練された印象を与えます。

5. レスポンシブデザイン(モバイル対応)

スマートフォンやタブレットでも快適にプレイできるよう、メディアクエリを使用します。

/* モバイルデバイス向けの調整 */
@media (max-width: 600px) {
.game-container {
flex-direction: column; /* 縦並びに変更 */
align-items: center;
}
#tetris-canvas {
width: 250px; /* Canvas を大きく表示 */
height: 500px;
/* 注意: CSSでのサイズ変更は画質に影響する場合があります */
}
.game-info {
margin-top: 20px;
min-width: 250px; /* 情報パネルの幅を統一 */
}
/* フォントサイズの調整 */
.game-info p {
font-size: 18px;
}
.controls {
font-size: 16px;
}
}

レスポンシブデザインの工夫点

  1. フレックス方向の変更: flex-direction: column で縦並びに
  2. Canvas サイズの調整: モバイルでは大きめに表示
  3. フォントサイズの拡大: タッチデバイスでの可読性向上

6. 完全版CSSコード

全てを組み合わせた完全なCSSコードです:

#tetris-game {
text-align: center;
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 15px;
color: white;
}
.game-container {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 20px;
margin: 20px 0;
}
#tetris-canvas {
border: 3px solid #fff;
background: #000;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
.game-info {
background: rgba(255, 255, 255, 0.1);
padding: 20px;
border-radius: 10px;
min-width: 150px;
text-align: left;
}
.game-info p {
margin: 10px 0;
font-size: 16px;
}
.controls {
margin-top: 20px;
font-size: 14px;
}
.controls p {
margin: 5px 0;
}
#restart-btn {
background: #4caf50;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-top: 10px;
transition: background-color 0.3s ease;
}
#restart-btn:hover {
background: #45a049;
}
#restart-btn:focus {
outline: 2px solid #fff;
outline-offset: 2px;
}
/* レスポンシブデザイン */
@media (max-width: 600px) {
.game-container {
flex-direction: column;
align-items: center;
}
#tetris-canvas {
width: 250px;
height: 500px;
}
.game-info {
margin-top: 20px;
min-width: 250px;
}
.game-info p {
font-size: 18px;
}
.controls {
font-size: 16px;
}
}

Step 4: JavaScriptでゲームの心臓部を作る

ここからが本番です。JavaScriptでゲームのロジックを実装していきます。完成版のコードは少し長くなりますが、機能ごとに分けて見ていきましょう。

1. 全体構造とセットアップ

まず、コード全体を即座実行関数(IIFE)で囲み、変数の名前空間を保護します。これにより、他のスクリプトとの競合を避けることができます。

(function() {
'use strict'; // strict modeで安全なコードを書く
// ゲーム設定
const BOARD_WIDTH = 10;
const BOARD_HEIGHT = 20;
const BLOCK_SIZE = 15;
// ゲーム状態
let board = [];
let score = 0;
let lines = 0;
let level = 1;
let gameSpeed = 1000;
let gameRunning = false;
let currentPiece = null;
let gameLoop = null;
// Canvas要素の取得(エラーハンドリング付き)
const canvas = document.getElementById('tetris-canvas');
if (!canvas) {
console.error('Canvas element not found');
return;
}
const ctx = canvas.getContext('2d');
})();

💪 IIFE(即座実行関数)とは

(function() { ... })()の形で書かれた関数は、定義と同時に実行されます。これにより、グローバル変数の汚染を防ぎ、他のスクリプトとの競合を避けることができます。

2. テトリミノ(ブロック)の定義

テトリスに登場する7種類のブロック(テトリミノ)の形と色を定義します。完成版では正確な形状と色を使用しています。

// テトリミノ定義(正しい形状)
const PIECES = [
{ shape: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], color: '#00f0f0' }, // I
{ shape: [[1,1],[1,1]], color: '#f0f000' }, // O
{ shape: [[0,1,0],[1,1,1],[0,0,0]], color: '#a000f0' }, // T
{ shape: [[0,1,1],[1,1,0],[0,0,0]], color: '#00f000' }, // S
{ shape: [[1,1,0],[0,1,1],[0,0,0]], color: '#f00000' }, // Z
{ shape: [[1,0,0],[1,1,1],[0,0,0]], color: '#0000f0' }, // J
{ shape: [[0,0,1],[1,1,1],[0,0,0]], color: '#f0a000' } // L
];

3. ゲーム初期化処理

ゲーム開始時とリスタート時に呼び出される初期化関数を実装します。エラーハンドリングも含めた安全な実装です。

function initGame() {
try {
// ボード初期化(2次元配列で空の状態を作成)
board = Array(BOARD_HEIGHT).fill().map(() => Array(BOARD_WIDTH).fill(0));
score = 0;
lines = 0;
level = 1;
gameSpeed = 1000;
gameRunning = true;
// 既存のゲームループをクリア
if (gameLoop) {
clearInterval(gameLoop);
gameLoop = null;
}
// 新しいピースを作成
currentPiece = createPiece();
// 表示更新
updateDisplay();
// ゲームループ開始
gameLoop = setInterval(() => {
if (gameRunning) {
dropPiece();
draw();
}
}, gameSpeed);
// 初期描画
draw();
console.log('Game initialized successfully');
} catch (error) {
console.error('Error initializing game:', error);
}
}

4. ピース作成と衝突判定

新しいピースを作成する関数と、ブロックが衝突するかを判定する関数を実装します。

function createPiece() {
const pieceIndex = Math.floor(Math.random() * PIECES.length);
const piece = PIECES[pieceIndex];
return {
x: Math.floor(BOARD_WIDTH / 2) - Math.floor(piece.shape.length / 2),
y: 0,
shape: piece.shape.map(row => [...row]), // ディープコピーで配列を複製
color: piece.color
};
}
function isCollision(piece, dx = 0, dy = 0) {
for (let y = 0; y < piece.shape.length; y++) {
for (let x = 0; x < piece.shape[y].length; x++) {
if (piece.shape[y][x]) {
const newX = piece.x + x + dx;
const newY = piece.y + y + dy;
// 境界チェックと既存ブロックとの衝突チェック
if (newX < 0 || newX >= BOARD_WIDTH ||
newY >= BOARD_HEIGHT ||
(newY >= 0 && board[newY] && board[newY][newX])) {
return true;
}
}
}
}
return false;
}

5. ゲームループとブロックの落下

ゲームの中核となる落下処理とライン消去、スコア計算を実装します。

function dropPiece() {
if (!currentPiece || !gameRunning) return;
if (!isCollision(currentPiece, 0, 1)) {
currentPiece.y++;
} else {
placePiece();
const linesCleared = clearLines();
// スコア計算とレベルアップ処理
if (linesCleared > 0) {
lines += linesCleared;
const scoreTable = [0, 40, 100, 300, 1200]; // テトリスの標準スコア表
score += scoreTable[linesCleared] * level;
level = Math.floor(lines / 10) + 1;
gameSpeed = Math.max(100, 1000 - (level - 1) * 100);
// ゲームスピード更新
if (gameLoop) {
clearInterval(gameLoop);
gameLoop = setInterval(() => {
if (gameRunning) {
dropPiece();
draw();
}
}, gameSpeed);
}
}
currentPiece = createPiece();
if (isCollision(currentPiece)) {
gameRunning = false;
alert('ゲームオーバー!スコア: ' + score);
if(gameLoop) clearInterval(gameLoop);
}
updateDisplay();
}
}
柴犬アイコン

完成版では、テトリスの標準的なスコア計算(1ライン40点、2ライン100点、3ライン300点、4ライン1200点)を実装し、レベルが上がるとゲームスピードも速くなります。

6. ブロック回転とライン消去

ブロックの回転処理と、揃ったラインを消去する処理を実装します。

function rotatePiece() {
if (!currentPiece) return;
const originalShape = currentPiece.shape;
const n = originalShape.length;
const rotated = Array(n).fill(0).map(() => Array(n).fill(0));
// 90度時計回りに回転
for (let y = 0; y < n; y++) {
for (let x = 0; x < n; x++) {
rotated[x][n - 1 - y] = originalShape[y][x];
}
}
currentPiece.shape = rotated;
// 衝突する場合は元に戻す
if (isCollision(currentPiece)) {
currentPiece.shape = originalShape;
}
}
function clearLines() {
let linesCleared = 0;
for (let y = BOARD_HEIGHT - 1; y >= 0; y--) {
if (board[y].every(cell => cell !== 0)) {
board.splice(y, 1); // ラインを削除
board.unshift(Array(BOARD_WIDTH).fill(0)); // 上に空のラインを追加
linesCleared++;
y++; // 削除したラインを再チェック
}
}
return linesCleared;
}

7. 描画処理と表示更新

ゲーム画面を描画し、スコアなどの情報を更新する処理を実装します。

function draw() {
if (!ctx) return;
// 画面クリア
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// ボードに固定されたブロックを描画
for (let y = 0; y < BOARD_HEIGHT; y++) {
for (let x = 0; x < BOARD_WIDTH; x++) {
if (board[y][x]) {
ctx.fillStyle = board[y][x];
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
}
}
}
// 現在落下中のピースを描画
if (currentPiece && gameRunning) {
ctx.fillStyle = currentPiece.color;
for (let y = 0; y < currentPiece.shape.length; y++) {
for (let x = 0; x < currentPiece.shape[y].length; x++) {
if (currentPiece.shape[y][x]) {
ctx.fillRect(
(currentPiece.x + x) * BLOCK_SIZE,
(currentPiece.y + y) * BLOCK_SIZE,
BLOCK_SIZE - 1,
BLOCK_SIZE - 1
);
}
}
}
}
}
function updateDisplay() {
const scoreEl = document.getElementById('score-display');
const linesEl = document.getElementById('lines-display');
const levelEl = document.getElementById('level-display');
if (scoreEl) scoreEl.textContent = score;
if (linesEl) linesEl.textContent = lines;
if (levelEl) levelEl.textContent = level;
}

8. プレイヤー操作とゲーム開始

キーボード操作とリスタート機能を実装し、最後にゲームを開始します。

// キーボード操作
document.addEventListener('keydown', function(e) {
if (!currentPiece || !gameRunning) return;
switch(e.key) {
case 'ArrowLeft':
if (!isCollision(currentPiece, -1, 0)) {
currentPiece.x--;
draw();
}
break;
case 'ArrowRight':
if (!isCollision(currentPiece, 1, 0)) {
currentPiece.x++;
draw();
}
break;
case 'ArrowDown':
dropPiece();
draw();
break;
case 'ArrowUp':
rotatePiece();
draw();
break;
case ' ': // スペースキーでハードドロップ
while (!isCollision(currentPiece, 0, 1)) {
currentPiece.y++;
}
dropPiece();
draw();
break;
}
e.preventDefault(); // デフォルトの動作を防止
});
// リスタートボタンの処理
const restartBtn = document.getElementById('restart-btn');
if (restartBtn) {
restartBtn.addEventListener('click', function() {
if (gameLoop) {
clearInterval(gameLoop);
gameLoop = null;
}
initGame();
});
}
// ゲーム開始(少し遅延させてDOM要素が確実に読み込まれるようにする)
setTimeout(() => {
initGame();
}, 100);

💡 完成版の特徴

完成版では、スペースキーでのハードドロップ機能、レベル上昇によるスピードアップ、正確なスコア計算、エラーハンドリングなど、より本格的なテトリスゲームの機能を実装しています。

まとめ

この記事では、HTML、CSS、JavaScriptを使ってテトリスゲームを作成する基本的な流れを解説しました。完成品のデモから始めることで、目標を明確にしながら学習を進められたのではないでしょうか。

開発のステップをまとめると、以下のようになります。

graph TD
A[完成形をイメージ] --> B(Step1: HTMLで骨格作成)
B --> C(Step2: CSSでデザイン)
C --> D(Step3: JavaScriptでロジック実装)
D --> E(ロジックの詳細)
E --> F(1.基本設定)
E --> G(2.ブロック定義)
E --> H(3.描画処理)
E --> I(4.ゲームループ)
E --> J(5.ユーザー操作)
E --> K(6.衝突判定とライン消去)
K --> L(完成!)

テトリス開発のステップ

ここからさらに、

  • 次に出現するブロックの表示
  • スコアやレベルの計算
  • 効果音やBGMの追加
  • タッチ操作への対応

など、様々な機能を追加して、あなただけのオリジナルゲームに発展させることも可能です。

この記事が、あなたのゲーム開発の第一歩となれば、とても嬉しいです。

著者アイコン
コッチ フロントエンドエンジニア

フロントエンドエンジニア歴10年。コロナ禍を機にフル在宅ワークへ移行。Web開発の知見を活かし、AI活用法や仕事が捗るガジェットについて発信しています。柴犬が好きです。