< ;script type="text/javascript"> /********************************************js Tetris source code**** *******************************************/ //Author: Gaoshan Liushui QQ: 21243468 //Creation date: 2009-08-06 / /Copyright Statement: This work was created by Gao Shanliushui. Please indicate the source when reprinting. Thank you for your cooperation! //Game design instructions: //1. Since this game is a two-dimensional game, laying out the grid is the key to writing the game well, whether it is the game window or the preview window, // The concept of grid is used in the block set virtual map. The advantage of this is that it can avoid frequently obtaining the position of elements. In addition, it can also accurately position and deform the movement of the block set //. The more important point here is to define the map of the block set in advance. For example, for the L block set, a three by three square //grid should be defined, and then according to each shape of L, determine each block set The position of the block in the square grid. In addition, the position of the block set in the map // (game window or preview window) needs to be saved, so that at any time, through the position of the block set, and the position of the block in the square grid position, you can determine the position of each block in the //block set in the map. //2. This game mainly uses some OOP ideas. For example, define a base class base, and the block set class inherits from the base class, which also includes object encapsulation, attributes, //definition of enumerations, etc., and of course events, delegates, attributes, garbage collectors, etc., because Due to time constraints, I will not do a code demonstration. If you are interested, you can expand it yourself // to deepen your understanding of js OOP. //Extensions of 3.js built-in objects: such as Array.prototype.removeAt, etc. /********************************************js Tetris source code**** ******************************************/ var Sys = null; function sys() { } sys.prototype = { GameMap: [], PreviewMap: [], BlocksObj: [], Timer: null, HorizontalNum: 10, //The level of the game map Number of cells VerticalNum: 18, //The number of vertical cells in the game map IsGameOver: false, //Determine whether the game is over ScoreStrategy: [100, 300, 500, 800], //Score strategy LevelScores: [100, 20000, 40000, 60000, 80000, 100000, 120000, 140000, 160000, 200000], //Score level IsPlay: false, //The game is in progress IsPlay: true, //Whether it is the first time to play SmallGridNum: 6, //The number of grids in the preview map is a square DirectionEnum: { left: 0, right: 1, up: 2, down: 3 }, //Direction Enumeration Speeds: [1000, 900, 800, 700, 600, 500, 400, 300, 200, 100], //Speed, or level CurrentSpeed: 1000, //Current level or level Speed TypeEnum: { none: 0, block: 1, blocks: 2 }, //Type BlocksEnum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], //0 is LL, 1 is LR, 2 is T, 3 is ZL, 4 is ZR, 5 is I, 6 is F, 7 is long T BlocksStateNum: [4, 4, 4, 2, 2, 2, 1, 4, 4, 2], //Corresponding to the number of deformations of each block set in BlocksEnum, that is, how many deformations there are BlocksShapeMaps: [ //A collection of block shape sets [ //Blocks A virtual map set of sets, corresponding to BlocksEnum, for example, the map is L's map [[[2, 1]], [[0, 2], [1, 2], [2, 2]]], / One of the transformations in /L [[[1, 0]], [[1, 1]], [[1, 2], [2, 2]]], [[[0, 1], [1, 1], [2, 1]], [[0, 2]]], [[[1, 0], [2, 0]], [[2, 1]] , [[2, 2]]] ], [ [[[2, 0]], [[2, 1]], [[1, 2], [2, 2] ]], [[[0, 1]], [[0, 2], [1, 2]], [[2, 2]]], [[[0, 0], [ 1, 0]], [[0, 1]], [[0, 2]]], [[[0, 1], [1, 1], [2, 1]], [[2 , 2]]] ], [ [[[0, 0], [1, 0], [2, 0]], [[1, 1]]], [[[1, 0]], [[0, 1], [1, 1]], [[1, 2]]], [[[1, 1]], [[0, 2] , [1, 2], [2, 2]]], [[[0, 0]], [[0, 1], [1, 1]], [[0, 2]]] ], [ [[0, 0]], [[0, 1], [1, 1]], [[1, 2]]], [[[1 , 1], [2, 1]], [[0, 2], [1, 2]]] ], [ [[[1, 0]], [[0, 1], [1, 1]], [[0, 2]]], [[[0, 1], [1, 1]], [[1, 2], [2, 2]] ] ], [ [[[0, 0]], [[0, 1]], [[0, 2]], [[0, 3]]], [[[0, 3]], [[1, 3]], [[2, 3]], [[3, 3]]] ], [ [[[0, 0], [0, 1]], [[1, 0], [1, 1]]] ], [ [[[0, 0], [1, 0], [2, 0]], [[1, 1]], [[1, 2]]], [[[2, 0]], [[0, 1], [1, 1], [ 2, 1]], [[2, 2]]], [[[1, 0]], [[1, 1]], [[0, 2], [1, 2], [2 , 2]]], [[[0, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]] ] , [ [[[0, 1], [1, 1], [2, 1]], [[0, 2]], [[2, 2]]], [ [[1, 0], [2, 0]], [[2, 1]], [[1, 2], [2, 2]]], [[[0, 1], [2 , 1]], [[0, 2], [1, 2], [2, 2]]], [[[1, 0], [2, 0]], [[1, 1] ], [[1, 2], [2, 2]]] ], [ [[[0, 0], [1, 0]], [[1, 1]] , [[1, 2], [2, 2]]], [[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[ 0, 2]]] ], ], ColorEnum: [[0, 0], [-28, 0], [-56, 0], [-84, 0], [ -112, 0], [-140, 0], [-168, 0], [0, 0], [-28, 0], [-56, 0]], //Color enumeration, corresponding to BlocksEnum CreateGameMap: function() { //Create game map for (var i = 0; i < this.VerticalNum; i ) { this.GameMap.push([]); for (var j = 0; j < this.HorizontalNum; j ) { this.GameMap[i][j] = {}; this.GameMap[i][j][Sys.TypeEnum. blocks] = null; } } }, GetBlocks: function() { //Get the block set in GameMap for (var i = 0; i < this. BlocksObj.length; i ) { if (this.BlocksObj[i].isInGameMap) { return this.BlocksObj[i]; } } return null; }, AllowBlocksMove: function() { //Whether the block set is allowed to move var blocksItem = this.GetBlocks(); var itemPosArray = this._getBlocksItemPosArray(blocksItem, false); return this .NoBlocksInthePlace(itemPosArray, blocksItem) && this.CheckBoundary(itemPosArray, blocksItem); }, GetMaxAndMinItemPosArray: function(itemPosArray) { //Get the maximum and minimum block position set itemPosArray.ItemPosXArray.sorts() ; itemPosArray.ItemPosYArray.sorts(); return { maxItemPosX: itemPosArray.ItemPosXArray[itemPosArray.ItemPosXArray.length - 1], maxItemPosY: itemPosArray.ItemPosYArray[itemPosArray.ItemPosYArray.length - 1], minItem PosX: itemPosArray .ItemPosXArray[0], minItemPosY: itemPosArray.ItemPosYArray[0] }; }, NoBlocksInthePlace: function(itemPosArray, blocksItem) { //Detect whether there are already blocks in the game grid return this. _isOverMapChild(itemPosArray, blocksItem) ? false : true; }, CheckBoundary: function(itemPosArray, blocksItem) { //是否到达边界了 var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray); var isNotToBoundary = false; switch (blocksItem.currentDirectionEnum) { case this.DirectionEnum.left: isNotToBoundary = (maxAndMinItemPosArray.minItemPosX > 0) break; case this.DirectionEnum.right: isNotToBoundary = (maxAndMinItemPosArray.maxItemPosX < this.HorizontalNum - 1) break; case this.DirectionEnum.down: isNotToBoundary = (maxAndMinItemPosArray.maxItemPosY < this.VerticalNum - 1); break; } return isNotToBoundary; }, _isOverMapChild: function(itemPosArray, blocksItem) { //检测是否会覆盖某个游戏方格中的元素 var isOverMapChild = false; for (var i = 0; i < itemPosArray.ItemPosYArray.length; i ) { var itemX = itemPosArray.ItemPosXArray[i]; var itemY = itemPosArray.ItemPosYArray[i]; if (blocksItem.currentDirectionEnum == this.DirectionEnum.left) { itemX--; } else if (blocksItem.currentDirectionEnum == this.DirectionEnum.right) { itemX ; } else if (blocksItem.currentDirectionEnum == this.DirectionEnum.down) { itemY ; } if (this.GameMap[itemY] && this.GameMap[itemY][itemX] && this.GameMap[itemY][itemX][this.TypeEnum.blocks] != null) { isOverMapChild = true; break; } } return isOverMapChild; }, _getBlocksItemPosArray: function(blocksItem, isRelative) { //获取方块集的位置集合,isRelative=true获取的是方块相对方块集map的位置,否则是方块相对游戏map的位置 var itemPosXArray = []; var itemPosYArray = []; for (var i = 0; i < blocksItem.blocks.length; i ) { if (isRelative) { itemPosXArray.push(blocksItem.blocks[i].x); itemPosYArray.push(blocksItem.blocks[i].y); } else { itemPosXArray.push(blocksItem.blocks[i].x blocksItem.x); itemPosYArray.push(blocksItem.blocks[i].y blocksItem.y); } } return { ItemPosXArray: itemPosXArray, ItemPosYArray: itemPosYArray }; }, GetBlocksInitPos: function(blocks) { //获取方块的初始位置 var blocksItem = null; if (!blocks) blocksItem = this.GetBlocks(); else blocksItem = blocks; var itemPosArray = this._getBlocksItemPosArray(blocksItem, true); itemPosArray.ItemPosXArray = itemPosArray.ItemPosXArray.filter(); itemPosArray.ItemPosYArray = itemPosArray.ItemPosYArray.filter(); var childsNumX = itemPosArray.ItemPosXArray.length; var childsNumY = itemPosArray.ItemPosYArray.length; var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray); if (blocks) //获取方块集在预览map中的初始位置 return { x: (this.SmallGridNum - childsNumX - 1) / 2 0.5 - maxAndMinItemPosArray.minItemPosX, y: (this.SmallGridNum - childsNumY - 1) / 2 0.5 - maxAndMinItemPosArray.minItemPosY }; else //获取方块集在游戏map中的初始位置 return { x: parseInt((this.HorizontalNum - childsNumX - 1) / 2) 1 - maxAndMinItemPosArray.minItemPosX, y: -(childsNumY maxAndMinItemPosArray.minItemPosY) }; }, GetNextActiviteBrocks: function() { //获取下一个活动的方块集 for (var i = 0; i < this.BlocksObj.length; i ) { if (this.BlocksObj[i].isInGameMap) { this.BlocksObj.removeAt(i); } } this.BlocksObj[0].isInGameMap = true; var itemPos = this.GetBlocksInitPos(); this.BlocksObj[0].x = itemPos.x; this.BlocksObj[0].y = itemPos.y; this.BlocksObj[0].AddToMap(false, false); this.CreateBlocks(); }, PlayGame: function() { //启动游戏 this.IsPlay = true; this.NaturalMove(); if (!this.IsFirstPlay) { return; } this.GetNextActiviteBrocks(); }, AddToGameMapGrid: function() { //加入到游戏map网格中 var blocks = this.GetBlocks(); blocks.UseGrid(this.GameMap, blocks); }, GetScore: function() { //分数处理 var rowIndexArray = []; for (var i = 0; i < this.VerticalNum; i ) { //获取满行的行数 var entireRow = true; for (var j = 0; j < this.HorizontalNum; j ) { if (this.GameMap[i][j][this.TypeEnum.blocks] == null) { entireRow = false; break; } } if (entireRow) rowIndexArray.push(i); } if (rowIndexArray.length > 0) { this._FreeMapGrid(rowIndexArray); document.getElementById("score").innerText = this.ScoreStrategy[rowIndexArray.length - 1] parseInt(document.getElementById("score").innerText); this.CheckTheLevel(); } }, CheckTheLevel: function() { //Check whether to enter the next level var currentScore = parseInt(document.getElementById("score").innerText); var speedList = document.getElementById("speed "); var currentLevel = parseInt(speedList.options[speedList.selectedIndex].text) - 1; var levelScore = this.LevelScores[currentLevel]; if (currentScore >= levelScore) { if (currentLevel < this.LevelScores.length) { var element = document.getElementById("gameOver"); element.innerText = "Congratulations on passing " (speedList.selectedIndex 1) " "off"; element.style.display = "block"; this.PauseGame(); document.getElementById("btnStart").disabled = true; document.getElementById("speed ").disabled = true; this._goToNextLevel.delay(3000); } else { this._finishAllTheLevel(element); } } } , _goToNextLevel: function() { //Enter the next level, speed up Sys.IsPlay = true; document.getElementById("btnStart").disabled = false; var speedList = document.getElementById("speed"); speedList.disabled = false; speedList.options[speedList.selectedIndex 1].selected = true; Sys.CurrentSpeed = Sys.Speeds[speedList.selectedIndex 1 ]; Sys.NaturalMove(); document.getElementById("gameOver").style.display = "none"; }, _finishAllTheLevel: function() { //Finish all Game level this.PauseGame(); }, _FreeMapGrid: function(rowIndexArray) { //Release the full row grid from the game map var gameMap = this.GameMap; var startIndex = rowIndexArray[0]; var len = rowIndexArray.length; var maxIndex = startIndex len - 1; for (var i = startIndex; i <= maxIndex; i ) { for (var j = 0; j < this.HorizontalNum; j ) { if (gameMap[i][j][this.TypeEnum.blocks] != null) { document.getElementById( "map").removeChild(gameMap[i][j][this.TypeEnum.blocks].domElement); gameMap[i][j][this.TypeEnum.blocks] = null; } } } this.ResetMapGrid(rowIndexArray); }, ResetMapGrid: function(rowIndexArray) { //Reset the game grid var gameMap = this.GameMap; var maxIndex = rowIndexArray[0]; var len = rowIndexArray.length; for (var i = maxIndex - 1; i >= 0; i--) { for (var j = 0; j < this.HorizontalNum; j ) { if (gameMap[i][j][this.TypeEnum.blocks] != null) { this._resetMapElement(gameMap[i][j ][this.TypeEnum.blocks].domElement, len); gameMap[i len][j][this.TypeEnum.blocks] = gameMap[i][j][this.TypeEnum.blocks]; gameMap[i][j][this.TypeEnum.blocks] = null; } } } }, _resetMapElement: function(element, len) { //reset Set the dom element. For example, if there are two full rows, the above elements need to be dropped by two element.style.top = (parseInt(element.style.top) 28 * len) "px"; }, InitSpeed: function() { //Initialize game level var speedList = document.getElementById("speed"); if (speedList.options.length == 0) { for (var i = 0; i < this.Speeds.length; i ) { var varItem = new Option(i 1, this.Speeds[i]); speedList.options.add(varItem); } } this.SetSpeedSelected(); }, SetSpeedSelected: function() { //Selected level var speedList = document.getElementById("speed"); for (var i = 0; i < speedList.options.length; i ) { if (speedList.options[i].value == this.CurrentSpeed) { speedList.options[i ].selected = true; break; } } }, GameOver: function() { //Game over this.IsGameOver = true; this .PauseGame(); var element = document.getElementById("gameOver"); element.innerText = "Game Over!"; element.style.display = "block"; document .getElementById("btnStart").value = "try again"; }, PauseGame: function() { //Pause the game this.IsPlay = false; clearInterval(this.Timer ); }, CreateBlocks: function() { //Create block set var currentNum = this.BlocksEnum.length.getRandom(); var blocks = new Blocks(0, 0, this.BlocksStateNum[currentNum], currentNum, this.ColorEnum[currentNum]); blocks.Init(); if (this.BlocksObj.length == 3) Sys.BlocksObj.pop() ; Sys.BlocksObj.push(blocks); }, NaturalMove: function() { //Natural movement this.Timer = setInterval("Moving()", Sys.CurrentSpeed) ; } } function Base() { } //Define the base class Base.prototype.AddToMap = function(isAddToPreviewMap, isMoving) { //Add the block set to the map for (var i = 0; i < this.blocks.length; i ) { var element = null; if (!this.isInGameMap) { //If the block set is in the preview map element = document.createElement("DIV"); document.getElementById("PreviewMap").appendChild(element); this.blocksElement.push(element); this.blocks[i] .domElement = element; } else element = this.blocksElement[i]; if (!isAddToPreviewMap && !isMoving) //由预览map移动到游戏map时 document.getElementById("map").appendChild(element); element.style.position = "absolute"; element.style.left = ((this.x this.blocks[i].x) * 28) "px"; //设置元素所在的map的位置 element.style.top = ((this.y this.blocks[i].y) * 28) "px"; element.style.backgroundPositionX = this.color[0]; element.style.backgroundPositionY = this.color[1]; } } Base.prototype.UseGrid = function(map, blocksItem) { //方块集加入到游戏map中 for (var i = 0; i < blocksItem.blocks.length; i ) { var itemX = blocksItem.x blocksItem.blocks[i].x; var itemY = blocksItem.y blocksItem.blocks[i].y; if (blocksItem.y < 0) { Sys.GameOver(); return; } map[itemY][itemX] = {}; map[itemY][itemX][this.type] = blocksItem.blocks[i]; } } function Block(x, y) { //定义方块结构体 this.x = x; this.y = y; this.type = Sys.TypeEnum.block; this.domElement = null; } function Blocks(x, y, state, blocksEnum, colorEnum) { //方块集类 this.x = x; this.y = y; this.state = state; this.blocksEnum = blocksEnum; //方块类型(比如L,I,田字形,Z等) this.color = colorEnum; this.type = Sys.TypeEnum.blocks; //废弃属性 this.blocks = []; //方块集下的方块的集合 this.blocksElement = []; //方块集下的方块的对应dom元素集 this.currentState = 0; //当前的状态,比如L有四种类型的变形 this.isInGameMap = false; //是否在游戏map中 this.currentDirectionEnum = Sys.DirectionEnum.down; //默认方向向下 } Blocks.prototype = new Base(); //继承base类 Blocks.prototype.Init = function() {//初始化blocks var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum]; this.currentState = Sys.BlocksStateNum[this.blocksEnum].getRandom(); //随机获取方块集的状态 var blocksPos = blocksPoses[this.currentState]; //获取方块集的map for (var i = 0; i < blocksPos.length; i ) { for (var j = 0; j < blocksPos[i].length; j ) { var block = new Block(blocksPos[i][j][0], blocksPos[i][j][1]); this.blocks.push(block); } } var itemPos = Sys.GetBlocksInitPos(this); //获取初始位置,也就是说在预览map中的位置 this.x = itemPos.x; this.y = itemPos.y; this.AddToMap(true, false); //加入到预览map中 } Blocks.prototype.ChangeShape = function() {//方块变换形状 var gameMap = Sys.GameMap; var allowChangeShape = true; var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum]; var num = Sys.BlocksStateNum[this.blocksEnum]; var currentState1 = -1; this.currentState == num - 1 ? currentState1 = 0 : currentState1 = this.currentState 1; var blocksPos = blocksPoses[currentState1]; var k = 0; for (var i = 0; i < blocksPos.length; i ) { //主要是检测方块集的下一个变形是否合理 for (var j = 0; j < blocksPos[i].length; j ) { var block = this.blocks[k]; var itemX = this.x blocksPos[i][j][0]; var itemY = this.y blocksPos[i][j][1]; if ((itemX > Sys.HorizontalNum - 1) || (itemX < 0) || (itemY > Sys.VerticalNum - 1) || itemY >= 0 && gameMap[itemY][itemX] != null && gameMap[itemY][itemX][Sys.TypeEnum.blocks] != null) { allowChangeShape = false; break; } k ; } } if (allowChangeShape)//如果允许变形 { this.currentState == num - 1 ? this.currentState = 0 : this.currentState ; //设置下一个变形的状态 k = 0; for (var i = 0; i < blocksPos.length; i ) { for (var j = 0; j < blocksPos[i].length; j ) { var block = this.blocks[k]; block.x = blocksPos[i][j][0]; block.y = blocksPos[i][j][1]; k ; } } this.AddToMap(false, true); //变形后加入到游戏map中 } } Blocks.prototype.BlocksMoveDown = function(isMoving) { //方块集下落 this.currentDirectionEnum = Sys.DirectionEnum.down; if (!Sys.AllowBlocksMove()) { //如果不允许移动 Sys.AddToGameMapGrid(); //固定方块集在游戏map中的位置 Sys.GetScore(); //得分处理 Sys.GetNextActiviteBrocks(); //获取下一个方块集 } else { //下落一格 this.y ; this.AddToMap(false, isMoving); } } Number.prototype.getRandom = function() {//获取0至number之间的随机数 var num = this; var i = this 1; while (i >= num) { i = Math.round(Math.random() * 10); } return i; } Array.prototype.sorts = function() { return this.sort(compare); } //数组排序,按照升序排序 function compare(a, b) { return a - b; }//Define sorting rules Array.prototype.removeAt = function(dx) { //Clear the array element at the specified index if (isNaN(dx) || dx > this.length) { return false; } for (var i = 0, n = 0; i < this.length; i ) { if (this[i] != this[dx]) this[n ] = this[ i]; } this.length -= 1; } Array.prototype.filter = function() { //Clear duplicate values in the array var arr = [] ; for (var i = 0; i < this.length; i ) { if (!arr.contains(this[i])) arr.push(this[i]); } return arr; } Array.prototype.contains = function(item) { //Detect whether the array contains an element for (var i = 0; i < this .length; i ) { if (this[i] == item) return true; } return false; } Function.prototype.delay = function( time) { var timer = setTimeout(this, time); } //The function delays time millisecond execution window.onload = InitGame; function InitGame() {//Initialize the game Sys = new sys( ); Sys.BlocksObj = []; Sys.InitSpeed(); //Initialize game speed Sys.CreateGameMap(); //Create game map Sys.CreateBlocks(); /Create block set } function GameStart(element) { if (element.value == "start") { //Start the game element.value = "pause"; Sys.PlayGame(); Sys.IsFirstPlay = false; } else if (element.value == "pause") { //Pause the game element.value = "start" Sys.PauseGame(); } else { //Restart after the game is over window.location.reload(); } } function Moving() {//Move Sys.GetBlocks().BlocksMoveDown(false); } function ChangeSpeed(e) {//Switch level var speedlist = document.getElementById("speed"); Sys.CurrentSpeed = speedlist.options[speedlist.selectedIndex].value; if (!Sys.IsGameOver) { clearInterval(Sys.Timer); this.NaturalMove(); } } function keyDown(e) { //Key operation if (Sys.IsGameOver || !Sys.IsPlay) return; var blocks = Sys.GetBlocks(); if (e.keyCode == 37) { //To the left blocks.currentDirectionEnum = Sys.DirectionEnum.left; if (Sys.AllowBlocksMove()) blocks.x--; if (blocks.x != 0) blocks.AddToMap(false, true); } else if (e.keyCode == 38) { //Up blocks.currentDirectionEnum = Sys.DirectionEnum.up; blocks.ChangeShape(); } else if (e.keyCode == 39) { //Right blocks.currentDirectionEnum = Sys.DirectionEnum.right; var oldX = blocks.x; if (Sys.AllowBlocksMove()) blocks.x ; if (blocks.x != oldX) blocks.AddToMap(false, true ); } else if (e.keyCode == 40) //Down { blocks.currentDirectionEnum = Sys.DirectionEnum.down; blocks.BlocksMoveDown(true); } }
< div id="mask" class="mask">
Score: 0
Level:
All rights reserved
This Tetris was developed by Gaoshan Liushui. Everyone is welcome to use it. If you have any bugs or good suggestions, please leave me a message. Thank you for your support! If you need to reprint, please indicate the source!
By the way, let me promote my MVC qq group: 45660795, welcome to join! < br />
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn