[代码] [JavaScript]代码
1 | 只实现了基本功能。 |
2 |
3 | isColorful=false;
//方块是否有颜色 |
4 | 把这里的变量设置为true值,将可以显示为简单的彩色版 |
5 |
6 | 该文档已经做了改善,修复了下标越界的错误和修改了一些奇怪的设定和代码,而且加入了计分和级别的功能。 |
[文件] Tetris.html
001 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
002 | <html
xmlns="http://www.w3.org/1999/xhtml"> |
003 | <head> |
004 | <meta
http-equiv="Content-Type"
content="text/html; charset=gb2312"
/> |
005 | <title>Hello World</title> |
006 | <script
type="text/javascript"> |
007 | /******************************************/ |
008 | function $(id){ |
009 | return document.getElementById(id); |
010 | } |
011 | function myalert(msg){ |
012 | $("myalertbox").value=msg; |
013 | } |
014 | /******************************************/ |
015 | //分数和级别统计 |
016 | var SCORE_LEVEL_INC=80; //升价所需分数值 |
017 | var score=0, //分数 |
018 | level=0, //级数 |
019 | interval_unit=25, //随级数递增的时间间隔增量 |
020 | interval_base=300; //时间间隔基量 |
021 | ////////////////////// |
022 |
023 |
024 | var widthDot=20, //方块格宽度 |
025 | heightDot=20, //方块格高度 |
026 | widthContainer=240, //游戏面板宽度 |
027 | heightContainer=420, //游戏面板高度 |
028 | rows=0, //游戏面板中容纳的方格行数 |
029 | cols=0, //列数 |
030 | widthGraph=0; //方块宽度 |
031 |
032 | var colors="#ffffff,#cccccc,#ff0000,#00ff00,#0000ff,#ffff00,#ff00ff,#00ffff,#666666"; //颜色 |
033 |
034 | //方块的坐标值 |
035 | var cur_top=0, |
036 | cur_left=0; |
037 |
038 | var eles=null, //方格数组 |
039 | graph=null; //方块对象 |
040 |
041 | var control_interval=50, |
042 | main_interval=200; //主时间实例(线程)的时间触发间隔,即方块的下降速度 |
043 | //时间实例 |
044 | var main_t=0, //主时间实例 |
045 | control_t=0; //控制方块操作功能的时间实例 |
046 |
047 | var isPause=false, //暂停标识 |
048 | isColorful=false; //方块是否有颜色 |
049 |
050 | //监控的事件 |
051 | document.onkeydown=keyDown; |
052 | document.onkeyup=keyUp; |
053 | window.onload=init; |
054 |
055 | function init(){ |
056 | //计算并赋值变量,用以保存 |
057 | rows=heightContainer/heightDot; |
058 | cols=widthContainer/widthDot; |
059 | eles=$("MainDiv").getElementsByTagName("DIV"); |
060 | |
061 | initPanel(); |
062 | start(); |
063 | } |
064 | function start(){ |
065 | graph=exportGraph(parseInt(Math.random()*7)); |
066 | //获取方块的宽度,并保存到全局变量 |
067 | widthGraph=0; |
068 | for(var i=0;i<graph.length;i++){ |
069 | if(widthGraph<graph[i].length)widthGraph=graph[i].length; |
070 | } |
071 | //////////////// |
072 | cur_top=0-graph.length; |
073 | cur_left=parseInt((cols-widthGraph)/2); |
074 |
075 | startDiamonds(); |
076 | } |
077 | //启动线程触发方块运行 |
078 | function startDiamonds(){ |
079 | if(main_t==0){ |
080 | main_t=setInterval(function(){ |
081 | doDownShift(); |
082 | },main_interval); |
083 | } |
084 | } |
085 | //停止方块运行监控线程 |
086 | function stopDiamonds(){ |
087 | clearInterval(main_t); |
088 | main_t=0; |
089 | } |
090 | //下移 |
091 | function doDownShift(){ |
092 | if(graph==null)return; |
093 | if(isTouchBottom()){ //判断是否不能再继续下降 |
094 | stopDiamonds(); |
095 | //把方块降落地点的所有方格设置起fixed属性,代表该方格已被占用 |
096 | for(var
i=0;i<graph.length;i++){ |
097 | for(var
j=0;j<graph[i].length;j++){ |
098 | //if(graph[i][j]==1)eles[(cur_top+i)*cols+(cur_left+j)].setAttribute("fixed",1); |
099 | if(graph[i][j]!=0)eles[(cur_top+i)*cols+(cur_left+j)].setAttribute("fixed",graph[i][j]); |
100 | } |
101 | } |
102 | //消行处理 |
103 | clearRow(); |
104 | start(); |
105 | return; |
106 | } |
107 | cur_top++; |
108 | repaint(); |
109 | refreshGraph(); |
110 | } |
111 | //左移 |
112 | function doLeftShift(){ |
113 | if(graph==null)return; |
114 | if(cur_left<=0)return; |
115 | var
fixed=0; |
116 | for(var
i=0;i<graph.length;i++){ |
117 | if(cur_top+i<0)continue; |
118 | for(var
j=0;j<graph[i].length;j++){ //左边数起 |
119 | if(graph[i][j]!=0&&(cur_top+i)*cols+cur_left+j<rows*cols){ //判断是否实心方块和防止下标越界 |
120 | fixed=eles[(cur_top+i)*cols+(cur_left+j-1)].getAttribute("fixed"); |
121 | if(fixed!=0)return; //判断靠左的一点是否被置点 |
122 | break; //只需要判断方块当前行中最左端的一个有颜色的点 |
123 | } |
124 | } |
125 | } |
126 | cur_left--; |
127 | repaint(); |
128 | refreshGraph();
|
129 | } |
130 | //右移 |
131 | function doRightShift(){ |
132 | if(graph==null)return; |
133 | if(cur_left>=cols-widthGraph)return; |
134 | var fixed=0; |
135 | for(var i=0;i<graph.length;i++){ |
136 | if(cur_top+i<0)continue; |
137 | for(var
j=graph[i].length-1;j>=0;j--){ //右边数起 |
138 | if(graph[i][j]!=0&&(cur_top+i)*cols+cur_left+j<rows*cols){ //判断是否实心方格和防止下标访问越界 |
139 | fixed=eles[(cur_top+i)*cols+(cur_left+j+1)].getAttribute("fixed"); |
140 | if(fixed!=0)return; //判断靠右的一点是否被置点 |
141 | break; //只需要判断方块当前行中的最右端的一个有颜色的点 |
142 | } |
143 | } |
144 | } |
145 | cur_left++; |
146 | repaint(); |
147 | refreshGraph(); |
148 | } |
149 | //改变方块方向 |
150 | function doRedirection(){ |
151 | var
row=graph.length; |
152 | var
col=widthGraph; |
153 | var
temp=new
Array(col); |
154 | for(var
i=0;i<temp.length;i++)temp[i]=new Array(); |
155 | for(var
i=0;i<col;i++) |
156 | for(var
j=0;j<row;j++) |
157 | temp[i][j]=graph[row-j-1][i]; |
158 | //给方块重新定位 |
159 | var
incHeight=col-row; |
160 | var
incWidth=row-col; |
161 | //判断当前的空间是否可以让方块改变方向 |
162 | var
tempWidth=row; |
163 | var
temp_cur_top=cur_top-parseInt(incHeight/2); |
164 | var
temp_cur_left=cur_left-parseInt(incWidth/2); |
165 | if(temp_cur_top>rows-temp.length)return; |
166 | if(temp_cur_left<0)return; |
167 | if(temp_cur_left>(cols-tempWidth))return; |
168 | //判断是否存在足够方块旋转的空间大小 |
169 | var nMax=row>col?row:col; |
170 | for(var i=0;i<nMax;i++){ |
171 | for(var
j=0;j<nMax;j++){ |
172 | fixed=eles[(cur_top+i)*cols+(cur_left+j)].getAttribute("fixed"); |
173 | if(fixed!=0)return; |
174 | } |
175 | }
|
176 | //为全局对象赋值 |
177 | cur_top=temp_cur_top; |
178 | cur_left=temp_cur_left; |
179 | widthGraph=tempWidth; //方块宽度已经改变,必须重新赋值 |
180 | graph=temp; |
181 | |
182 | repaint(); |
183 | refreshGraph(); |
184 | } |
185 | //加速 |
186 | function doAccelerate(){ |
187 | if(isTouchBottom())return; |
188 | cur_top++; |
189 | repaint(); |
190 | refreshGraph(); |
191 | } |
192 | //刷新,重绘游戏面板 |
193 | function repaint(){ |
194 | var
fixed=0; |
195 | var
cur=null; |
196 | for(var
i=0;i<rows;i++){ |
197 | for(var
j=0;j<cols;j++){ |
198 | cur=eles[i*cols+j]; |
199 | fixed=cur.getAttribute("fixed"); |
200 | cur.style.backgroundColor=getRandomColor(fixed);
|
201 | } |
202 | } |
203 | } |
204 | //刷新,重绘方块对象 |
205 | function refreshGraph(){ |
206 | if(graph==null)return; |
207 | for(var
i=0;i<graph.length;i++){ |
208 | if(cur_top+i<0)continue; |
209 | for(var
j=0;j<graph[i].length;j++){ |
210 | if(graph[i][j]!=0){ //实心方格 |
211 | if((cur_top+i)*cols+cur_left+j<rows*cols){ //防止下标访问越界 |
212 | eles[(cur_top+i)*cols+(cur_left+j)].style.backgroundColor=getRandomColor(graph[i][j]); |
213 | } |
214 | } |
215 | } |
216 | } |
217 | } |
218 | //是否到底部 |
219 | function isTouchBottom(){ |
220 | if(graph==null)return; |
221 | if(cur_top<0)return false; //首先判断cur_top是否超出正确的下标值,不然程序无法运行 |
222 | if(cur_top>=rows-graph.length)return true; |
223 | var fixed=0; |
224 | for(var i=graph.length-1;i>=0;i--){ //为效率考虑,从方块底部开始扫描 |
225 | for(var j=0;j<graph[i].length;j++){ |
226 | //判断是否为实心方格和防止下标访问越界 |
227 | if(graph[i][j]!=0&&(cur_top+i+1)*cols+cur_left+j<rows*cols){ |
228 | //判断当前点的下面一个点的属性fixed是否不为0(白色),非0代表有颜色 |
229 | fixed=eles[(cur_top+i+1)*cols+cur_left+j].getAttribute("fixed"); |
230 | if(fixed!=0) return true; |
231 | } |
232 | } |
233 | } |
234 | return false; |
235 | } |
236 | //消行处理 |
237 | function clearRow(){ |
238 | var
isFilled=false; //填充标识 |
239 | var
count=0; //消行次数 |
240 | for(var
i=rows-1;i>=0;i--){ |
241 | isFilled=true; |
242 | for(var j=0;j<cols;j++){ |
243 | if(eles[i*cols+j].getAttribute("fixed")=="0"){ |
244 | isFilled=false; |
245 | break; |
246 | } |
247 | } |
248 | if(isFilled){ |
249 | for(var
j=0;j<cols;j++)eles[i*cols+j].setAttribute("fixed",0); |
250 | repaint(); |
251 | var
fixed=0; |
252 | //所有方块下移 |
253 | for(var
k=i-1;k>=0;k--){ //因为当前的i为被消除的一行的下标,所以应该设置k=i-1 |
254 | for(var j=0;j<cols;j++){ |
255 | fixed=eles[k*cols+j].getAttribute("fixed"); |
256 | eles[(k+1)*cols+j].setAttribute("fixed",fixed); |
257 | } |
258 | } |
259 | i=i+1; //注意这里因为所有方块已经下移, |
260 | //所以必须重新修改i值让外部的循环再次扫描最近被撤销的一行
|
261 | count++; |
262 | } |
263 | } |
264 | //最高级别为9级,所以分数极限为(9+1)*SCORE_LEVEL_INC-1 |
265 | if(score>=10*SCORE_LEVEL_INC-1)return; |
266 |
267 | //加分规则:消除行数,1行加10分,2行加15分,3行加20分,4行加30分 |
268 | switch(count){ |
269 | case 1: |
270 | score+=10; |
271 | break; |
272 | case 2: |
273 | score+=15; |
274 | break; |
275 | case 3: |
276 | score+=20; |
277 | break; |
278 | case 4: |
279 | score+=30; |
280 | break; |
281 | } |
282 | var temp_level=parseInt(score/SCORE_LEVEL_INC); |
283 | if(temp_level>level){ |
284 | //更新显示内容 |
285 | level=temp_level; |
286 | $("level_span").innerHTML=level; |
287 | //撤销当前时间实例,然后重设 |
288 | main_interval=interval_base-level*interval_unit; |
289 | stopDiamonds(); |
290 | startDiamonds(); |
291 | } |
292 | $("score_span").innerHTML=score; |
293 | } |
294 | function exportGraph(num){ |
295 | var oGraph=null; |
296 | switch(num){ |
297 | case 0: //水平条 |
298 | oGraph=[[1,1,1,1]]; |
299 | break; |
300 | case 1: //三角 |
301 | oGraph=[[0,1,0],[1,1,1]]; //graph[0]为顶,graph[1]为底 |
302 | break; |
303 | case 2: //左横节 |
304 | oGraph=[[1,0,0],[1,1,1]]; |
305 | break; |
306 | case 3: //右横节 |
307 | oGraph=[[0,0,1],[1,1,1]]; |
308 | break; |
309 | case 4: //左闪电 |
310 | oGraph=[[1,1,0],[0,1,1]]; |
311 | break; |
312 | case 5: //右闪电 |
313 | oGraph=[[0,1,1],[1,1,0]]; |
314 | break; |
315 | case 6: //石头 |
316 | oGraph=[[1,1],[1,1]]; |
317 | break; |
318 | } |
319 | //附上颜色 |
320 | if(isColorful){ |
321 | var idx=parseInt(Math.random()*(colors.split(",").length)); |
322 | idx=idx==0?1:idx; //idx不应该为0 |
323 | var temp=new Array(oGraph.length); //只能创建临时变量temp,并重新赋值给oGraph,不然会出现奇怪的错误,原因不明 |
324 | for(var i=0;i<temp.length;i++){ |
325 | temp[i]=new Array(); |
326 | for(var
j=0;j<oGraph[i].length;j++){ |
327 | if(oGraph[i][j]!=0) temp[i].push(idx); |
328 | else temp[i].push(0); |
329 | } |
330 | } |
331 | oGraph=temp; |
332 | } |
333 | return oGraph; |
334 | } |
335 | //根据调色板的值,获取特定颜色值 |
336 | function getRandomColor(idx){ |
337 | var
arrayColors=colors.split(","); |
338 | return arrayColors[idx]; |
339 | } |
340 | function keyDown(e){ |
341 | if(control_t!=0)return; |
342 | e=e||window.event; |
343 | switch(e.keyCode){ |
344 | case 37: //左移 |
345 | if(!isPause)control_t=setInterval(doLeftShift,control_interval); |
346 | //doLeftShift(); |
347 | break; |
348 | case 39: //右移 |
349 | if(!isPause)control_t=setInterval(doRightShift,control_interval); |
350 | //doRightShift(); |
351 | break; |
352 | case 38: //改变方向 |
353 | //control_t=setInterval(doRedirection,control_interval); |
354 | if(!isPause)doRedirection(); |
355 | break; |
356 | case 40: //加速 |
357 | if(!isPause)control_t=setInterval(doAccelerate,control_interval); |
358 | //doAccelerate(); |
359 | break; |
360 | case 13: //暂停或取消暂停,回车符 |
361 | if(isPause){ |
362 | startDiamonds(); |
363 | }else{ |
364 | stopDiamonds(); |
365 | } |
366 | isPause=!isPause; |
367 | break; |
368 | } |
369 | return; |
370 | } |
371 | function keyUp(){ |
372 | if(control_t==0)return; |
373 | clearInterval(control_t); |
374 | control_t=0; |
375 | } |
376 | //初始化游戏,创建方格,生成游戏面板, |
377 | function initPanel(){ |
378 | var container=$("MainDiv"); |
379 | container.style.width=widthContainer+"px"; |
380 | container.style.height=heightContainer+"px"; |
381 |
382 | var
row=heightContainer/heightDot; |
383 | var
col=widthContainer/widthDot; |
384 | var
strHtml="<table width='"+widthContainer+"'
height='"+heightContainer+"'
border='0'
cellpadding='0'
cellspacing='0'>"; |
385 | |
386 | for(var i=0;i<row;i++){
|
387 | strHtml+="<tr>"; |
388 | for(var j=0;j<col;j++){ |
389 | strHtml+="<td
style='width:"+widthDot+"; height:"+heightDot+"; text-align:center;'><div
class='dot'
fixed='0'
style='border:solid 1px #000000; background-color:#ffffff; width:"+(widthDot-4)+"px; height:"+(heightDot-4)+"px;'></div></td>"; |
390 | } |
391 | strHtml+="</tr>"; |
392 | } |
393 | strHtml+="</table>"; |
394 | container.innerHTML=strHtml; |
395 | } |
396 | </script> |
397 | </head> |
398 | <body> |
399 | <input
type="text"
id="myalertbox"
style="width:500px; height:15px; border:solid 1px #000000; position:absolute; top:10px; left:10px;"
/> |
400 | <div
style="margin:0 auto; margin-top:30px; width:250px; font-size:12px;"> |
401 | <div
style="width:100px; float:left; margin-left:10px; text-align:center;">分数:<span
id="score_span"
style="color:#ff0000">0</span></div> |
402 | <div
style="width:100px; float:right; margin-right:10px; text-align:center;">级别:<span
id="level_span"
style="color:#ff0000;">0</span></div> |
403 | </div> |
404 | <div
style="margin:0 auto; margin-top:10px; width:250px; border:solid 1px #000000; font-size:12px; clear:both;">方向键:<br
/>上:变形 下:加速 左:左移 右:右移<br
/>回车符:暂停</div> |
405 | <div
id="MainDiv"
style="margin:0 auto; margin-top:10px; padding:0px; border:solid 1px #000000;"></div> |
406 | </body> |
407 | </html> |
俄罗斯方块游戏实现
本文介绍了一个使用HTML和JavaScript实现的俄罗斯方块游戏。游戏具备基本功能,包括方块的移动、旋转、加速及计分等。代码经过优化,修复了部分错误并增加了级别和分数功能。
8408

被折叠的 条评论
为什么被折叠?



