純JS實現俄羅斯方塊小遊戲

這次的代碼有點長,就想玩的直接複製就行,如果想研究可以拆分成JS文件自己看看。不需要別的庫。效果是這樣
在這裏插入圖片描述
這次懶得弄到Github裏頭,很麻煩,就這麼一個文件用不着

<規則>

  • ←鍵:控制方塊向左走一格
  • →鍵:控制方塊向右走一格
  • ↓鍵:向下走一格(加速)
  • ↑鍵:旋轉
    在這裏插入圖片描述
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>俄羅斯方塊</title>
<script type="text/javascript">
var TETRIS_ROWS = 20;
var TETRIS_COLS = 14;
var CELL_SIZE = 24;
// 沒方塊是0
var NO_BLOCK = 0;
var tetris_canvas;
var tetris_ctx;
// 記錄當前積分
var curScore = 0;
// 記錄當前速度
var curSpeed = 1;
// 記錄曾經的最高積分
var maxScore = 0;
var curScoreEle , curSpeedEle , maxScoreEle;
var curTimer;
// 記錄當前是否遊戲中的旗標
var isPlaying = true;
// 記錄正在下掉的四個方塊
var currentFall;
// 該數組用於記錄底下已經固定下來的方塊。
var tetris_status = [];
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}
	],
	// 代表第六種可能出現的方塊組合 : 條
	[
		{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}
	];
};
// 定義一個創建canvas組件的函數
var createCanvas = function(rows , cols , cellWidth, cellHeight)
{
	tetris_canvas = document.createElement("canvas");
	// 設置canvas組件的高度、寬度
	tetris_canvas.width = cols * cellWidth;
	tetris_canvas.height = rows * cellHeight;
	// 設置canvas組件的邊框
	tetris_canvas.style.border = "1px solid black";
	// 獲取canvas上的繪圖API
	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]];
				// 繪製矩形
				tetris_ctx.fillRect(j * CELL_SIZE + 1 
					, i * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2);
			}
			// 沒有方塊的地方繪製白色
			else
			{
				// 設置填充顏色
				tetris_ctx.fillStyle = 'white';
				// 繪製矩形
				tetris_ctx.fillRect(j * CELL_SIZE + 1 
					, i * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2);
			}
		}
	}
}
// 當頁面加載完成時,執行該函數裏的代碼。
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");
	// 讀取Local Storage裏的tetris_status記錄
	var tmpStatus = localStorage.getItem("tetris_status");
	tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus);
	// 把方塊狀態繪製出來
	drawBlock();
	// 讀取Local Storage裏的curScore記錄
	curScore = localStorage.getItem("curScore");
	curScore = curScore == null ? 0 : parseInt(curScore);
	curScoreEle.innerHTML = curScore;
	// 讀取Local Storage裏的maxScore記錄
	maxScore = localStorage.getItem("maxScore");
	maxScore = maxScore == null ? 0 : parseInt(maxScore);
	maxScoreEle.innerHTML = maxScore;
	// 讀取Local Storage裏的curSpeed記錄
	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)
		{
			// 將當前積分增加100
			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);
		}
		// 遍歷每個方塊, 控制每個方塊的y座標加1。
		// 也就是控制方塊都下掉一格
		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
	{
		// 遍歷每個方塊, 把每個方塊的值記錄到tetris_status數組中
		for (var i = 0 ; i < currentFall.length ; i++)
		{
			var cur = currentFall[i];
			// 如果有方塊已經到最上面了,表明輸了
			if(cur.y < 2)
			{
				// 清空Local Storage中的當前積分值、遊戲狀態、當前速度
				localStorage.removeItem("curScore");
				localStorage.removeItem("tetris_status");
				localStorage.removeItem("curSpeed");
				if(confirm("您已經輸了!是否參數排名?"))
				{
					// 讀取Local Storage裏的maxScore記錄
					maxScore = localStorage.getItem("maxScore");
					maxScore = maxScore == null ? 0 : maxScore ;
					// 如果當前積分大於localStorage中記錄的最高積分
					if(curScore >= maxScore)
					{
						// 記錄最高積分
						localStorage.setItem("maxScore" , curScore);
					}
				}
				// 遊戲結束
				isPlaying = false;
				// 清除計時器
				clearInterval(curTimer);
				return;
			}
			// 把每個方塊當前所在位置賦爲當前方塊的顏色值
			tetris_status[cur.y][cur.x] = cur.color;
		}
		// 判斷是否有“可消除”的行
		lineFull();
		// 使用Local Storage記錄俄羅斯方塊的遊戲狀態
		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;
		// 始終以第三個方塊作爲旋轉的中心,
		// i == 2時,說明是旋轉的中心
		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;
			// 始終以第三個方塊作爲旋轉的中心,
			// i == 2時,說明是旋轉的中心
			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();
// 爲窗口的按鍵事件綁定事件監聽器
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;
	}
}
</script>
<style type="text/css">
body>div {
    font-size: 13pt;
    padding-bottom: 8px;
}
span {
    font-size: 20pt;
    color: red;
}
</style>
</head>
<body>
<h2>俄羅斯方塊</h2>
<div style="width:336px;">&nbsp;
    <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> 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章