<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" > < html > < style > div { display : block ; background-color : black ; position : absolute ; font : 17px Tahoma ; color : #fff ; } <!-- 层 --> span { float : left ; background-color : gray ; width : 12px ; height : 12px ; border : 4px gray outset ; margin : 0 1 1 0 ; overflow : hidden ; } <!-- 游戏区单体方块 --> p { float : left ; width : 8px ; height : 8px ; border : 2px gray outset ; overflow : hidden ; margin : 0 ; padding : 0 } <!-- 预览区单体方块 --> #main { width : 375px ; height : 430px ; display : block ; background-color : gray ; position : relative ; } <!-- 全面版 --> #body { width : 241px ; height : 401px ; left : 15px ; top : 15px ; border : 1px solid #999 ; } <!-- 主游戏区 --> #score { width : 80px ; height : 24px ; left : 270px ; top : 15px ; padding : 4px ; } <!-- 计分区 --> #level { width : 80px ; height : 24px ; left : 270px ; top : 50px ; padding : 4px ; } <!-- 等级区 --> #next { width : 50px ; height : 50px ; left : 270px ; top : 85px ; border : 19px black solid ; } <!-- 预览区 --> #ctrl { width : 80px ; height : 55px ; left : 270px ; top : 360px ; padding : 4px ; text-align : center ; background-color : gray } <!-- 按钮区 --> #ctrl button { width : 80px ; height : 25px ; } <!-- 按钮 --> </ style > < script > // By: X!ao_f QQ:120000512 var G = { fs:[], // 游戏区点阵 fn:[], // 预览区点阵 score: 0 , l:[ ' #9F0 ' , ' #FF3 ' , ' #F33 ' , ' #C0C ' , ' #33C ' , ' #0F3 ' , ' #F93 ' ], // 颜色选择器 v:[ 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0x801 , 0xFFFF ], // 游戏区行点阵 d:[[ 0xCC00 ],[ 0x4444 , 0xF0 ],[ 0x8C40 , 0x6C00 ],[ 0x4C80 , 0xC600 ],[ 0x44C0 , 0x8E00 , 0xC880 , 0xE200 ],[ 0x88C0 , 0xE800 , 0xC440 , 0x2E00 ],[ 0x4E00 , 0x8C80 , 0xE400 , 0x4C40 ]], // 方块造型点阵 init: function (){ var body = document.getElementById( ' body ' ); var next = document.getElementById( ' next ' ); for ( var i = 0 ;i < 240 ;i ++ ){ // 以游戏区单体方块填充点阵 G.fs.push(body.appendChild(document.createElement( " span " ))); } for ( var i = 0 ;i < 16 ;i ++ ){ // 以预览区单体方块填充点阵 G.fn.push(next.appendChild(document.createElement( " p " ))); } G.domScore = document.getElementById( ' score ' ); G.domLevel = document.getElementById( ' level ' ); document.onkeydown = function (e){G.event(e || window.event, 0 )}; // 页面按键事件控制转移到闭包 G.rand(); G.next(); }, timeTesk: function (){ if (G.pause) return ; // 如果暂停按钮按下,则停止该函数运行 if ( ! G.move(G.x, G.y + 1 , G.t)){ // 是否能继续下移 var s = 0 ; for ( var i = 0 ;i < 19 ;i ++ ){ G.v[i] = G.v[i] | G.m[i]; if (G.v[i] == 0xFFF ){ // 根据行点阵判断是否已达到可消除行 for ( var k = i;k > 0 ;k -- ){ G.v[k] = G.v[k - 1 ]; // 消除已完全成行(向下移位) } G.score +=++ s; // 计分 } } G.next(); return false ; } G.draw(); return true ; }, move: function (x,y,t){ // 位移函数 var m = []; for ( var k = 0 ;k < 4 ;k ++ ){ // 因为方块形状建模是4X4点阵.所以循环4次 m[y + k] = (G.d[G.n][t] >> ( 3 - k) * 4 & 0xF ) << Math.max(x, 0 ) >>- Math.min(x, 0 ); // G.d[G.n]为现在使用的方块点阵模型,[t]为第t种旋转模型点阵 // G.d[G.n][t]>>(3-k)*4&0xF为获取单行位移后的点阵 // <<Math.max(x,0)>>-Math.min(x,0)为判断x为左移还是右移 // m[y+k]对应位移点阵行 if (m[y + k] & G.v[y + k]){ // 判断是否位移后点阵与游戏区现有点阵重叠 return false ; } } G.x = x; // 保存当前下落点阵模型坐标 G.y = y; // 保存当前下落点阵模型坐标 G.t = t; // 保存当前下落点阵模型旋转模型 G.m = m; // 保存当前下落点阵模型在类游戏区的位置(镜像位置) G.draw(); return true ; }, rand: function (){ G.n = G._n; // 获取上次生成的随机方块点阵模型 G.t = G._t; // 获取上次生成的随机方块点阵旋转系数 G.c = G._c; // 获取上次生成的随机颜色 G._n = parseInt(Math.random() * G.d.length); // 以模型长度生成随机数 G._t = parseInt(Math.random() * G.d[G._n].length); // 同上 G._c = parseInt(Math.random() * G.l.length); // 同上 }, next: function (){ G.rand(); if (parseInt(G.score / 20)!=G.level){ / / 分数与等级互换的概念...20分一级哦... G.level = parseInt(G.score / 20); / / 不等于当前等级则升级 clearInterval(G.handle); // 清楚上一计时器 G.handle = setInterval( " G.timeTesk() " , 500 / (G.level+1)); / / 按等级设计时器(控制速度的地方哦 ! ) } G.domScore.innerHTML = ' Score: ' + G.score; // 更新分数 G.domLevel.innerHTML = ' Level: ' + G.level; // 更新等级 var i = 0 ; while ( ! (G.d[G.n][G.t] >> i * 4 & 0xF ))i ++ ; // 将i移动到点阵模型有显示的第一行 if ( ! G.move( 3 , i - 3 , G.t)){ // 如果在点阵模型3行内不能进行向上位移(即点阵到游戏区顶部) alert( ' Game over! ' ); // 则Game over并提示 clearInterval(G.handle); // 良好习惯,清除计时器 } }, draw: function (){ for ( var i = 0 ;i < 240 ;i ++ ){ // 遍历游戏区全点阵,每i为全点阵中的1点 if ((G.v[parseInt(i / 12)]>>(11-i%12))&0x1){ // G.v[parseInt(i/12)为该点所在行 // >>(11-i%12))&0x1为整体操作,&运算前将点移后,让&运算能判断点是否显示 G.fs[i].style.visibility = '' ; // 判断为真则显示 } else if ((G.m[parseInt(i / 12)]>>(11-i%12))&0x1){ / / 同上理画下落中的模型镜像点阵 G.fs[i].style.visibility = '' ; G.fs[i].style.borderColor = G.fs[i].style.background = G.l[G.c]; // 上色 } else { G.fs[i].style.visibility = ' hidden ' ; // 判断为假则隐藏 } } for ( var i = 0 ;i < 16 ;i ++ ){ // 同理画预览区点阵 if (G.d[G._n][G._t] >> ( 15 - i) & 0x1 ){ G.fn[i].style.visibility = '' ; G.fn[i].style.borderColor = G.fn[i].style.background = G.l[G._c]; } else { G.fn[i].style.visibility = ' hidden ' ; } } }, event: function (e,t){ switch (e.keyCode){ case 37 : // 左键 G.move(G.x + 1 , G.y, G.t); break ; case 39 : // 右键 G.move(G.x - 1 , G.y, G.t); break ; case 38 : // 上键 G.move(G.x, G.y, (G.t + 1 ) % G.d[G.n].length); break ; case 40 : // 下键 G.timeTesk(); break ; case 32 : // 空格...看了半天才知道原来按空格直接落底... while (G.timeTesk()); } } } </ script > < body onload ='G.init();' > <!-- 网页读取后初始化 --> < div id ='main' > < div id ='body' ></ div > < div id ='score' ></ div > < div id ='level' ></ div > < div id ='next' ></ div > < div id ='ctrl' > < button onclick ="javascript:location.href=location.href;" > New </ button > <!-- 重置等于重新读取网页 --> < button onclick ="javascript:this.innerHTML={'true':'Start','false':'Pause'}[G.pause=!!!G.pause];" > Pause </ button > <!-- 这里运用了一个很好的技巧:!!的用法,可以将为空的数据转成false,避免出现空指针异常 --> </ div > </ div > </ body > </ html >