下面是俄罗斯方块的截图:
请到这里下载源码:
下面是代码:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" >
3 <head>
4 <title></title>
5 <script type="text/javascript">
6 /********************************************js俄罗斯方块源码***********************************************/
7 //作者:高山流水 QQ:21243468
8 //创建日期:2009-08-06
9 //版权声明:该作品由高山流水创作,转载请注明出处,谢谢合作!
10 //游戏设计说明:
11 //1.由于该游戏属于二维游戏,所以布置好网格是写好该游戏的关键,无论是游戏窗口还是预览窗口、
12 //方块集虚拟map等都用到了网格的概念,这样做的好处可以避免频繁的获取元素的位置,另外还可以对方块集的移动
13 //进行精确的定位和变形。这里比较重要的一点就是提前定义好方块集的map,比如对于L方块集,应定义一个三乘三的正方形
14 //网格,然后根据L的每一个形状,确定方块集中的每个方块在正方形网格中的位置,另外还需要保存方块集在map
15 //(游戏窗口或者预览窗)中的位置,这样任意时刻,通过方块集的位置,和方块在正方形网格中的位置,就可以确定
16 //方块集中的每一个方块在map中的位置。
17 //2.该游戏主要用到了一些OOP的思想。比如定义一个基类base,方块集类继承自base类,其中还有对象的封装,属性,
18 //枚举的定义等,当然还有事件,委托,attribute,垃圾收集器等,因为时间关系,就不做代码演示了,各位有兴趣可以自己
19 //扩展一下,加深对js OOP的理解。
20 //3.js内置对象的扩展:比如Array.prototype.removeAt等
21 /********************************************js俄罗斯方块源码**********************************************/
22 var Sys = null;
23 function sys() { }
24 sys.prototype = {
25 GameMap: [],
26 PreviewMap: [],
27 BlocksObj: [],
28 Timer: null,
29 HorizontalNum: 10, //游戏map的水平格数
30 VerticalNum: 18, //游戏map的竖直格数
31 IsGameOver: false, //判断游戏是否结束
32 ScoreStrategy: [100, 300, 500, 800], //得分策略
33 LevelScores: [100, 20000, 40000, 60000, 80000, 100000, 120000, 140000, 160000, 200000], //分数等级
34 IsPlay: false, //游戏进行中
35 IsFirstPlay: true, //是否第一次玩
36 SmallGridNum: 6, //预览map的格子数,为正方形
37 DirectionEnum: { left: 0, right: 1, up: 2, down: 3 }, //方向的枚举
38 Speeds: [1000, 900, 800, 700, 600, 500, 400, 300, 200, 100], //速度,或者说级别
39 CurrentSpeed: 1000, //当前级别或者说速度
40 TypeEnum: { none: 0, block: 1, blocks: 2 }, //类型
41 BlocksEnum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], //0为LL,1为LR,2为T,3为ZL,4为ZR,5为I,6为F,7为长T
42 BlocksStateNum: [4, 4, 4, 2, 2, 2, 1, 4, 4, 2], //对应BlocksEnum中每个方块集的变形个数,即有多少种变形
43 BlocksShapeMaps: [ //方块形状集的集合
44 [ //方块集的虚拟map集合,对应BlocksEnum,比如该map是L的map
45 [[[2, 1]], [[0, 2], [1, 2], [2, 2]]], //L中的其中一个变形
46 [[[1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
47 [[[0, 1], [1, 1], [2, 1]], [[0, 2]]],
48 [[[1, 0], [2, 0]], [[2, 1]], [[2, 2]]]
49 ],
50 [
51 [[[2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
52 [[[0, 1]], [[0, 2], [1, 2]], [[2, 2]]],
53 [[[0, 0], [1, 0]], [[0, 1]], [[0, 2]]],
54 [[[0, 1], [1, 1], [2, 1]], [[2, 2]]]
55 ],
56 [
57 [[[0, 0], [1, 0], [2, 0]], [[1, 1]]],
58 [[[1, 0]], [[0, 1], [1, 1]], [[1, 2]]],
59 [[[1, 1]], [[0, 2], [1, 2], [2, 2]]],
60 [[[0, 0]], [[0, 1], [1, 1]], [[0, 2]]]
61 ],
62 [
63 [[[0, 0]], [[0, 1], [1, 1]], [[1, 2]]],
64 [[[1, 1], [2, 1]], [[0, 2], [1, 2]]]
65 ],
66 [
67 [[[1, 0]], [[0, 1], [1, 1]], [[0, 2]]],
68 [[[0, 1], [1, 1]], [[1, 2], [2, 2]]]
69 ],
70 [
71 [[[0, 0]], [[0, 1]], [[0, 2]], [[0, 3]]],
72 [[[0, 3]], [[1, 3]], [[2, 3]], [[3, 3]]]
73 ],
74 [
75 [[[0, 0], [0, 1]], [[1, 0], [1, 1]]]
76 ],
77 [
78 [[[0, 0], [1, 0], [2, 0]], [[1, 1]], [[1, 2]]],
79 [[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[2, 2]]],
80 [[[1, 0]], [[1, 1]], [[0, 2], [1, 2], [2, 2]]],
81 [[[0, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
82 ],
83 [
84 [[[0, 1], [1, 1], [2, 1]], [[0, 2]], [[2, 2]]],
85 [[[1, 0], [2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
86 [[[0, 1], [2, 1]], [[0, 2], [1, 2], [2, 2]]],
87 [[[1, 0], [2, 0]], [[1, 1]], [[1, 2], [2, 2]]]
88 ],
89 [
90 [[[0, 0], [1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
91 [[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
92 ],
93 ],
94 ColorEnum: [[0, 0], [-28, 0], [-56, 0], [-84, 0], [-112, 0], [-140, 0], [-168, 0], [0, 0], [-28, 0], [-56, 0]], //颜色的枚举,对应BlocksEnum
95 CreateGameMap: function() { //创建游戏map
96 for (var i = 0; i < this.VerticalNum; i++) {
97 this.GameMap.push([]);
98 for (var j = 0; j < this.HorizontalNum; j++) {
99 this.GameMap[i][j] = {};
100 this.GameMap[i][j][Sys.TypeEnum.blocks] = null;
101 }
102 }
103 },
104 GetBlocks: function() { //获取GameMap里的方块集
105 for (var i = 0; i < this.BlocksObj.length; i++) {
106 if (this.BlocksObj[i].isInGameMap) {
107 return this.BlocksObj[i];
108 }
109 }
110 return null;
111 },
112 AllowBlocksMove: function() { //是否允许方块集移动
113 var blocksItem = this.GetBlocks();
114 var itemPosArray = this._getBlocksItemPosArray(blocksItem, false);
115 return this.NoBlocksInthePlace(itemPosArray, blocksItem) && this.CheckBoundary(itemPosArray, blocksItem);
116 },
117 GetMaxAndMinItemPosArray: function(itemPosArray) { //获取最大最小方块位置集合
118 itemPosArray.ItemPosXArray.sorts();
119 itemPosArray.ItemPosYArray.sorts();
120 return { maxItemPosX: itemPosArray.ItemPosXArray[itemPosArray.ItemPosXArray.length - 1], maxItemPosY: itemPosArray.ItemPosYArray[itemPosArray.ItemPosYArray.length - 1], minItemPosX: itemPosArray.ItemPosXArray[0], minItemPosY: itemPosArray.ItemPosYArray[0] };
121 },
122 NoBlocksInthePlace: function(itemPosArray, blocksItem) { //检测游戏方格中是否已经有方块
123 return this._isOverMapChild(itemPosArray, blocksItem) ? false : true;
124 },
125 CheckBoundary: function(itemPosArray, blocksItem) { //是否到达边界了
126 var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray);
127 var isNotToBoundary = false;
128 switch (blocksItem.currentDirectionEnum) {
129 case this.DirectionEnum.left:
130 isNotToBoundary = (maxAndMinItemPosArray.minItemPosX > 0)
131 break;
132 case this.DirectionEnum.right:
133 isNotToBoundary = (maxAndMinItemPosArray.maxItemPosX < this.HorizontalNum - 1)
134 break;
135 case this.DirectionEnum.down:
136 isNotToBoundary = (maxAndMinItemPosArray.maxItemPosY < this.VerticalNum - 1);
137 break;
138 }
139 return isNotToBoundary;
140 },
141 _isOverMapChild: function(itemPosArray, blocksItem) { //检测是否会覆盖某个游戏方格中的元素
142 var isOverMapChild = false;
143 for (var i = 0; i < itemPosArray.ItemPosYArray.length; i++) {
144 var itemX = itemPosArray.ItemPosXArray[i];
145 var itemY = itemPosArray.ItemPosYArray[i];
146 if (blocksItem.currentDirectionEnum == this.DirectionEnum.left) {
147 itemX--;
148 }
149 else if (blocksItem.currentDirectionEnum == this.DirectionEnum.right) {
150 itemX++;
151 }
152 else if (blocksItem.currentDirectionEnum == this.DirectionEnum.down) {
153 itemY++;
154 }
155 if (this.GameMap[itemY] && this.GameMap[itemY][itemX] && this.GameMap[itemY][itemX][this.TypeEnum.blocks] != null) {
156 isOverMapChild = true;
157 break;
158 }
159 }
160 return isOverMapChild;
161 },
162 _getBlocksItemPosArray: function(blocksItem, isRelative) { //获取方块集的位置集合,isRelative=true获取的是方块相对方块集map的位置,否则是方块相对游戏map的位置
163 var itemPosXArray = [];
164 var itemPosYArray = [];
165 for (var i = 0; i < blocksItem.blocks.length; i++) {
166 if (isRelative) {
167 itemPosXArray.push(blocksItem.blocks[i].x);
168 itemPosYArray.push(blocksItem.blocks[i].y);
169 }
170 else {
171 itemPosXArray.push(blocksItem.blocks[i].x + blocksItem.x);
172 itemPosYArray.push(blocksItem.blocks[i].y + blocksItem.y);
173 }
174 }
175 return { ItemPosXArray: itemPosXArray, ItemPosYArray: itemPosYArray };
176 },
177 GetBlocksInitPos: function(blocks) { //获取方块的初始位置
178 var blocksItem = null;
179 if (!blocks)
180 blocksItem = this.GetBlocks();
181 else
182 blocksItem = blocks;
183 var itemPosArray = this._getBlocksItemPosArray(blocksItem, true);
184 itemPosArray.ItemPosXArray = itemPosArray.ItemPosXArray.filter();
185 itemPosArray.ItemPosYArray = itemPosArray.ItemPosYArray.filter();
186 var childsNumX = itemPosArray.ItemPosXArray.length;
187 var childsNumY = itemPosArray.ItemPosYArray.length;
188 var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray);
189 if (blocks) //获取方块集在预览map中的初始位置
190 return { x: (this.SmallGridNum - childsNumX - 1) / 2 + 0.5 - maxAndMinItemPosArray.minItemPosX, y: (this.SmallGridNum - childsNumY - 1) / 2 + 0.5 - maxAndMinItemPosArray.minItemPosY };
191 else //获取方块集在游戏map中的初始位置
192 return { x: parseInt((this.HorizontalNum - childsNumX - 1) / 2) + 1 - maxAndMinItemPosArray.minItemPosX, y: -(childsNumY + maxAndMinItemPosArray.minItemPosY) };
193 },
194 GetNextActiviteBrocks: function() { //获取下一个活动的方块集
195 for (var i = 0; i < this.BlocksObj.length; i++) {
196 if (this.BlocksObj[i].isInGameMap) {
197 this.BlocksObj.removeAt(i);
198 }
199 }
200 this.BlocksObj[0].isInGameMap = true;
201 var itemPos = this.GetBlocksInitPos();
202 this.BlocksObj[0].x = itemPos.x;
203 this.BlocksObj[0].y = itemPos.y;
204 this.BlocksObj[0].AddToMap(false, false);
205 this.CreateBlocks();
206 },
207 PlayGame: function() { //启动游戏
208 this.IsPlay = true;
209 this.NaturalMove();
210 if (!this.IsFirstPlay) {
211 return;
212 }
213 this.GetNextActiviteBrocks();
214 },
215 AddToGameMapGrid: function() { //加入到游戏map网格中
216 var blocks = this.GetBlocks();
217 blocks.UseGrid(this.GameMap, blocks);
218 },
219 GetScore: function() { //分数处理
220 var rowIndexArray = [];
221 for (var i = 0; i < this.VerticalNum; i++) { //获取满行的行数
222 var entireRow = true;
223 for (var j = 0; j < this.HorizontalNum; j++) {
224 if (this.GameMap[i][j][this.TypeEnum.blocks] == null) {
225 entireRow = false;
226 break;
227 }
228 }
229 if (entireRow)
230 rowIndexArray.push(i);
231 }
232 if (rowIndexArray.length > 0) {
233 this._FreeMapGrid(rowIndexArray);
234 document.getElementById("score").innerText = this.ScoreStrategy[rowIndexArray.length - 1] + parseInt(document.getElementById("score").innerText);
235 this.CheckTheLevel();
236 }
237 },
238 CheckTheLevel: function() { //检测是否进入下一个级别
239 var currentScore = parseInt(document.getElementById("score").innerText);
240 var speedList = document.getElementById("speed");
241 var currentLevel = parseInt(speedList.options[speedList.selectedIndex].text) - 1;
242 var levelScore = this.LevelScores[currentLevel];
243 if (currentScore >= levelScore) {
244 if (currentLevel < this.LevelScores.length) {
245 var element = document.getElementById("gameOver");
246 element.innerText = "恭喜你通过第" + (speedList.selectedIndex + 1) + "关";
247 element.style.display = "block";
248 this.PauseGame();
249 document.getElementById("btnStart").disabled = true;
250 document.getElementById("speed").disabled = true;
251 this._goToNextLevel.delay(3000);
252 }
253 else {
254 this._finishAllTheLevel(element);
255 }
256 }
257 },
258 _goToNextLevel: function() { //进入下一个级别,速度加快
259 Sys.IsPlay = true;
260 document.getElementById("btnStart").disabled = false;
261 var speedList = document.getElementById("speed");
262 speedList.disabled = false;
263 speedList.options[speedList.selectedIndex + 1].selected = true;
264 Sys.CurrentSpeed = Sys.Speeds[speedList.selectedIndex + 1];
265 Sys.NaturalMove();
266 document.getElementById("gameOver").style.display = "none";
267 },
268 _finishAllTheLevel: function() { //完成所有的游戏级别
269 this.PauseGame();
270 },
271 _FreeMapGrid: function(rowIndexArray) { //从游戏map中释放满行的网格
272 var gameMap = this.GameMap;
273 var startIndex = rowIndexArray[0];
274 var len = rowIndexArray.length;
275 var maxIndex = startIndex + len - 1;
276 for (var i = startIndex; i <= maxIndex; i++) {
277 for (var j = 0; j < this.HorizontalNum; j++) {
278 if (gameMap[i][j][this.TypeEnum.blocks] != null) {
279 document.getElementById("map").removeChild(gameMap[i][j][this.TypeEnum.blocks].domElement);
280 gameMap[i][j][this.TypeEnum.blocks] = null;
281 }
282 }
283 }
284 this.ResetMapGrid(rowIndexArray);
285 },
286 ResetMapGrid: function(rowIndexArray) { //重置游戏网格
287 var gameMap = this.GameMap;
288 var maxIndex = rowIndexArray[0];
289 var len = rowIndexArray.length;
290 for (var i = maxIndex - 1; i >= 0; i--) {
291 for (var j = 0; j < this.HorizontalNum; j++) {
292 if (gameMap[i][j][this.TypeEnum.blocks] != null) {
293 this._resetMapElement(gameMap[i][j][this.TypeEnum.blocks].domElement, len);
294 gameMap[i + len][j][this.TypeEnum.blocks] = gameMap[i][j][this.TypeEnum.blocks];
295 gameMap[i][j][this.TypeEnum.blocks] = null;
296 }
297 }
298 }
299 },
300 _resetMapElement: function(element, len) { //重置dom元素,比如共有满行两行,则之上的元素均需下降两个
301 element.style.top = (parseInt(element.style.top) + 28 * len) + "px";
302 },
303 InitSpeed: function() { //初始化游戏级别
304 var speedList = document.getElementById("speed");
305 if (speedList.options.length == 0) {
306 for (var i = 0; i < this.Speeds.length; i++) {
307 var varItem = new Option(i + 1, this.Speeds[i]);
308 speedList.options.add(varItem);
309 }
310 }
311 this.SetSpeedSelected();
312 },
313 SetSpeedSelected: function() { //选中级别
314 var speedList = document.getElementById("speed");
315 for (var i = 0; i < speedList.options.length; i++) {
316 if (speedList.options[i].value == this.CurrentSpeed) {
317 speedList.options[i].selected = true;
318 break;
319 }
320 }
321 },
322 GameOver: function() { //游戏结束
323 this.IsGameOver = true;
324 this.PauseGame();
325 var element = document.getElementById("gameOver");
326 element.innerText = "Game Over!";
327 element.style.display = "block";
328 document.getElementById("btnStart").value = "try again";
329 },
330 PauseGame: function() { //暂停游戏
331 this.IsPlay = false;
332 clearInterval(this.Timer);
333 },
334 CreateBlocks: function() { //创建方块集
335 var currentNum = this.BlocksEnum.length.getRandom();
336 var blocks = new Blocks(0, 0, this.BlocksStateNum[currentNum], currentNum, this.ColorEnum[currentNum]);
337 blocks.Init();
338 if (this.BlocksObj.length == 3)
339 Sys.BlocksObj.pop();
340 Sys.BlocksObj.push(blocks);
341 },
342 NaturalMove: function() { //自然下落
343 this.Timer = setInterval("Moving()", Sys.CurrentSpeed);
344 }
345 }
346 function Base() { } //定义base类
347 Base.prototype.AddToMap = function(isAddToPreviewMap, isMoving) { //添加方块集到map中
348 for (var i = 0; i < this.blocks.length; i++) {
349 var element = null;
350 if (!this.isInGameMap) { //如果方块集是在预览map中
351 element = document.createElement("DIV");
352 document.getElementById("PreviewMap").appendChild(element);
353 this.blocksElement.push(element);
354 this.blocks[i].domElement = element;
355 }
356 else
357 element = this.blocksElement[i];
358 if (!isAddToPreviewMap && !isMoving) //由预览map移动到游戏map时
359 document.getElementById("map").appendChild(element);
360 element.style.position = "absolute";
361 element.style.left = ((this.x + this.blocks[i].x) * 28) + "px"; //设置元素所在的map的位置
362 element.style.top = ((this.y + this.blocks[i].y) * 28) + "px";
363 element.style.backgroundPositionX = this.color[0];
364 element.style.backgroundPositionY = this.color[1];
365 }
366 }
367 Base.prototype.UseGrid = function(map, blocksItem) { //方块集加入到游戏map中
368 for (var i = 0; i < blocksItem.blocks.length; i++) {
369 var itemX = blocksItem.x + blocksItem.blocks[i].x;
370 var itemY = blocksItem.y + blocksItem.blocks[i].y;
371 if (blocksItem.y < 0) {
372 Sys.GameOver();
373 return;
374 }
375 map[itemY][itemX] = {};
376 map[itemY][itemX][this.type] = blocksItem.blocks[i];
377 }
378 }
379 function Block(x, y) { //定义方块结构体
380 this.x = x;
381 this.y = y;
382 this.type = Sys.TypeEnum.block;
383 this.domElement = null;
384 }
385 function Blocks(x, y, state, blocksEnum, colorEnum) { //方块集类
386 this.x = x;
387 this.y = y;
388 this.state = state;
389 this.blocksEnum = blocksEnum; //方块类型(比如L,I,田字形,Z等)
390 this.color = colorEnum;
391 this.type = Sys.TypeEnum.blocks; //废弃属性
392 this.blocks = []; //方块集下的方块的集合
393 this.blocksElement = []; //方块集下的方块的对应dom元素集
394 this.currentState = 0; //当前的状态,比如L有四种类型的变形
395 this.isInGameMap = false; //是否在游戏map中
396 this.currentDirectionEnum = Sys.DirectionEnum.down; //默认方向向下
397 }
398 Blocks.prototype = new Base(); //继承base类
399 Blocks.prototype.Init = function() {//初始化blocks
400 var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum];
401 this.currentState = Sys.BlocksStateNum[this.blocksEnum].getRandom(); //随机获取方块集的状态
402 var blocksPos = blocksPoses[this.currentState]; //获取方块集的map
403 for (var i = 0; i < blocksPos.length; i++) {
404 for (var j = 0; j < blocksPos[i].length; j++) {
405 var block = new Block(blocksPos[i][j][0], blocksPos[i][j][1]);
406 this.blocks.push(block);
407 }
408 }
409 var itemPos = Sys.GetBlocksInitPos(this); //获取初始位置,也就是说在预览map中的位置
410 this.x = itemPos.x;
411 this.y = itemPos.y;
412 this.AddToMap(true, false); //加入到预览map中
413 }
414 Blocks.prototype.ChangeShape = function() {//方块变换形状
415 var gameMap = Sys.GameMap;
416 var allowChangeShape = true;
417 var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum];
418 var num = Sys.BlocksStateNum[this.blocksEnum];
419 var currentState1 = -1;
420 this.currentState == num - 1 ? currentState1 = 0 : currentState1 = this.currentState + 1;
421 var blocksPos = blocksPoses[currentState1];
422 var k = 0;
423 for (var i = 0; i < blocksPos.length; i++) { //主要是检测方块集的下一个变形是否合理
424 for (var j = 0; j < blocksPos[i].length; j++) {
425 var block = this.blocks[k];
426 var itemX = this.x + blocksPos[i][j][0];
427 var itemY = this.y + blocksPos[i][j][1];
428 if ((itemX > Sys.HorizontalNum - 1) || (itemX < 0) || (itemY > Sys.VerticalNum - 1) || itemY >= 0 && gameMap[itemY][itemX] != null && gameMap[itemY][itemX][Sys.TypeEnum.blocks] != null) {
429 allowChangeShape = false;
430 break;
431 }
432 k++;
433 }
434 }
435 if (allowChangeShape)//如果允许变形
436 {
437 this.currentState == num - 1 ? this.currentState = 0 : this.currentState++; //设置下一个变形的状态
438 k = 0;
439 for (var i = 0; i < blocksPos.length; i++) {
440 for (var j = 0; j < blocksPos[i].length; j++) {
441 var block = this.blocks[k];
442 block.x = blocksPos[i][j][0];
443 block.y = blocksPos[i][j][1];
444 k++;
445 }
446 }
447 this.AddToMap(false, true); //变形后加入到游戏map中
448 }
449 }
450 Blocks.prototype.BlocksMoveDown = function(isMoving) { //方块集下落
451 this.currentDirectionEnum = Sys.DirectionEnum.down;
452 if (!Sys.AllowBlocksMove()) { //如果不允许移动
453 Sys.AddToGameMapGrid(); //固定方块集在游戏map中的位置
454 Sys.GetScore(); //得分处理
455 Sys.GetNextActiviteBrocks(); //获取下一个方块集
456 }
457 else { //下落一格
458 this.y++;
459 this.AddToMap(false, isMoving);
460 }
461 }
462 Number.prototype.getRandom = function() {//获取0至number之间的随机数
463 var num = this;
464 var i = this + 1;
465 while (i >= num) {
466 i = Math.round(Math.random() * 10);
467 }
468 return i;
469 }
470 Array.prototype.sorts = function() { return this.sort(compare); } //数组排序,按照升序排序
471 function compare(a, b) { return a - b; } //定义排序规则
472 Array.prototype.removeAt = function(dx) { //清除指定索引的数组元素
473 if (isNaN(dx) || dx > this.length) { return false; }
474 for (var i = 0, n = 0; i < this.length; i++) {
475 if (this[i] != this[dx])
476 this[n++] = this[i];
477 }
478 this.length -= 1;
479 }
480 Array.prototype.filter = function() { //清除数组中的重复值
481 var arr = [];
482 for (var i = 0; i < this.length; i++) {
483 if (!arr.contains(this[i]))
484 arr.push(this[i]);
485 }
486 return arr;
487 }
488 Array.prototype.contains = function(item) { //检测数组是否包含某元素
489 for (var i = 0; i < this.length; i++) {
490 if (this[i] == item)
491 return true;
492 }
493 return false;
494 }
495 Function.prototype.delay = function(time) { var timer = setTimeout(this, time); } //函数延迟time毫秒执行
496 window.onload = InitGame;
497 function InitGame() {//初始化游戏
498 Sys = new sys();
499 Sys.BlocksObj = [];
500 Sys.InitSpeed(); //初始化游戏速度
501 Sys.CreateGameMap(); //创建游戏map
502 Sys.CreateBlocks(); //创建方块集
503 }
504 function GameStart(element) {
505 if (element.value == "start") { //开始游戏
506 element.value = "pause";
507 Sys.PlayGame();
508 Sys.IsFirstPlay = false;
509 }
510 else if (element.value == "pause") { //暂停游戏
511 element.value = "start"
512 Sys.PauseGame();
513 }
514 else { //游戏结束后重新开始
515 window.location.reload();
516 }
517 }
518 function Moving() {//移动
519 Sys.GetBlocks().BlocksMoveDown(false);
520 }
521 function ChangeSpeed(e) {//切换级别
522 var speedlist = document.getElementById("speed");
523 Sys.CurrentSpeed = speedlist.options[speedlist.selectedIndex].value;
524 if (!Sys.IsGameOver) {
525 clearInterval(Sys.Timer);
526 this.NaturalMove();
527 }
528 }
529 function keyDown(e) { //按键操作
530 if (Sys.IsGameOver || !Sys.IsPlay) return;
531 var blocks = Sys.GetBlocks();
532 if (e.keyCode == 37) { //向左
533 blocks.currentDirectionEnum = Sys.DirectionEnum.left;
534 if (Sys.AllowBlocksMove())
535 blocks.x--;
536 if (blocks.x != 0)
537 blocks.AddToMap(false, true);
538 }
539 else if (e.keyCode == 38) { //向上
540 blocks.currentDirectionEnum = Sys.DirectionEnum.up;
541 blocks.ChangeShape();
542 }
543 else if (e.keyCode == 39) { //向右
544 blocks.currentDirectionEnum = Sys.DirectionEnum.right;
545 var oldX = blocks.x;
546 if (Sys.AllowBlocksMove())
547 blocks.x++;
548 if (blocks.x != oldX)
549 blocks.AddToMap(false, true);
550 }
551 else if (e.keyCode == 40) //向下
552 {
553 blocks.currentDirectionEnum = Sys.DirectionEnum.down;
554 blocks.BlocksMoveDown(true);
555 }
556 }
557 </script>
558 <style type="text/css">
559 body
560 {
561 background-color:#ffffff;
562 overflow:hidden;
563 font-size:14px;
564 }
565 .gameZone
566 {
567 position:absolute;
568 left:0px;
569 top:0px;
570 width:100%;
571 height:550px;
572 background-Color:white;
573 }
574 .mask
575 {
576 position:absolute;
577 left:100px;
578 top:0px;
579 width:300px;
580 height:20px;
581 background-color:White;
582 border:solid 0px;
583 z-index:5;
584 }
585 .map
586 {
587 position:absolute;
588 left:100px;
589 top:20px;
590 width:280px;
591 height:504px;
592 background-image:url(images/tetris_grid.gif);
593 border:solid 3px green;
594 }
595 .gameOver
596 {
597 position:absolute;
598 left:100px;
599 top:20px;
600 width:280px;
601 height:504px;
602 font-weight:800;
603 font-size:xx-large;
604 color:Red;
605 text-align:center;
606 border:solid 3px;
607 line-height:420px;
608 display:none;
609 filter: Alpha(Opacity=80);
610 background-color:pink;
611 }
612 .map div
613 {
614 BACKGROUND-IMAGE: url(images/tetris.gif);
615 WIDTH: 28px;
616 BACKGROUND-REPEAT: no-repeat;
617 POSITION: absolute;
618 HEIGHT: 28px
619 }
620 .PreviewMap
621 {
622 position:absolute;
623 left:400px;
624 top:20px;
625 width:168px;
626 height:168px;
627 background-color:pink;
628 border:solid 2px green;
629 }
630 .PreviewMap div
631 {
632 BACKGROUND-IMAGE: url(images/tetris.gif);
633 WIDTH: 28px;
634 BACKGROUND-REPEAT: no-repeat;
635 POSITION: absolute;
636 HEIGHT: 28px
637 }
638 .start
639 {
640 position:absolute;
641 left:400px;
642 top:240px;
643 width:168px;
644 height:40px;
645 }
646 .scoreSpeed
647 {
648 position:absolute;
649 left:400px;
650 top:200px;
651 width:190px;
652 height:40px;
653 }
654 .score
655 {
656 color:pink;
657 font-weight:bold;
658 width:20px;
659 height:20px;
660 background-color:blue;
661 padding-left:10px;
662 padding-right:10px;
663 font-size:medium;
664 }
665 .speed
666 {
667 color:pink;
668 font-weight:bold;
669 width:20px;
670 height:20px;
671 background-color:blue;
672 padding-left:5px;
673 padding-right:5px;
674 font-size:medium;
675 }
676 .copyright
677 {
678 position:absolute;
679 left:400px;
680 top:280px;
681 word-break:break-all;
682 width:160px;
683 height:225px;
684 border:solid 2px green;
685 padding:5px;
686 }
687 </style>
688 </head>
689 <body onkeydown="keyDown(event)">
690 <div class="gameZone">
691 <div id="mask" class="mask"></div>
692 <div id="map" class="map"></div>
693 <div id="gameOver" class="gameOver"></div>
694 <div id="PreviewMap" class="PreviewMap"></div>
695 <div id="scoreSpeed" class="scoreSpeed">得分:<span id="score" class="score">0</span></div>
696 <div id="start" class="start">
697 <input type="button" id="btnStart" value="start" onclick="GameStart(this);" />
698 级别:<select id="speed" onchange="ChangeSpeed();"></select>
699 </div>
700 <div id="copyright" class="copyright">
701 <b><center>版权所有</center></b><br />
702 此俄罗斯方块由高山流水开发,欢迎各位使用,
703 如有bug或者好的意见,请给我留言,谢谢支持!
704 另如需转载,请注明出处!<br />
705 <font color=red><b>顺便,宣传一下我的MVC qq群:45660795,欢迎加入!</b></font>
706 <br /><br />
707 作者:<a href="http://www.cnblogs.com/JackFeng/" target="_blank">高山流水</a><br />
708 QQ:21243468
709 </div>
710 </div>
711 </body>
712 </html>
2 <html xmlns="http://www.w3.org/1999/xhtml" >
3 <head>
4 <title></title>
5 <script type="text/javascript">
6 /********************************************js俄罗斯方块源码***********************************************/
7 //作者:高山流水 QQ:21243468
8 //创建日期:2009-08-06
9 //版权声明:该作品由高山流水创作,转载请注明出处,谢谢合作!
10 //游戏设计说明:
11 //1.由于该游戏属于二维游戏,所以布置好网格是写好该游戏的关键,无论是游戏窗口还是预览窗口、
12 //方块集虚拟map等都用到了网格的概念,这样做的好处可以避免频繁的获取元素的位置,另外还可以对方块集的移动
13 //进行精确的定位和变形。这里比较重要的一点就是提前定义好方块集的map,比如对于L方块集,应定义一个三乘三的正方形
14 //网格,然后根据L的每一个形状,确定方块集中的每个方块在正方形网格中的位置,另外还需要保存方块集在map
15 //(游戏窗口或者预览窗)中的位置,这样任意时刻,通过方块集的位置,和方块在正方形网格中的位置,就可以确定
16 //方块集中的每一个方块在map中的位置。
17 //2.该游戏主要用到了一些OOP的思想。比如定义一个基类base,方块集类继承自base类,其中还有对象的封装,属性,
18 //枚举的定义等,当然还有事件,委托,attribute,垃圾收集器等,因为时间关系,就不做代码演示了,各位有兴趣可以自己
19 //扩展一下,加深对js OOP的理解。
20 //3.js内置对象的扩展:比如Array.prototype.removeAt等
21 /********************************************js俄罗斯方块源码**********************************************/
22 var Sys = null;
23 function sys() { }
24 sys.prototype = {
25 GameMap: [],
26 PreviewMap: [],
27 BlocksObj: [],
28 Timer: null,
29 HorizontalNum: 10, //游戏map的水平格数
30 VerticalNum: 18, //游戏map的竖直格数
31 IsGameOver: false, //判断游戏是否结束
32 ScoreStrategy: [100, 300, 500, 800], //得分策略
33 LevelScores: [100, 20000, 40000, 60000, 80000, 100000, 120000, 140000, 160000, 200000], //分数等级
34 IsPlay: false, //游戏进行中
35 IsFirstPlay: true, //是否第一次玩
36 SmallGridNum: 6, //预览map的格子数,为正方形
37 DirectionEnum: { left: 0, right: 1, up: 2, down: 3 }, //方向的枚举
38 Speeds: [1000, 900, 800, 700, 600, 500, 400, 300, 200, 100], //速度,或者说级别
39 CurrentSpeed: 1000, //当前级别或者说速度
40 TypeEnum: { none: 0, block: 1, blocks: 2 }, //类型
41 BlocksEnum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], //0为LL,1为LR,2为T,3为ZL,4为ZR,5为I,6为F,7为长T
42 BlocksStateNum: [4, 4, 4, 2, 2, 2, 1, 4, 4, 2], //对应BlocksEnum中每个方块集的变形个数,即有多少种变形
43 BlocksShapeMaps: [ //方块形状集的集合
44 [ //方块集的虚拟map集合,对应BlocksEnum,比如该map是L的map
45 [[[2, 1]], [[0, 2], [1, 2], [2, 2]]], //L中的其中一个变形
46 [[[1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
47 [[[0, 1], [1, 1], [2, 1]], [[0, 2]]],
48 [[[1, 0], [2, 0]], [[2, 1]], [[2, 2]]]
49 ],
50 [
51 [[[2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
52 [[[0, 1]], [[0, 2], [1, 2]], [[2, 2]]],
53 [[[0, 0], [1, 0]], [[0, 1]], [[0, 2]]],
54 [[[0, 1], [1, 1], [2, 1]], [[2, 2]]]
55 ],
56 [
57 [[[0, 0], [1, 0], [2, 0]], [[1, 1]]],
58 [[[1, 0]], [[0, 1], [1, 1]], [[1, 2]]],
59 [[[1, 1]], [[0, 2], [1, 2], [2, 2]]],
60 [[[0, 0]], [[0, 1], [1, 1]], [[0, 2]]]
61 ],
62 [
63 [[[0, 0]], [[0, 1], [1, 1]], [[1, 2]]],
64 [[[1, 1], [2, 1]], [[0, 2], [1, 2]]]
65 ],
66 [
67 [[[1, 0]], [[0, 1], [1, 1]], [[0, 2]]],
68 [[[0, 1], [1, 1]], [[1, 2], [2, 2]]]
69 ],
70 [
71 [[[0, 0]], [[0, 1]], [[0, 2]], [[0, 3]]],
72 [[[0, 3]], [[1, 3]], [[2, 3]], [[3, 3]]]
73 ],
74 [
75 [[[0, 0], [0, 1]], [[1, 0], [1, 1]]]
76 ],
77 [
78 [[[0, 0], [1, 0], [2, 0]], [[1, 1]], [[1, 2]]],
79 [[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[2, 2]]],
80 [[[1, 0]], [[1, 1]], [[0, 2], [1, 2], [2, 2]]],
81 [[[0, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
82 ],
83 [
84 [[[0, 1], [1, 1], [2, 1]], [[0, 2]], [[2, 2]]],
85 [[[1, 0], [2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
86 [[[0, 1], [2, 1]], [[0, 2], [1, 2], [2, 2]]],
87 [[[1, 0], [2, 0]], [[1, 1]], [[1, 2], [2, 2]]]
88 ],
89 [
90 [[[0, 0], [1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
91 [[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
92 ],
93 ],
94 ColorEnum: [[0, 0], [-28, 0], [-56, 0], [-84, 0], [-112, 0], [-140, 0], [-168, 0], [0, 0], [-28, 0], [-56, 0]], //颜色的枚举,对应BlocksEnum
95 CreateGameMap: function() { //创建游戏map
96 for (var i = 0; i < this.VerticalNum; i++) {
97 this.GameMap.push([]);
98 for (var j = 0; j < this.HorizontalNum; j++) {
99 this.GameMap[i][j] = {};
100 this.GameMap[i][j][Sys.TypeEnum.blocks] = null;
101 }
102 }
103 },
104 GetBlocks: function() { //获取GameMap里的方块集
105 for (var i = 0; i < this.BlocksObj.length; i++) {
106 if (this.BlocksObj[i].isInGameMap) {
107 return this.BlocksObj[i];
108 }
109 }
110 return null;
111 },
112 AllowBlocksMove: function() { //是否允许方块集移动
113 var blocksItem = this.GetBlocks();
114 var itemPosArray = this._getBlocksItemPosArray(blocksItem, false);
115 return this.NoBlocksInthePlace(itemPosArray, blocksItem) && this.CheckBoundary(itemPosArray, blocksItem);
116 },
117 GetMaxAndMinItemPosArray: function(itemPosArray) { //获取最大最小方块位置集合
118 itemPosArray.ItemPosXArray.sorts();
119 itemPosArray.ItemPosYArray.sorts();
120 return { maxItemPosX: itemPosArray.ItemPosXArray[itemPosArray.ItemPosXArray.length - 1], maxItemPosY: itemPosArray.ItemPosYArray[itemPosArray.ItemPosYArray.length - 1], minItemPosX: itemPosArray.ItemPosXArray[0], minItemPosY: itemPosArray.ItemPosYArray[0] };
121 },
122 NoBlocksInthePlace: function(itemPosArray, blocksItem) { //检测游戏方格中是否已经有方块
123 return this._isOverMapChild(itemPosArray, blocksItem) ? false : true;
124 },
125 CheckBoundary: function(itemPosArray, blocksItem) { //是否到达边界了
126 var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray);
127 var isNotToBoundary = false;
128 switch (blocksItem.currentDirectionEnum) {
129 case this.DirectionEnum.left:
130 isNotToBoundary = (maxAndMinItemPosArray.minItemPosX > 0)
131 break;
132 case this.DirectionEnum.right:
133 isNotToBoundary = (maxAndMinItemPosArray.maxItemPosX < this.HorizontalNum - 1)
134 break;
135 case this.DirectionEnum.down:
136 isNotToBoundary = (maxAndMinItemPosArray.maxItemPosY < this.VerticalNum - 1);
137 break;
138 }
139 return isNotToBoundary;
140 },
141 _isOverMapChild: function(itemPosArray, blocksItem) { //检测是否会覆盖某个游戏方格中的元素
142 var isOverMapChild = false;
143 for (var i = 0; i < itemPosArray.ItemPosYArray.length; i++) {
144 var itemX = itemPosArray.ItemPosXArray[i];
145 var itemY = itemPosArray.ItemPosYArray[i];
146 if (blocksItem.currentDirectionEnum == this.DirectionEnum.left) {
147 itemX--;
148 }
149 else if (blocksItem.currentDirectionEnum == this.DirectionEnum.right) {
150 itemX++;
151 }
152 else if (blocksItem.currentDirectionEnum == this.DirectionEnum.down) {
153 itemY++;
154 }
155 if (this.GameMap[itemY] && this.GameMap[itemY][itemX] && this.GameMap[itemY][itemX][this.TypeEnum.blocks] != null) {
156 isOverMapChild = true;
157 break;
158 }
159 }
160 return isOverMapChild;
161 },
162 _getBlocksItemPosArray: function(blocksItem, isRelative) { //获取方块集的位置集合,isRelative=true获取的是方块相对方块集map的位置,否则是方块相对游戏map的位置
163 var itemPosXArray = [];
164 var itemPosYArray = [];
165 for (var i = 0; i < blocksItem.blocks.length; i++) {
166 if (isRelative) {
167 itemPosXArray.push(blocksItem.blocks[i].x);
168 itemPosYArray.push(blocksItem.blocks[i].y);
169 }
170 else {
171 itemPosXArray.push(blocksItem.blocks[i].x + blocksItem.x);
172 itemPosYArray.push(blocksItem.blocks[i].y + blocksItem.y);
173 }
174 }
175 return { ItemPosXArray: itemPosXArray, ItemPosYArray: itemPosYArray };
176 },
177 GetBlocksInitPos: function(blocks) { //获取方块的初始位置
178 var blocksItem = null;
179 if (!blocks)
180 blocksItem = this.GetBlocks();
181 else
182 blocksItem = blocks;
183 var itemPosArray = this._getBlocksItemPosArray(blocksItem, true);
184 itemPosArray.ItemPosXArray = itemPosArray.ItemPosXArray.filter();
185 itemPosArray.ItemPosYArray = itemPosArray.ItemPosYArray.filter();
186 var childsNumX = itemPosArray.ItemPosXArray.length;
187 var childsNumY = itemPosArray.ItemPosYArray.length;
188 var maxAndMinItemPosArray = this.GetMaxAndMinItemPosArray(itemPosArray);
189 if (blocks) //获取方块集在预览map中的初始位置
190 return { x: (this.SmallGridNum - childsNumX - 1) / 2 + 0.5 - maxAndMinItemPosArray.minItemPosX, y: (this.SmallGridNum - childsNumY - 1) / 2 + 0.5 - maxAndMinItemPosArray.minItemPosY };
191 else //获取方块集在游戏map中的初始位置
192 return { x: parseInt((this.HorizontalNum - childsNumX - 1) / 2) + 1 - maxAndMinItemPosArray.minItemPosX, y: -(childsNumY + maxAndMinItemPosArray.minItemPosY) };
193 },
194 GetNextActiviteBrocks: function() { //获取下一个活动的方块集
195 for (var i = 0; i < this.BlocksObj.length; i++) {
196 if (this.BlocksObj[i].isInGameMap) {
197 this.BlocksObj.removeAt(i);
198 }
199 }
200 this.BlocksObj[0].isInGameMap = true;
201 var itemPos = this.GetBlocksInitPos();
202 this.BlocksObj[0].x = itemPos.x;
203 this.BlocksObj[0].y = itemPos.y;
204 this.BlocksObj[0].AddToMap(false, false);
205 this.CreateBlocks();
206 },
207 PlayGame: function() { //启动游戏
208 this.IsPlay = true;
209 this.NaturalMove();
210 if (!this.IsFirstPlay) {
211 return;
212 }
213 this.GetNextActiviteBrocks();
214 },
215 AddToGameMapGrid: function() { //加入到游戏map网格中
216 var blocks = this.GetBlocks();
217 blocks.UseGrid(this.GameMap, blocks);
218 },
219 GetScore: function() { //分数处理
220 var rowIndexArray = [];
221 for (var i = 0; i < this.VerticalNum; i++) { //获取满行的行数
222 var entireRow = true;
223 for (var j = 0; j < this.HorizontalNum; j++) {
224 if (this.GameMap[i][j][this.TypeEnum.blocks] == null) {
225 entireRow = false;
226 break;
227 }
228 }
229 if (entireRow)
230 rowIndexArray.push(i);
231 }
232 if (rowIndexArray.length > 0) {
233 this._FreeMapGrid(rowIndexArray);
234 document.getElementById("score").innerText = this.ScoreStrategy[rowIndexArray.length - 1] + parseInt(document.getElementById("score").innerText);
235 this.CheckTheLevel();
236 }
237 },
238 CheckTheLevel: function() { //检测是否进入下一个级别
239 var currentScore = parseInt(document.getElementById("score").innerText);
240 var speedList = document.getElementById("speed");
241 var currentLevel = parseInt(speedList.options[speedList.selectedIndex].text) - 1;
242 var levelScore = this.LevelScores[currentLevel];
243 if (currentScore >= levelScore) {
244 if (currentLevel < this.LevelScores.length) {
245 var element = document.getElementById("gameOver");
246 element.innerText = "恭喜你通过第" + (speedList.selectedIndex + 1) + "关";
247 element.style.display = "block";
248 this.PauseGame();
249 document.getElementById("btnStart").disabled = true;
250 document.getElementById("speed").disabled = true;
251 this._goToNextLevel.delay(3000);
252 }
253 else {
254 this._finishAllTheLevel(element);
255 }
256 }
257 },
258 _goToNextLevel: function() { //进入下一个级别,速度加快
259 Sys.IsPlay = true;
260 document.getElementById("btnStart").disabled = false;
261 var speedList = document.getElementById("speed");
262 speedList.disabled = false;
263 speedList.options[speedList.selectedIndex + 1].selected = true;
264 Sys.CurrentSpeed = Sys.Speeds[speedList.selectedIndex + 1];
265 Sys.NaturalMove();
266 document.getElementById("gameOver").style.display = "none";
267 },
268 _finishAllTheLevel: function() { //完成所有的游戏级别
269 this.PauseGame();
270 },
271 _FreeMapGrid: function(rowIndexArray) { //从游戏map中释放满行的网格
272 var gameMap = this.GameMap;
273 var startIndex = rowIndexArray[0];
274 var len = rowIndexArray.length;
275 var maxIndex = startIndex + len - 1;
276 for (var i = startIndex; i <= maxIndex; i++) {
277 for (var j = 0; j < this.HorizontalNum; j++) {
278 if (gameMap[i][j][this.TypeEnum.blocks] != null) {
279 document.getElementById("map").removeChild(gameMap[i][j][this.TypeEnum.blocks].domElement);
280 gameMap[i][j][this.TypeEnum.blocks] = null;
281 }
282 }
283 }
284 this.ResetMapGrid(rowIndexArray);
285 },
286 ResetMapGrid: function(rowIndexArray) { //重置游戏网格
287 var gameMap = this.GameMap;
288 var maxIndex = rowIndexArray[0];
289 var len = rowIndexArray.length;
290 for (var i = maxIndex - 1; i >= 0; i--) {
291 for (var j = 0; j < this.HorizontalNum; j++) {
292 if (gameMap[i][j][this.TypeEnum.blocks] != null) {
293 this._resetMapElement(gameMap[i][j][this.TypeEnum.blocks].domElement, len);
294 gameMap[i + len][j][this.TypeEnum.blocks] = gameMap[i][j][this.TypeEnum.blocks];
295 gameMap[i][j][this.TypeEnum.blocks] = null;
296 }
297 }
298 }
299 },
300 _resetMapElement: function(element, len) { //重置dom元素,比如共有满行两行,则之上的元素均需下降两个
301 element.style.top = (parseInt(element.style.top) + 28 * len) + "px";
302 },
303 InitSpeed: function() { //初始化游戏级别
304 var speedList = document.getElementById("speed");
305 if (speedList.options.length == 0) {
306 for (var i = 0; i < this.Speeds.length; i++) {
307 var varItem = new Option(i + 1, this.Speeds[i]);
308 speedList.options.add(varItem);
309 }
310 }
311 this.SetSpeedSelected();
312 },
313 SetSpeedSelected: function() { //选中级别
314 var speedList = document.getElementById("speed");
315 for (var i = 0; i < speedList.options.length; i++) {
316 if (speedList.options[i].value == this.CurrentSpeed) {
317 speedList.options[i].selected = true;
318 break;
319 }
320 }
321 },
322 GameOver: function() { //游戏结束
323 this.IsGameOver = true;
324 this.PauseGame();
325 var element = document.getElementById("gameOver");
326 element.innerText = "Game Over!";
327 element.style.display = "block";
328 document.getElementById("btnStart").value = "try again";
329 },
330 PauseGame: function() { //暂停游戏
331 this.IsPlay = false;
332 clearInterval(this.Timer);
333 },
334 CreateBlocks: function() { //创建方块集
335 var currentNum = this.BlocksEnum.length.getRandom();
336 var blocks = new Blocks(0, 0, this.BlocksStateNum[currentNum], currentNum, this.ColorEnum[currentNum]);
337 blocks.Init();
338 if (this.BlocksObj.length == 3)
339 Sys.BlocksObj.pop();
340 Sys.BlocksObj.push(blocks);
341 },
342 NaturalMove: function() { //自然下落
343 this.Timer = setInterval("Moving()", Sys.CurrentSpeed);
344 }
345 }
346 function Base() { } //定义base类
347 Base.prototype.AddToMap = function(isAddToPreviewMap, isMoving) { //添加方块集到map中
348 for (var i = 0; i < this.blocks.length; i++) {
349 var element = null;
350 if (!this.isInGameMap) { //如果方块集是在预览map中
351 element = document.createElement("DIV");
352 document.getElementById("PreviewMap").appendChild(element);
353 this.blocksElement.push(element);
354 this.blocks[i].domElement = element;
355 }
356 else
357 element = this.blocksElement[i];
358 if (!isAddToPreviewMap && !isMoving) //由预览map移动到游戏map时
359 document.getElementById("map").appendChild(element);
360 element.style.position = "absolute";
361 element.style.left = ((this.x + this.blocks[i].x) * 28) + "px"; //设置元素所在的map的位置
362 element.style.top = ((this.y + this.blocks[i].y) * 28) + "px";
363 element.style.backgroundPositionX = this.color[0];
364 element.style.backgroundPositionY = this.color[1];
365 }
366 }
367 Base.prototype.UseGrid = function(map, blocksItem) { //方块集加入到游戏map中
368 for (var i = 0; i < blocksItem.blocks.length; i++) {
369 var itemX = blocksItem.x + blocksItem.blocks[i].x;
370 var itemY = blocksItem.y + blocksItem.blocks[i].y;
371 if (blocksItem.y < 0) {
372 Sys.GameOver();
373 return;
374 }
375 map[itemY][itemX] = {};
376 map[itemY][itemX][this.type] = blocksItem.blocks[i];
377 }
378 }
379 function Block(x, y) { //定义方块结构体
380 this.x = x;
381 this.y = y;
382 this.type = Sys.TypeEnum.block;
383 this.domElement = null;
384 }
385 function Blocks(x, y, state, blocksEnum, colorEnum) { //方块集类
386 this.x = x;
387 this.y = y;
388 this.state = state;
389 this.blocksEnum = blocksEnum; //方块类型(比如L,I,田字形,Z等)
390 this.color = colorEnum;
391 this.type = Sys.TypeEnum.blocks; //废弃属性
392 this.blocks = []; //方块集下的方块的集合
393 this.blocksElement = []; //方块集下的方块的对应dom元素集
394 this.currentState = 0; //当前的状态,比如L有四种类型的变形
395 this.isInGameMap = false; //是否在游戏map中
396 this.currentDirectionEnum = Sys.DirectionEnum.down; //默认方向向下
397 }
398 Blocks.prototype = new Base(); //继承base类
399 Blocks.prototype.Init = function() {//初始化blocks
400 var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum];
401 this.currentState = Sys.BlocksStateNum[this.blocksEnum].getRandom(); //随机获取方块集的状态
402 var blocksPos = blocksPoses[this.currentState]; //获取方块集的map
403 for (var i = 0; i < blocksPos.length; i++) {
404 for (var j = 0; j < blocksPos[i].length; j++) {
405 var block = new Block(blocksPos[i][j][0], blocksPos[i][j][1]);
406 this.blocks.push(block);
407 }
408 }
409 var itemPos = Sys.GetBlocksInitPos(this); //获取初始位置,也就是说在预览map中的位置
410 this.x = itemPos.x;
411 this.y = itemPos.y;
412 this.AddToMap(true, false); //加入到预览map中
413 }
414 Blocks.prototype.ChangeShape = function() {//方块变换形状
415 var gameMap = Sys.GameMap;
416 var allowChangeShape = true;
417 var blocksPoses = Sys.BlocksShapeMaps[this.blocksEnum];
418 var num = Sys.BlocksStateNum[this.blocksEnum];
419 var currentState1 = -1;
420 this.currentState == num - 1 ? currentState1 = 0 : currentState1 = this.currentState + 1;
421 var blocksPos = blocksPoses[currentState1];
422 var k = 0;
423 for (var i = 0; i < blocksPos.length; i++) { //主要是检测方块集的下一个变形是否合理
424 for (var j = 0; j < blocksPos[i].length; j++) {
425 var block = this.blocks[k];
426 var itemX = this.x + blocksPos[i][j][0];
427 var itemY = this.y + blocksPos[i][j][1];
428 if ((itemX > Sys.HorizontalNum - 1) || (itemX < 0) || (itemY > Sys.VerticalNum - 1) || itemY >= 0 && gameMap[itemY][itemX] != null && gameMap[itemY][itemX][Sys.TypeEnum.blocks] != null) {
429 allowChangeShape = false;
430 break;
431 }
432 k++;
433 }
434 }
435 if (allowChangeShape)//如果允许变形
436 {
437 this.currentState == num - 1 ? this.currentState = 0 : this.currentState++; //设置下一个变形的状态
438 k = 0;
439 for (var i = 0; i < blocksPos.length; i++) {
440 for (var j = 0; j < blocksPos[i].length; j++) {
441 var block = this.blocks[k];
442 block.x = blocksPos[i][j][0];
443 block.y = blocksPos[i][j][1];
444 k++;
445 }
446 }
447 this.AddToMap(false, true); //变形后加入到游戏map中
448 }
449 }
450 Blocks.prototype.BlocksMoveDown = function(isMoving) { //方块集下落
451 this.currentDirectionEnum = Sys.DirectionEnum.down;
452 if (!Sys.AllowBlocksMove()) { //如果不允许移动
453 Sys.AddToGameMapGrid(); //固定方块集在游戏map中的位置
454 Sys.GetScore(); //得分处理
455 Sys.GetNextActiviteBrocks(); //获取下一个方块集
456 }
457 else { //下落一格
458 this.y++;
459 this.AddToMap(false, isMoving);
460 }
461 }
462 Number.prototype.getRandom = function() {//获取0至number之间的随机数
463 var num = this;
464 var i = this + 1;
465 while (i >= num) {
466 i = Math.round(Math.random() * 10);
467 }
468 return i;
469 }
470 Array.prototype.sorts = function() { return this.sort(compare); } //数组排序,按照升序排序
471 function compare(a, b) { return a - b; } //定义排序规则
472 Array.prototype.removeAt = function(dx) { //清除指定索引的数组元素
473 if (isNaN(dx) || dx > this.length) { return false; }
474 for (var i = 0, n = 0; i < this.length; i++) {
475 if (this[i] != this[dx])
476 this[n++] = this[i];
477 }
478 this.length -= 1;
479 }
480 Array.prototype.filter = function() { //清除数组中的重复值
481 var arr = [];
482 for (var i = 0; i < this.length; i++) {
483 if (!arr.contains(this[i]))
484 arr.push(this[i]);
485 }
486 return arr;
487 }
488 Array.prototype.contains = function(item) { //检测数组是否包含某元素
489 for (var i = 0; i < this.length; i++) {
490 if (this[i] == item)
491 return true;
492 }
493 return false;
494 }
495 Function.prototype.delay = function(time) { var timer = setTimeout(this, time); } //函数延迟time毫秒执行
496 window.onload = InitGame;
497 function InitGame() {//初始化游戏
498 Sys = new sys();
499 Sys.BlocksObj = [];
500 Sys.InitSpeed(); //初始化游戏速度
501 Sys.CreateGameMap(); //创建游戏map
502 Sys.CreateBlocks(); //创建方块集
503 }
504 function GameStart(element) {
505 if (element.value == "start") { //开始游戏
506 element.value = "pause";
507 Sys.PlayGame();
508 Sys.IsFirstPlay = false;
509 }
510 else if (element.value == "pause") { //暂停游戏
511 element.value = "start"
512 Sys.PauseGame();
513 }
514 else { //游戏结束后重新开始
515 window.location.reload();
516 }
517 }
518 function Moving() {//移动
519 Sys.GetBlocks().BlocksMoveDown(false);
520 }
521 function ChangeSpeed(e) {//切换级别
522 var speedlist = document.getElementById("speed");
523 Sys.CurrentSpeed = speedlist.options[speedlist.selectedIndex].value;
524 if (!Sys.IsGameOver) {
525 clearInterval(Sys.Timer);
526 this.NaturalMove();
527 }
528 }
529 function keyDown(e) { //按键操作
530 if (Sys.IsGameOver || !Sys.IsPlay) return;
531 var blocks = Sys.GetBlocks();
532 if (e.keyCode == 37) { //向左
533 blocks.currentDirectionEnum = Sys.DirectionEnum.left;
534 if (Sys.AllowBlocksMove())
535 blocks.x--;
536 if (blocks.x != 0)
537 blocks.AddToMap(false, true);
538 }
539 else if (e.keyCode == 38) { //向上
540 blocks.currentDirectionEnum = Sys.DirectionEnum.up;
541 blocks.ChangeShape();
542 }
543 else if (e.keyCode == 39) { //向右
544 blocks.currentDirectionEnum = Sys.DirectionEnum.right;
545 var oldX = blocks.x;
546 if (Sys.AllowBlocksMove())
547 blocks.x++;
548 if (blocks.x != oldX)
549 blocks.AddToMap(false, true);
550 }
551 else if (e.keyCode == 40) //向下
552 {
553 blocks.currentDirectionEnum = Sys.DirectionEnum.down;
554 blocks.BlocksMoveDown(true);
555 }
556 }
557 </script>
558 <style type="text/css">
559 body
560 {
561 background-color:#ffffff;
562 overflow:hidden;
563 font-size:14px;
564 }
565 .gameZone
566 {
567 position:absolute;
568 left:0px;
569 top:0px;
570 width:100%;
571 height:550px;
572 background-Color:white;
573 }
574 .mask
575 {
576 position:absolute;
577 left:100px;
578 top:0px;
579 width:300px;
580 height:20px;
581 background-color:White;
582 border:solid 0px;
583 z-index:5;
584 }
585 .map
586 {
587 position:absolute;
588 left:100px;
589 top:20px;
590 width:280px;
591 height:504px;
592 background-image:url(images/tetris_grid.gif);
593 border:solid 3px green;
594 }
595 .gameOver
596 {
597 position:absolute;
598 left:100px;
599 top:20px;
600 width:280px;
601 height:504px;
602 font-weight:800;
603 font-size:xx-large;
604 color:Red;
605 text-align:center;
606 border:solid 3px;
607 line-height:420px;
608 display:none;
609 filter: Alpha(Opacity=80);
610 background-color:pink;
611 }
612 .map div
613 {
614 BACKGROUND-IMAGE: url(images/tetris.gif);
615 WIDTH: 28px;
616 BACKGROUND-REPEAT: no-repeat;
617 POSITION: absolute;
618 HEIGHT: 28px
619 }
620 .PreviewMap
621 {
622 position:absolute;
623 left:400px;
624 top:20px;
625 width:168px;
626 height:168px;
627 background-color:pink;
628 border:solid 2px green;
629 }
630 .PreviewMap div
631 {
632 BACKGROUND-IMAGE: url(images/tetris.gif);
633 WIDTH: 28px;
634 BACKGROUND-REPEAT: no-repeat;
635 POSITION: absolute;
636 HEIGHT: 28px
637 }
638 .start
639 {
640 position:absolute;
641 left:400px;
642 top:240px;
643 width:168px;
644 height:40px;
645 }
646 .scoreSpeed
647 {
648 position:absolute;
649 left:400px;
650 top:200px;
651 width:190px;
652 height:40px;
653 }
654 .score
655 {
656 color:pink;
657 font-weight:bold;
658 width:20px;
659 height:20px;
660 background-color:blue;
661 padding-left:10px;
662 padding-right:10px;
663 font-size:medium;
664 }
665 .speed
666 {
667 color:pink;
668 font-weight:bold;
669 width:20px;
670 height:20px;
671 background-color:blue;
672 padding-left:5px;
673 padding-right:5px;
674 font-size:medium;
675 }
676 .copyright
677 {
678 position:absolute;
679 left:400px;
680 top:280px;
681 word-break:break-all;
682 width:160px;
683 height:225px;
684 border:solid 2px green;
685 padding:5px;
686 }
687 </style>
688 </head>
689 <body onkeydown="keyDown(event)">
690 <div class="gameZone">
691 <div id="mask" class="mask"></div>
692 <div id="map" class="map"></div>
693 <div id="gameOver" class="gameOver"></div>
694 <div id="PreviewMap" class="PreviewMap"></div>
695 <div id="scoreSpeed" class="scoreSpeed">得分:<span id="score" class="score">0</span></div>
696 <div id="start" class="start">
697 <input type="button" id="btnStart" value="start" onclick="GameStart(this);" />
698 级别:<select id="speed" onchange="ChangeSpeed();"></select>
699 </div>
700 <div id="copyright" class="copyright">
701 <b><center>版权所有</center></b><br />
702 此俄罗斯方块由高山流水开发,欢迎各位使用,
703 如有bug或者好的意见,请给我留言,谢谢支持!
704 另如需转载,请注明出处!<br />
705 <font color=red><b>顺便,宣传一下我的MVC qq群:45660795,欢迎加入!</b></font>
706 <br /><br />
707 作者:<a href="http://www.cnblogs.com/JackFeng/" target="_blank">高山流水</a><br />
708 QQ:21243468
709 </div>
710 </div>
711 </body>
712 </html>