您的位置:首页 > Web前端 > JavaScript

js版俄罗斯方块(二)

2013-03-27 12:47 337 查看
之前曾发过一个js版的俄罗斯方块,界面比较简单,Bug也不少。抽空重构了一下,加入了javaScript面向对象的知识,修复了一些明显的BUG。

斌斌 (给我写信) 原创博文(http://blog.csdn.net/binbinxyz),转载请注明出处

源码(下载http://download.csdn.net/detail/binbinxyz/5186774)公开如下:

<!DOCTYPE html>
<html>
<head>
<title>俄罗斯方块_经典游戏</title>

<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="keywords" content="游戏, 经典游戏, 俄罗斯方块, tetris">
<meta http-equiv="description" content="斌斌的小游戏:俄罗斯方块">

<link rel="icon" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" />
<link rel="bookmark" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" />

<!--
游戏名称:俄罗斯方块(Tetris)<br/>
游戏版本:1.2<br/>
作者:斌斌<br/>
邮箱:cn.binbin@qq.com<br/>
网站:http://blog.csdn.net/binbinxyz<br/>
-->

<style type="text/css">
<!--
.thinBorderTable{border-collapse:collapse;}
.thinBorderTable th{background:#ffffff;border:solid 1px #000000;}
.thinBorderTable td{background:#ffffff;border:solid 1px #000000;}

.outerBorderTable{border-collapse:collapse;}
.outerBorderTable th{background:#ffffff;}
.outerBorderTable td{background:#ffffff;}
-->
</style>

</head>
<body>
<div id="game" align="center">
<h2>俄罗斯方块  <span style="font-size: 14px">V1.2</span></h2>
<table>
<tr>
<td>
<fieldset>
<legend>俄罗斯方块的<b>基本规则</b></legend>
<table id="info">
<tr valign="top"><td>1、</td><td width='400px'>一个用于摆放小型正方形的平面虚拟矩形,标准大小:宽为10,高为20,以小正方形为单位。</td></tr>
<tr valign="top"><td>2、</td><td width='400px'>一组由4个小型正方形组成的规则图形即方块(Tetromino ),共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名。</td></tr>
<tr valign="top"><td>3、</td><td width='400px'>部分游戏有单格方块,可以穿透固定的方块到达最下层空位。</td></tr>
<tr valign="top"><td>4、</td><td width='400px'>玩家的操作有:以90度为单位旋转方块,以格子为单位左右移动方块,让方块加速落下。</td></tr>
<tr valign="top"><td>5、</td><td width='400px'>方块移到区域最下方或是落到其他方块上会固定在该处,而新的方块从区域上方开始落下。</td></tr>
<tr valign="top"><td>6、</td><td width='400px'>当区域中某一行横向格子全部由方块填满,则该行会消失。</td></tr>
<tr valign="top"><td>7、</td><td width='400px'>当固定的方块堆到区域最上方而无法消除层数时,则游戏结束。</td></tr>
<tr valign="top"><td>8、</td><td width='400px'>游戏会提示下一个要落下的方块。</td></tr>
<tr valign="top"><td>9、</td><td width='400px'>计分标准:下落一个方块1分,一次消一行10分、2行30分、3行60分、4行100分。</td></tr>
</table>
</fieldset>

<fieldset>
<legend>游戏按键提示</legend>
<table style="width: 430px;margin: 0px;padding: 0px">
<tr>
<td><b>↑</b>:旋转方块</td>
<td><b>↓</b>:方块下移</td>
<td><b>←</b>:方块左移</td>
<td><b>→</b>:方块右移</td>
</tr>
<tr>
<td colspan="4">空格键(<b>Spacebar</b>):方块降落(直接下移到位)</td>
</tr>
</table>
</fieldset>
</td>
<td>
<fieldset>
<legend>显示屏幕</legend>
<div id='screen' style="background-color: #aaaaaa;padding: 5px"></div>
</fieldset>
</td>
<td>
<div style="width: 200px;">
<fieldset>
<legend>显示区域</legend>
<table>
<tr><td>当前级别:</td><td><span id='level'>1</span></td></tr>
<tr><td>当前分数:</td><td><span id='score'>0</span></td></tr>
<tr><td>运行时间:</td><td><span id='runTime'>00:00</span></td></tr>
<tr><td colspan='2'><div id="smallScreen"></div></td></tr>
</table>
</fieldset>

<fieldset id="configArea" style="display: none;"><!-- style="display: none;" -->
<legend>配置区域</legend>
<table>
<tr><td>显示屏幕辅助线:</td></tr>
<tr>
<td>
<label><input type='radio' id='lineVisible_1' name='lineVisible' value='Y' checked='checked'/>YES</label>
<label><input type='radio' id='lineVisible_2' name='lineVisible' value='N'/>NO</label>
</td>
</tr>
<tr><td>生成的方块颜色随机:</td></tr>
<tr>
<td>
<label><input type='radio' name='randomTetrominoColor' checked='checked' onclick='setRTColor(true)'/>YES</label>
<label><input type='radio' name='randomTetrominoColor' onclick='setRTColor(false)'/>NO</label>
</td>
</tr>
<tr><td>底部方块的颜色随机:</td></tr>
<tr>
<td>
<label><input type='radio' name='randomBottomColor' checked='checked' onclick='setRBColor(true)'/>YES</label>
<label><input type='radio' name='randomBottomColor' onclick='setRBColor(false)'/>NO</label>
</td>
</tr>
</table>
</fieldset>

<fieldset>
<legend>控制区域</legend>
<input id='btnstart' type='button' value='开始' onclick="start()"/>
<input id='btnpause' type='button' value='暂停' disabled="disabled" onclick='pause()'/>
<input id='btnend' type='button' value='结束' disabled="disabled" onclick='end()'/>
</fieldset>
</div>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var COLUMN = 10; //显示屏幕的宽度(相对列数)
var ROW = 20; //显示屏幕的高度(相对行数)

//定义方块数据数组,存储所有的方块数据
var TETROMINO_DATA = [
[
[[0,0,0,0],[1,1,1,1],  [0,0,0,0], [0,0,0,0]],
[[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]]
],
[
[[1,1,1,0], [1,0,0,0], [0,0,0,0], [0,0,0,0]],
[[1,1,0,0], [0,1,0,0], [0,1,0,0], [0,0,0,0]],
[[0,0,1,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]],
[[0,1,0,0], [0,1,0,0], [0,1,1,0], [0,0,0,0]]
],
[
[[0,0,0,0], [1,1,1,0], [0,1,0,0], [0,0,0,0]],
[[0,1,0,0], [1,1,0,0], [0,1,0,0], [0,0,0,0]],
[[0,1,0,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]],
[[0,1,0,0], [0,1,1,0], [0,1,0,0], [0,0,0,0]]
],
[
[[0,0,0,0], [1,1,1,0], [0,0,1,0], [0,0,0,0]],
[[0,1,0,0], [0,1,0,0], [1,1,0,0], [0,0,0,0]],
[[1,0,0,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]],
[[0,1,1,0], [0,1,0,0], [0,1,0,0], [0,0,0,0]]
],
[
[[0,0,0,0], [1,1,0,0], [0,1,1,0], [0,0,0,0]],
[[0,0,1,0], [0,1,1,0], [0,1,0,0], [0,0,0,0]]
],
[
[[0,1,0,0], [0,1,1,0], [0,0,1,0], [0,0,0,0]],
[[0,0,0,0], [0,1,1,0], [1,1,0,0], [0,0,0,0]]
],
[
[[0,0,0,0], [0,1,1,0], [0,1,1,0], [0,0,0,0]]
]
];
var POSSIBLE_COLORS = [
"aqua", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive",
"purple", "red", "silver", "teal", "yellow"]; //可能使用的颜色库
var DEFAULT_TETROMINO_COLOR = "GRAY"; //生成方块的默认颜色
var DEFAULT_BOTTOM_COLOR = "GRAY"; //底部方块的默认颜色
var DEFAULT_BLOCK_X = 4; //方块出现时左上角默认位置的X坐标

var bottom = []; //底部方块堆(实际是与屏幕大小相同的二维数组)
var bottomColor = []; //底部方块堆中各块的颜色

var level; //当前级别
var period; //各后续任务之间的时间间隔,单位是毫秒
var score; //当前分数
var runTime; //运行时间,单位秒
var block; //显示屏幕中的方块

var block_x; //方块出现时左上角的X坐标
var block_y; //方块出现时左上角的y坐标

var gameStatus; //游戏状态(offDuty|run|pause|end)

var lineVisible; //屏幕辅助线是否可见
var randomTetrominoColor; //生成的方块颜色随机
var randomBottomColor; //底部方块的颜色随机

var taskTimer = function(){}; //计时器,用于循环执行方块下落等事件
var updateTimer = function(){}; //计时器,游戏运行时用于更新游戏运行时间

//定义方块对象的构造方法
function Tetromino(color, index, status){
this.color = color; //方块的颜色
this.index = index;
this.status = status;
this.data = TETROMINO_DATA[index][status];
};
//获取当前方块的高度
Tetromino.prototype.getHeight = function(){
for(var i=3;i>=0;i--)
{
for(j=0;j<4;j++)
{
if(this.data[i][j] == 1)
{
return i + 1;
}
}
}
};
//获取方块对象的下一个状态
Tetromino.prototype.getNextStatus = function(){
this.status ++;
this.status %= TETROMINO_DATA[this.index].length;
return TETROMINO_DATA[this.index][this.status];
};
//alert(new Tetromino("blue", 2, 0).data);

//定义屏幕显示的方块
function Block(x, y, tetromino){
this.x = x; //方块所在4×4矩形的左上角x坐标
this.y = y; //方块所在4×4矩形的左上角y坐标
this.tetromino = tetromino; //方块
};

//方块工厂,用于生成各种方块
var TetrominoFactory = {
tetromino: [], //方块属性
createTetromino: function(){ //生成一个新的方块
var color = DEFAULT_TETROMINO_COLOR; //使用默认颜色初始化
//如果生成的方块颜色随机
if(randomTetrominoColor)
{
var i = Math.floor(Math.random() * POSSIBLE_COLORS.length);
color = POSSIBLE_COLORS[i];
//alert("0:" + color);
}
var index = Math.floor(Math.random() * TETROMINO_DATA.length);
var status = Math.floor(Math.random() * TETROMINO_DATA[index].length);
//alert(color + ":" + index + ":" + status);
this.tetromino = new Tetromino(color, index, status);
return this.getCurrentTetromino();
},
getCurrentTetromino: function(){ //返回当前的方块
return this.tetromino;
}
};
//alert("a:" + TetrominoFactory.createTetromino().data);
//alert("b:" + TetrominoFactory.getCurrentTetromino().data);

//初始化整个页面
function initBody()
{
//初始化控制区域的按钮
//document.getElementById("btnstart").removeAttribute("disabled");
//document.getElementById("btnpause").disabled = "disabled";
//document.getElementById("btnend").disabled = "disabled";
initButtonStatus();

//初始化全局变量
level = 1;
period = 800;
score = 0;
runTime = 0;

block_x = DEFAULT_BLOCK_X;
block_y = -3;

gameStatus = "offDuty";

lineVisible = true;
randomTetrominoColor = true;
randomBottomColor = true;

initScreen();
initSmallScreen();

//初始化底部方块堆及堆中各块的颜色
for(var i=0;i<ROW;i++)
{
bottom[i] = [];
bottomColor[i] = [];
for(var j=0;j<COLUMN;j++)
{
bottom[i][j] = 0;
bottomColor[i][j] = "white";
}
}

drawTetromino2smallScreen(TetrominoFactory.createTetromino());
//next();
}

//初始化显示屏幕
function initScreen()
{
var classStyle = "outerBorderTable";
if(lineVisible)
{
classStyle = "thinBorderTable";
}
//拼接显示屏幕的HTML代码
var screenHtml = "<table id='screenTable' class='" + classStyle + "'>";
for(var i=0;i<ROW;i++)
{
screenHtml += "<tr height='20'>";
for(var j=0;j<COLUMN;j++)
{
screenHtml += "<td width='20'></td>";
}
screenHtml += "</tr>";
}
screenHtml += "</table>";
document.getElementById("screen").innerHTML = screenHtml;
}

//初始化小显示屏幕
function initSmallScreen()
{
//拼接小显示屏幕(用于显示下一个落下的方块)的HTML代码
var smallScreenHtml = "<table class='thinBorderTable'>";
for(var i=0;i<4;i++)
{
smallScreenHtml += "<tr height='15'>";
for(var j=0;j<4;j++)
{
smallScreenHtml += "<td width='15'></td>";
}
smallScreenHtml += "</tr>";
}
smallScreenHtml += "</table>";
document.getElementById("smallScreen").innerHTML = smallScreenHtml;
}

//在小显示屏幕绘制方块(tetromino)
function drawTetromino2smallScreen(tetromino){
//获取小显示屏幕
var smallScreen = document.getElementById("smallScreen");
//获取小显示屏幕中的所有行(即tr标记)
var trs = smallScreen.getElementsByTagName("tr");
for(var i=0;i<trs.length;i++)
{
//获取当前行中的所有单元格
var tds = trs[i].getElementsByTagName("td");
for(var j=0;j<tds.length;j++)
{
if(tetromino.data[i][j] == 1)
{
tds[j].style.backgroundColor = tetromino.color;
}
else
{
tds[j].style.backgroundColor = "white";
}
}
}
}

//将小显示屏幕上的方块移入显示屏幕中,并生成下一个方块显示在小显示屏幕上
function moveTetromino2screen()
{
var tetromino = TetrominoFactory.getCurrentTetromino();
block_y = -1 * tetromino.getHeight();
block = new Block(block_x, block_y, tetromino);
drawTetromino2smallScreen(TetrominoFactory.createTetromino());

score += 1; //
document.getElementById("score").innerHTML = score;
}

//显示屏幕刷新
function reshow(){
var style = "outerBorderTable";
if(document.getElementById("lineVisible_1").checked)
{
style = "thinBorderTable";
}
document.getElementById("screenTable").className=style;

var screenDiv = document.getElementById("screen");
//获取显示屏幕中的所有行(即tr标记)
var trs = screenDiv.getElementsByTagName("tr");
//alert("下落方块的坐标(x,y):" + block.x + "," + block.y);
for(var i=0;i<trs.length;i++)
{
//获取当前行中的所有单元格
var tds = trs[i].getElementsByTagName("td");
for(var j=0;j<tds.length;j++)
{
//如果是底部的方块
if(bottom[i][j] == 1)
{
//alert("2:" + block.tetromino.color);
tds[j].style.backgroundColor = bottomColor[i][j];
}
else
{
tds[j].style.backgroundColor = "white";
}

//下落的方块(当前方块的坐标位于4*4的矩形内部,且矩形内部对应位置的值是1)
if(i>=block.y && i<block.y+4 && j>=block.x && j<block.x+4
&& block.tetromino.data[i-block.y][j-block.x] == 1)
{
//alert("3:" + block.tetromino.color);
tds[j].style.backgroundColor = block.tetromino.color;
}
}
}
}

//旋转
function up(){
//缓存旧数据
var oldData = block.tetromino.data;
block.tetromino.data = block.tetromino.getNextStatus();
if(!isMoveable())
{
block.tetromino.data = oldData;
}
reshow();
}

//下移
function down(){
block.y ++; //向下移动一格
if(!isMoveable())
{
block.y --;
accept();
}
reshow();
}

//快速下移
function quickDown(){
while(true)
{
block.y ++; //向下移动一格
if(!isMoveable())
{
block.y --;
accept();
break;
}
reshow();
}
}

//左移
function left(){
block.x --;
if(!isMoveable())
{
block.x ++;
}
reshow();
}

//右移
function right(){
block.x ++;
if(!isMoveable())
{
block.x --;
}
reshow();
}

//是否可以移动(即此次移动操作是否可行)
function isMoveable()
{
var tetromino = block.tetromino;
for(var i=0;i<tetromino.data.length;i++)
{
for(var j=0;j<tetromino.data[i].length;j++)
{
//左侧出界
if(block.x+j<0 && tetromino.data[i][j] == 1)
{
return false;
}
//右侧出界
if(block.x+j>=COLUMN && tetromino.data[i][j] == 1)
{
return false;
}
//到达底部了
if(block.y+tetromino.getHeight()>ROW)
{
return false;
}
//方块重合
if(block.y+i>=0 && tetromino.data[i][j]==1 && bottom[block.y + i][block.x + j] == 1)
{
return false;
}
}
}
return true;
}

//接收一个block
function accept(){
var tetromino = block.tetromino;
for(var i=0;i<tetromino.data.length;i++)
{
for(var j=0;j<tetromino.data[i].length;j++)
{
if(tetromino.data[i][j] == 1)
{
if(block.y + i < 0)
{
gameOver();
return;
}
bottom[block.y + i][block.x + j] = 1;
if(randomBottomColor)
{
bottomColor[block.y + i][block.x + j] = tetromino.color;
}
else
{
bottomColor[block.y + i][block.x + j] = DEFAULT_BOTTOM_COLOR;
}
}
}
}

var line = disappear();
score += getScore(line); //
changeLevel();
document.getElementById("score").innerHTML = score;

moveTetromino2screen();
}

//消去满行
function disappear()
{
var line = 0; //此次消去行数
var r = block.y + block.tetromino.getHeight() - 1;

//alert(bottom[i].length);
for(var i=0;i<block.tetromino.getHeight();i++,r--)
{
//当前行是否可以消去,默认可消去
var disapperable = true;
for(var j=0;j<bottom[r].length;j++)
{
//alert(r + ":" + j + ":" + bottom[r][j]);
//如果有单元格为空,则当前行不可消去
if(bottom[r][j] == 0)
{
disapperable = false;
break;
}
}
if(disapperable)
{
line ++; //更新消去行数
//消去当前满行
for(var k=r;k>0;k--)
{
for(var j=0;j<bottom[k].length;j++)
{
bottom[k][j] = bottom[k-1][j];
if(bottomColor[k-1][j] != "white")
{
bottomColor[k][j] = bottomColor[k-1][j];
}
}
}
//首行全部为零
for(var j=0;j<bottom[i].length;j++)
{
bottom[0][j] = 0;
}

r ++; //消去一行时,继续判断当前行
reshow();
}
}

return line;
}

//一次消去行数为line时取得的分数
function getScore(line)
{
var score;
switch(line)
{
case 1:
score = 10;
break;
case 2:
score = 30;
break;
case 3:
score = 60;
break;
case 4:
score = 100;
break;
default:
score = 0;
break;
}
return score;
}

//更改级别。第一次升级需要1000分,以后每一次升级所需分数都比上一次多1000分
function changeLevel()
{
var n = sum(level);
if(level == 20) //最高20级
{
return;
}
if(score / 1000 > n)
{
level += 1;

changeSpeed();

//升级后,方块下落的速度需要发生变化,所以清除旧的计时器,再新建一个
clearInterval(taskTimer);
taskTimer = setInterval("down();", period);
document.getElementById("level").innerHTML = level;
}
}

//求1+2+3+...+n的结果
function sum(n)
{
if(n <= 1)
{
return 1;
}

return n + sum(n - 1);
}

//修改方块下落的速度
function changeSpeed()
{
//每升一级,方块下落的时间间隔减少40毫秒
period = 1200 - 40 * level;
}

function gameOver()
{
clearInterval(taskTimer);
clearInterval(updateTimer);
alert("您的得分是:" + score + "!\n游戏结束!");

initBody();
if(confirm("您想重新挑战吗?"))
{
start();
}
}

//更新显示区域的时间
function updateTime()
{
var runTimeLabel = document.getElementById("runTime");
runTime ++;
runTimeLabel.innerHTML = formatTime(runTime);
}

//格式化时间
function formatTime(time)
{
var m = Math.floor(time / 60); //分钟
var s = time % 60; //秒数

var str = "";
if(m < 10)
{
str = "0";
}
str += m + ":";
if(s < 10)
{
str += "0";
}
str += s;
return str;
}

function keyEvent(e) {
if(navigator.appName == "Microsoft Internet Explorer")
{
//var keycode = event.keyCode;
//var realkey = String.fromCharCode(event.keyCode);
//alert("按键码: " + keycode + " 字符: " + realkey);
keyAction(event.keyCode);
}else
{
//alert(e.which);
keyAction(e.which);
}
}

function keyAction(key)
{
switch(key)
{
case 32: //向下快速移动
quickDown();
break;
case 37: //向左移动
left();
break;
case 38: //向上移动
up();
break;
case 39: //向右移动
right();
break;
case 40: //向下移动
down();
break;
}
}

//开始游戏
function start()
{
changeButtonStatus();
if(gameStatus == "offDuty")
{
gameStatus = "run";
moveTetromino2screen();
}
taskTimer = setInterval("down()", period);
updateTimer = setInterval("updateTime()", 1000);
}

//暂停游戏
function pause()
{
var btnpause = document.getElementById("btnpause");
if(btnpause.value=="暂停")
{
btnpause.value = "恢复";
clearInterval(taskTimer);
clearInterval(updateTimer);
}
else{
btnpause.value = "暂停";
taskTimer = setInterval("down();", period);
updateTimer = setInterval("updateTime()", 1000);
}
}

//结束游戏
function end()
{
if(confirm("你确定要结束游戏吗?"))
{
clearInterval(taskTimer);
clearInterval(updateTimer);
initBody();
}
}

//更改按钮状态
function initButtonStatus()
{
document.getElementById("btnstart").disabled = false;
document.getElementById("btnpause").disabled = true;
document.getElementById("btnpause").value = "暂停";
document.getElementById("btnend").disabled = true;

document.getElementsByName("lineVisible")[0].disabled = false;
document.getElementsByName("lineVisible")[0].checked = "checked";
document.getElementsByName("lineVisible")[1].disabled = false;
document.getElementsByName("randomTetrominoColor")[0].disabled = false;
document.getElementsByName("randomTetrominoColor")[1].disabled = false;
document.getElementsByName("randomTetrominoColor")[1].checked = "checked";
document.getElementsByName("randomBottomColor")[0].disabled = false;
document.getElementsByName("randomBottomColor")[1].disabled = false;
document.getElementsByName("randomBottomColor")[1].checked = "checked";

if(document.getElementsByName("randomTetrominoColor")[0].checked)
{
randomTetrominoColor = true;
}
else
{
randomTetrominoColor = false;
}
if(document.getElementsByName("randomBottomColor")[0].checked)
{
randomBottomColor = true;
}
else
{
randomBottomColor = false;
}
}

//初始化按钮状态
function changeButtonStatus()
{
document.getElementById("btnstart").disabled = true;
document.getElementById("btnpause").disabled = false;
document.getElementById("btnend").disabled = false;

document.getElementsByName("lineVisible")[0].disabled = true;
document.getElementsByName("lineVisible")[1].disabled = true;
document.getElementsByName("randomTetrominoColor")[0].disabled = true;
document.getElementsByName("randomTetrominoColor")[1].disabled = true;
document.getElementsByName("randomBottomColor")[0].disabled = true;
document.getElementsByName("randomBottomColor")[1].disabled = true;
}

//设置方块颜色的生成方式(即是否随机)
function setRTColor(flag)
{
randomTetrominoColor = flag;
}

//设置底部方块颜色的生成方式(即是否随机)
function setRBColor(flag)
{
randomBottomColor = flag;
}

//设置是否显示屏幕辅助线
function setLineVisible(flag)
{
lineVisible = flag;
}

initBody();
document.onkeydown = keyEvent;
</script>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: