テトリスサンプル
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>テトリス</title> <script type="text/javascript" src="tetris.js"> </script> <style type="text/css"> body>div { font-size:13pt; padding-bottom: 8px; } span { font-family: tmb; font-size:20pt; color: green; } </style> </head> <body> <div style="width:336px;border:1px solid black;background:#ff9;"> <div style="float:left;">速度:<span id="curSpeedEle"></span> 得点:<span id="curScoreEle"></span></div> <div style="float:right;">最高得点:<span id="maxScoreEle"></span></div> </div> </body> </html>
tetris.js
var TETRIS_ROWS = 20; // 行数(偶数) var TETRIS_COLS = 14; // 列数(偶数) var CELL_SIZE = 24; // 位置ブロックのサイズ var NO_BLOCK = 0; // 空状態 var curScore = 0; // 現在の得点 var curSpeed = 1; // 現在の速度 var maxScore = 0; // 最高得点 var isPlaying = true; // ゲーム中 // 固定されているブロック var tetris_status = []; var curTimer; // DOM var tetris_canvas; var tetris_ctx; var curScoreEle, curSpeedEle, maxScoreEle; var currentFall; // 現在のブロック // 固定されているブロック初期化 for (var i = 0; i < TETRIS_ROWS; i++) { tetris_status[i] = []; for (var j = 0; j < TETRIS_COLS; j++) { tetris_status[i][j] = NO_BLOCK; } } // 色 colors = ["#fff", "#f00", "#0f0", "#00f", "#c60", "#f0f", "#0ff", "#609"]; // 初期で生成されるブロックの種類 var blockArr = [ // Z [{ x: TETRIS_COLS / 2 - 1, y: 0, color: 1 }, { x: TETRIS_COLS / 2, y: 0, color: 1 }, { x: TETRIS_COLS / 2, y: 1, color: 1 }, { x: TETRIS_COLS / 2 + 1, y: 1, color: 1 }], // 逆Z [{ x: TETRIS_COLS / 2 + 1, y: 0, color: 2 }, { x: TETRIS_COLS / 2, y: 0, color: 2 }, { x: TETRIS_COLS / 2, y: 1, color: 2 }, { x: TETRIS_COLS / 2 - 1, y: 1, color: 2 }], // 田 [{ x: TETRIS_COLS / 2 - 1, y: 0, color: 3 }, { x: TETRIS_COLS / 2, y: 0, color: 3 }, { x: TETRIS_COLS / 2 - 1, y: 1, color: 3 }, { x: TETRIS_COLS / 2, y: 1, color: 3 }], // L [{ x: TETRIS_COLS / 2 - 1, y: 0, color: 4 }, { x: TETRIS_COLS / 2 - 1, y: 1, color: 4 }, { x: TETRIS_COLS / 2 - 1, y: 2, color: 4 }, { x: TETRIS_COLS / 2, y: 2, color: 4 }], // J [{ x: TETRIS_COLS / 2, y: 0, color: 5 }, { x: TETRIS_COLS / 2, y: 1, color: 5 }, { x: TETRIS_COLS / 2, y: 2, color: 5 }, { x: TETRIS_COLS / 2 - 1, y: 2, color: 5 }], // l [{ x: TETRIS_COLS / 2, y: 0, color: 6 }, { x: TETRIS_COLS / 2, y: 1, color: 6 }, { x: TETRIS_COLS / 2, y: 2, color: 6 }, { x: TETRIS_COLS / 2, y: 3, color: 6 }], // ㅗ [{ x: TETRIS_COLS / 2, y: 0, color: 7 }, { x: TETRIS_COLS / 2 - 1, y: 1, color: 7 }, { x: TETRIS_COLS / 2, y: 1, color: 7 }, { x: TETRIS_COLS / 2 + 1, y: 1, color: 7 }] ]; // ブロック処理化 var initBlock = function() { var rand = Math.floor(Math.random() * blockArr.length); // 現在のブロックをランダム生成 currentFall = [{ x: blockArr[rand][0].x, y: blockArr[rand][0].y, color: blockArr[rand][0].color }, { x: blockArr[rand][1].x, y: blockArr[rand][1].y, color: blockArr[rand][1].color }, { x: blockArr[rand][2].x, y: blockArr[rand][2].y, color: blockArr[rand][2].color }, { x: blockArr[rand][3].x, y: blockArr[rand][3].y, color: blockArr[rand][3].color }]; }; // ゲーム領域生成 var createCanvas = function(rows, cols, cellWidth, cellHeight) { tetris_canvas = document.createElement("canvas"); tetris_canvas.width = cols * cellWidth; tetris_canvas.height = rows * cellHeight; tetris_canvas.style.border = "1px solid black"; tetris_ctx = tetris_canvas.getContext('2d'); tetris_ctx.beginPath(); for (var i = 1; i < TETRIS_ROWS; i++) { tetris_ctx.moveTo(0, i * CELL_SIZE); tetris_ctx.lineTo(TETRIS_COLS * CELL_SIZE, i * CELL_SIZE); } for (var i = 1; i < TETRIS_COLS; i++) { tetris_ctx.moveTo(i * CELL_SIZE, 0); tetris_ctx.lineTo(i * CELL_SIZE, TETRIS_ROWS * CELL_SIZE); } tetris_ctx.closePath(); tetris_ctx.strokeStyle = "#aaa"; tetris_ctx.lineWidth = 0.3; tetris_ctx.stroke(); } // 再描画 var drawBlock = function() { // 全体再描画 for (var i = 0; i < TETRIS_ROWS; i++) { for (var j = 0; j < TETRIS_COLS; j++) { // ブロックあり if (tetris_status[i][j] != NO_BLOCK) { // 色 tetris_ctx.fillStyle = colors[tetris_status[i][j]]; } // ブロックなし else { // 白色 tetris_ctx.fillStyle = 'white'; } // 描く tetris_ctx.fillRect(j * CELL_SIZE + 1, i * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } } } // onloadリスナー window.onload = function() { // canvasラ生成 createCanvas(TETRIS_ROWS, TETRIS_COLS, CELL_SIZE, CELL_SIZE); document.body.appendChild(tetris_canvas); curScoreEle = document.getElementById("curScoreEle"); curSpeedEle = document.getElementById("curSpeedEle"); maxScoreEle = document.getElementById("maxScoreEle"); // ローカルストレージ var tmpStatus = localStorage.getItem("tetris_status"); tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus); // ブロック再描画 drawBlock(); // 現在得点 curScore = localStorage.getItem("curScore"); curScore = curScore == null ? 0 : parseInt(curScore); curScoreEle.innerHTML = curScore; // 最高得点 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : parseInt(maxScore); maxScoreEle.innerHTML = maxScore; // 現在速度 curSpeed = localStorage.getItem("curSpeed"); curSpeed = curSpeed == null ? 1 : parseInt(curSpeed); curSpeedEle.innerHTML = curSpeed; // 固定された領域初期化 initBlock(); // 落下 curTimer = setInterval("moveDown();", 500 / curSpeed); } // 一行が埋まった処理 var lineFull = function() { // 行繰り返し for (var i = 0; i < TETRIS_ROWS; i++) { var flag = true; // 列繰り返し for (var j = 0; j < TETRIS_COLS; j++) { if (tetris_status[i][j] == NO_BLOCK) { flag = false; break; } } // 埋まった if (flag) { // 得点 curScoreEle.innerHTML = curScore += 100; // ローカルストレージに保存 localStorage.setItem("curScore", curScore); // 速度あげ if (curScore >= curSpeed * curSpeed * 500) { curSpeedEle.innerHTML = curSpeed += 1; // ハケモテLocal StorageシヌツシcurSpeed。」 localStorage.setItem("curSpeed", curSpeed); clearInterval(curTimer); curTimer = setInterval("moveDown();", 500 / curSpeed); } // 行を一段下に移動 for (var k = i; k > 0; k--) { for (var l = 0; l < TETRIS_COLS; l++) { tetris_status[k][l] = tetris_status[k - 1][l]; } } // 消す drawBlock(); } } } //落下 var moveDown = function() { // 移動可能か var canDown = true; // for (var i = 0; i < currentFall.length; i++) { // 下に着いた if (currentFall[i].y >= TETRIS_ROWS - 1) { canDown = false; break; } // 下にブロックある if (tetris_status[currentFall[i].y + 1][currentFall[i].x] != NO_BLOCK) { canDown = false; break; } } // 移動可能 if (canDown) { // 移動前白塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = 'white'; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } // 移動 for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; cur.y++; } // 移動後色塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = colors[cur.color]; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } } // 移動不可 else { // 固定ブロックに記録 for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; // ゲーム終了 if (cur.y < 2) { // ローカルストレージ保存 localStorage.removeItem("curScore"); localStorage.removeItem("tetris_status"); localStorage.removeItem("curSpeed"); if (confirm("Game Over!")) { // 最高得点 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : maxScore; if (curScore >= maxScore) { localStorage.setItem("maxScore", curScore); } } // 終了フラグ isPlaying = false; // タイマークリア clearInterval(curTimer); return; } tetris_status[cur.y][cur.x] = cur.color; } // 一行埋まったか lineFull(); localStorage.setItem("tetris_status", JSON.stringify(tetris_status)); // ブロック生成 initBlock(); } } // 左移動 var moveLeft = function() { // 移動可能 var canLeft = true; for (var i = 0; i < currentFall.length; i++) { // 一番左 if (currentFall[i].x <= 0) { canLeft = false; break; } // 左にブロックあり if (tetris_status[currentFall[i].y][currentFall[i].x - 1] != NO_BLOCK) { canLeft = false; break; } } // 移動可能 if (canLeft) { // 移動前白塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = 'white'; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } // 移動 for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; cur.x--; } // 移動後色塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = colors[cur.color]; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } } } // 右移動 var moveRight = function() { // 右移動可能か var canRight = true; for (var i = 0; i < currentFall.length; i++) { // 一番右 if (currentFall[i].x >= TETRIS_COLS - 1) { canRight = false; break; } // 右にブロックあり if (tetris_status[currentFall[i].y][currentFall[i].x + 1] != NO_BLOCK) { canRight = false; break; } } // 移動可能 if (canRight) { // 移動前白塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = 'white'; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } // 移動 for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; cur.x++; } // 移動後色塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = colors[cur.color]; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } } } // 回転 var rotate = function() { // 回転可能 var canRotate = true; for (var i = 0; i < currentFall.length; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 第3ブロックを中心に回転 if (i != 2) { // x,y計算 var afterRotateX = currentFall[2].x + preY - currentFall[2].y; var afterRotateY = currentFall[2].y + currentFall[2].x - preX; // ネ郢鋗�ラェコレホサヨテメムモミキスソ鬟ャア��サトワミ�ラェ if (tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { canRotate = false; break; } // 左超えた場合 if (afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) { moveRight(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if (afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) { moveRight(); break; } // 右を超えた場合 if (afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { moveLeft(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if (afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { moveLeft(); break; } } } // 回転可能 if (canRotate) { // 回転前にすべて白 for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = 'white'; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } for (var i = 0; i < currentFall.length; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 第3ブロック中心 if (i != 2) { currentFall[i].x = currentFall[2].x + preY - currentFall[2].y; currentFall[i].y = currentFall[2].y + currentFall[2].x - preX; } } // 回転後色塗り for (var i = 0; i < currentFall.length; i++) { var cur = currentFall[i]; tetris_ctx.fillStyle = colors[cur.color]; tetris_ctx.fillRect(cur.x * CELL_SIZE + 1, cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2); } } } window.focus(); // keydownイベント window.onkeydown = function(evt) { switch (evt.keyCode) { // ↓ case 40: if (!isPlaying) return; moveDown(); // 下移動 break; // ← case 37: if (!isPlaying) return; moveLeft(); // 左移動 break; // → case 39: if (!isPlaying) return; moveRight(); // 右移動 break; // ↑ case 38: if (!isPlaying) return; rotate(); // 回転 break; } }