要做俄罗斯方块,先来对我们的游戏进行一个规划。首先,游戏要包含一个俄罗斯方块的游戏区,是一个10X20的矩形区域,然后旁边有一个区域提示下一个方块是什么,同时还有计时和计分。计分规则如下:同时消灭一行得10分,两行得30分,三行得60分,四行得100分。我们通过上下左右和空格来控制方块的移动,上表示旋转,下表示下移一行,空格表示直接下落,左右就不说了。
首先,我想先把界面做出来,看看效果才有心思做逻辑呀!
html:
<!DOCTYPE html> <head> <meta charset="utf-8" /> <title>俄罗斯方块</title> <link href="style.css" rel="stylesheet" /> </head> <body> <div class="game" id="game"></div> <div class="next" id="next"></div> <div class="info"> <div>已用时:<span id="time">0</span>s</div> <div>已得分:<span id="score">0</span>分</div> </div> <script src="script.js"></script> </body>check the result
css:
.game {
width: 200px;
height: 400px;
background: #F2FAFF;
border-left: 1px solid blue;
border-right: 1px solid blue;
border-bottom: 1px solid blue;
position: absolute;
top: 10px;
left: 10px;
}
.next {
width: 80px;
height: 80px;
position: absolute;
top: 10px;
left: 250px;
border: 1px solid blue;
background: #F2FAFF;
}
.info {
position: absolute;
top: 100px;
left: 250px;
}
.none, .current, .done {
width: 20px;
height: 20px;
position: absolute;
box-sizing: border-box;
}
.none {
background: #F2FAFF;
}
.current {
background: pink;
border: 1px solid red;
}
.done {
background: gray;
border: 1px solid black;
}
javascript:
var nextData = [
[2, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]; //代表竖棍
var gameData = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 1, 2, 2, 2, 1, 1, 0, 0],
[0, 1, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0]
];
var gameDivs = [];
var nextDivs = [];
var initGame = function() {
for(var i=0; i<gameData.length; i++) {
var gameDiv = [];
for(var j=0; j<gameData[0].length; j++) {
var newNode = document.createElement('div');
newNode.className = 'none';
newNode.style.top = (i*20) + 'px';
newNode.style.left = (j*20) + 'px';
document.getElementById('game').appendChild(newNode);
gameDiv.push(newNode);
}
gameDivs.push(gameDiv);
}
}
var initNext = function() {
for(var i=0; i<nextData.length; i++) {
var nextDiv = [];
for(var j=0; j<nextData[0].length; j++) {
var newNode = document.createElement('div');
newNode.className = 'none';
newNode.style.top = (i*20) + 'px';
newNode.style.left = (j*20) + 'px';
document.getElementById('next').appendChild(newNode);
nextDiv.push(newNode);
}
nextDivs.push(nextDiv);
}
}
var refreshGame = function() {
for(var i=0; i<gameData.length; i++) {
for(var j=0; j<gameData[0].length; j++) {
if(gameData[i][j] == 0) {
gameDivs[i][j].className = 'none';
} else if(gameData[i][j] == 1) {
gameDivs[i][j].className = 'done';
} else if(gameData[i][j] == 2) {
gameDivs[i][j].className = 'current';
}
}
}
}
var refreshNext = function() {
for(var i=0; i<nextData.length; i++) {
for(var j=0; j<nextData[0].length; j++) {
if(nextData[i][j] == 0) {
nextDivs[i][j].className = 'none';
} else if(nextData[i][j] == 1) {
nextDivs[i][j].className = 'done';
} else if(nextData[i][j] == 2) {
nextDivs[i][j].className = 'current';
}
}
}
}
initGame();
refreshGame();
initNext();
refreshNext();
思路其实比较明确,我们希望操作数组,然后把数组渲染成界面,也就是可视化,我们用到的是refreshxxx函数,这样,在每次改变数组的时候,调用refresh函数就ok了。Div还是用绝对布局,三种样式表示了div块的三种状态。但是看看代码,其实冗余度挺大的,我们来改写一下,做点面向对象该做的事情吧!
html:
<!DOCTYPE html> <head> <meta charset="utf-8" /> <title>俄罗斯方块</title> <link href="style.css" rel="stylesheet" /> </head> <body> <div class="game" id="game"></div> <div class="next" id="next"></div> <div class="info"> <div>已用时:<span id="time">0</span>s</div> <div>已得分:<span id="score">0</span>分</div> </div> <script src="game.js"></script> <script src="script.js"></script> </body>check the result
css:
.game {
width: 200px;
height: 400px;
background: #F2FAFF;
border-left: 1px solid blue;
border-right: 1px solid blue;
border-bottom: 1px solid blue;
position: absolute;
top: 10px;
left: 10px;
}
.next {
width: 80px;
height: 80px;
position: absolute;
top: 10px;
left: 250px;
border: 1px solid blue;
background: #F2FAFF;
}
.info {
position: absolute;
top: 100px;
left: 250px;
}
.none, .current, .done {
width: 20px;
height: 20px;
position: absolute;
box-sizing: border-box;
}
.none {
background: #F2FAFF;
}
.current {
background: pink;
border: 1px solid red;
}
.done {
background: gray;
border: 1px solid black;
}
script.js:
var game = new SquareGame(); game.init(); game.start();
game.js:
var SquareGame = function() {
// 下落速度
var INTERVAL = 200;
// 所有的方块
var squares = [
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0]
],
[
[0, 0, 2, 0],
[0, 0, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
];
// 下一个方块
var nextData = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
// 现在的方块
var curData = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
// 游戏矩阵
var gameData = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
// 游戏divs
var gameDivs = [];
// 下一个的divs
var nextDivs = [];
// 定时器
var timer;
// 原点
var origin = {
x: 0,
y: 0
}
// 初始化div
var initDivs = function(container, data, divs) {
for(var i=0; i<data.length; i++) {
var div = [];
for(var j=0; j<data[0].length; j++) {
var newNode = document.createElement('div');
newNode.className = 'none';
newNode.style.top = (i*20) + 'px';
newNode.style.left = (j*20) + 'px';
container.appendChild(newNode);
div.push(newNode);
}
divs.push(div);
}
}
// 更新div
var refreshDivs = function(data, divs) {
for(var i=0; i<data.length; i++) {
for(var j=0; j<data[0].length; j++) {
if(data[i][j] == 0) {
divs[i][j].className = 'none';
} else if(data[i][j] == 1) {
divs[i][j].className = 'done';
} else if(data[i][j] == 2) {
divs[i][j].className = 'current';
}
}
}
}
// 检测点是否合法
var check = function(pos, x, y) {
if(pos.x + x < 0) {
return false;
}
if(pos.x + x >= gameData.length) {
return false;
}
if(pos.y + y < 0) {
return false;
}
if(pos.y + y >= gameData[0].length) {
return false;
}
if(gameData[pos.x + x][pos.y + y] == 1) {
return false;
}
return true;
}
// 检测数据是否可行
var isValid = function(pos, data) {
for(var i=0; i<data.length; i++) {
for(var j=0; j<data[0].length; j++) {
if(data[i][j] != 0) {
if(!check(pos, i, j)) {
return false;
}
}
}
}
return true;
}
// 清除数据
var clearData = function() {
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
if(check(origin, i, j)) {
gameData[origin.x + i][origin.y + j] = 0;
}
}
}
}
// 设置数据
var setData = function() {
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
if(check(origin, i, j)) {
gameData[origin.x + i][origin.y + j] = curData[i][j];
}
}
}
}
// 能否下降
var canDown = function() {
var test = {};
test.x = origin.x + 1;
test.y = origin.y;
return isValid(test, curData);
}
// 下降
var down = function() {
clearData();
origin.x = origin.x + 1;
setData();
refreshDivs(gameData, gameDivs);
}
// 能否左移
var canLeft = function() {
var test = {};
test.x = origin.x;
test.y = origin.y - 1;
return isValid(test, curData);
}
// 左移
var left = function() {
clearData();
origin.y = origin.y - 1;
setData();
refreshDivs(gameData, gameDivs);
}
// 能否右移
var canRight = function() {
var test = {};
test.x = origin.x;
test.y = origin.y + 1;
return isValid(test, curData);
}
// 右移
var right = function() {
clearData();
origin.y = origin.y + 1;
setData();
refreshDivs(gameData, gameDivs);
}
// 能否旋转
var canRotate = function() {
var testData = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
testData[i][j] = curData[3-j][i];
}
}
return isValid(origin, testData);
}
// 旋转
var rotate = function() {
clearData();
var testData = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
testData[i][j] = curData[3-j][i];
}
}
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
curData[i][j] = testData[i][j];
}
}
setData();
refreshDivs(gameData, gameDivs);
}
// 消除行
var checkClear = function() {
for(var i=gameData.length-1; i>=0; i--) {
var clear = true;
for(var j=0; j<gameData[0].length; j++) {
if(gameData[i][j] != 1) {
clear = false;
}
}
if(clear) {
for(var m=i; m>0; m--) {
for(var n=0; n<gameData[0].length; n++) {
gameData[m][n] = gameData[m-1][n];
}
}
for(var n=0; n<gameData[0].length; n++) {
gameData[0][n] = 0;
}
i++;
}
}
}
// 检查游戏结束
var checkGameOver = function() {
var gameOver = false;
for(var i=0; i<gameData[0].length; i++) {
if(gameData[0][i] == 1) {
gameOver = true;
break;
}
}
if(gameOver) {
document.onkeydown = null;
clearInterval(timer);
window.alert('game over!');
}
}
// 方块移到底部,给它固定
var fixed = function() {
for(var i=0; i<curData.length; i++) {
for(var j=0; j<curData[0].length; j++) {
if(check(origin, i, j)) {
if(gameData[origin.x + i][origin.y + j] == 2) {
gameData[origin.x + i][origin.y + j] = 1;
}
}
}
}
checkClear();
checkGameOver();
}
// 生成下一个方块
var randomNext = function() {
var ran = Math.random();
var index = Math.ceil(ran * 7) - 1;
for(var i=0; i<squares[index].length; i++) {
for(var j=0; j<squares[index][0].length; j++) {
nextData[i][j] = squares[index][i][j];
}
}
refreshDivs(nextData, nextDivs);
}
// 使用下一个方块
var performNext = function() {
origin.x = 0;
origin.y = 3;
for(var i=0; i<nextData.length; i++) {
for(var j=0; j<nextData[0].length; j++) {
curData[i][j] = nextData[i][j];
}
}
setData();
randomNext();
}
// 绑定按键事件
var bindKeyEvent = function() {
document.onkeydown = function(e) {
if (e.keyCode == 38) { //up
if(canRotate()) {
rotate();
}
} else if (e.keyCode == 39) { //right
if(canRight()) {
right();
}
} else if (e.keyCode == 40) { //down
move();
} else if (e.keyCode == 37) { //left
if(canLeft()) {
left();
}
} else if (e.keyCode == 32) { //space
while(!move());
}
};
}
// 向下移动
var move = function() {
if(canDown()) {
down();
return false;
} else {
fixed();
performNext();
return true;
}
}
// 初始化
this.init = function() {
initDivs(document.getElementById('game'), gameData, gameDivs);
initDivs(document.getElementById('next'), nextData, nextDivs);
refreshDivs(gameData, gameDivs);
refreshDivs(nextData, nextDivs);
randomNext();
}
// 开始
this.start = function() {
performNext();
bindKeyEvent();
timer = setInterval(move, INTERVAL);
}
// 结束
this.stop = function() {
clearInterval(timer);
document.onkeydown = null;
}
}
在这个例子中,我们基本实现了游戏的逻辑。首先,我们抽象出来一个SquareGame类,放在game.js里面,所以在script.js中,代码出奇地少。但是在game.js中代码却十分复杂。首先,我们将7种方块定义成一个数组,然后,我们把initDiv和refreshDiv的公共逻辑抽取出来,接着,我们实现了方块的旋转、左移、右移和下移,最后在每次方块下移的时候,我们判断能否下移,不能的话,方块就固定住了,然后检查是否可以消行,是否游戏结束。
但是这个版本还有一些bug,比如旋转的时候,行为比较奇怪,计时和计分没有,没有较好的操作提示等等,而且game.js的代码比较多。对于旋转,其实没必要用算法,我们自己定义一些旋转的矩阵就行了,而且也应该分出一个方块类,然后让七种方块去继承这个类,将代码结构调整如下:
html:
<!DOCTYPE html> <head> <meta charset="utf-8" /> <title>俄罗斯方块</title> <link href="style.css" rel="stylesheet" /> </head> <body> <div class="game" id="game"></div> <div class="next" id="next"></div> <div class="info"> <div>已用时:<span id="time">0</span>s</div> <div>已得分:<span id="score">0</span>分</div> <div>请用方向键和空格进行操作:上->旋转,左->左移,右->右移,下->下移,空格->旋转</div> <div id="gameover"></div> </div> <script src="square.js"></script> <script src="square1.js"></script> <script src="square2.js"></script> <script src="square3.js"></script> <script src="square4.js"></script> <script src="square5.js"></script> <script src="square6.js"></script> <script src="square7.js"></script> <script src="game.js"></script> <script src="script.js"></script> </body>check the result
css:
.game {
width: 200px;
height: 400px;
background: #F2FAFF;
border-left: 1px solid blue;
border-right: 1px solid blue;
border-bottom: 1px solid blue;
position: absolute;
top: 10px;
left: 10px;
}
.next {
width: 80px;
height: 80px;
position: absolute;
top: 10px;
left: 250px;
border: 1px solid blue;
background: #F2FAFF;
}
.info {
position: absolute;
top: 100px;
left: 250px;
}
.none, .current, .done {
width: 20px;
height: 20px;
position: absolute;
box-sizing: border-box;
}
.none {
background: #F2FAFF;
}
.current {
background: pink;
border: 1px solid red;
}
.done {
background: gray;
border: 1px solid black;
}
script.js:
var game = new SquareGame(); game.init(); game.start();
game.js:
var SquareGame = function() {
// 下落速度
var INTERVAL = 200;
// 下一个方块
var next = Square.prototype.make();
// 现在的方块
var cur;
// 游戏矩阵
var gameData = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
// 游戏divs
var gameDivs = [];
// 下一个的divs
var nextDivs = [];
// 定时器
var timer;
// 分数
var score = 0;
// 时间
var time = 0;
// 时间计数
var timeCount = 0;
// 初始化div
var initDivs = function(container, data, divs) {
for(var i=0; i<data.length; i++) {
var div = [];
for(var j=0; j<data[0].length; j++) {
var newNode = document.createElement('div');
newNode.className = 'none';
newNode.style.top = (i*20) + 'px';
newNode.style.left = (j*20) + 'px';
container.appendChild(newNode);
div.push(newNode);
}
divs.push(div);
}
}
// 更新div
var refreshDivs = function(data, divs) {
for(var i=0; i<data.length; i++) {
for(var j=0; j<data[0].length; j++) {
if(data[i][j] == 0) {
divs[i][j].className = 'none';
} else if(data[i][j] == 1) {
divs[i][j].className = 'done';
} else if(data[i][j] == 2) {
divs[i][j].className = 'current';
}
}
}
}
// 检测点是否合法
var check = function(pos, x, y) {
if(pos.x + x < 0) {
return false;
}
if(pos.x + x >= gameData.length) {
return false;
}
if(pos.y + y < 0) {
return false;
}
if(pos.y + y >= gameData[0].length) {
return false;
}
if(gameData[pos.x + x][pos.y + y] == 1) {
return false;
}
return true;
}
// 检测数据是否可行
var isValid = function(pos, data) {
for(var i=0; i<data.length; i++) {
for(var j=0; j<data[0].length; j++) {
if(data[i][j] != 0) {
if(!check(pos, i, j)) {
return false;
}
}
}
}
return true;
}
// 清除数据
var clearData = function() {
for(var i=0; i<cur.data.length; i++) {
for(var j=0; j<cur.data[0].length; j++) {
if(check(cur.origin, i, j)) {
gameData[cur.origin.x + i][cur.origin.y + j] = 0;
}
}
}
}
// 设置数据
var setData = function() {
for(var i=0; i<cur.data.length; i++) {
for(var j=0; j<cur.data[0].length; j++) {
if(check(cur.origin, i, j)) {
gameData[cur.origin.x + i][cur.origin.y + j] = cur.data[i][j];
}
}
}
}
// 下降
var down = function() {
if(cur.canDown(isValid)) {
clearData();
cur.down();
setData();
refreshDivs(gameData, gameDivs);
return true;
} else {
return false;
}
}
// 左移
var left = function() {
if(cur.canLeft(isValid)) {
clearData();
cur.left();
setData();
refreshDivs(gameData, gameDivs);
}
}
// 右移
var right = function() {
if(cur.canRight(isValid)) {
clearData();
cur.right();
setData();
refreshDivs(gameData, gameDivs);
}
}
// 旋转
var rotate = function() {
var a = cur.canRotate(isValid);
if(a) {
clearData();
cur.rotate();
setData();
refreshDivs(gameData, gameDivs);
}
}
// 加分
var addScore = function(line) {
var s = 0;
if(line == 1) {
s = 10;
} else if(line == 2) {
s = 30;
} else if(line == 3) {
s = 60;
} else if(line == 4) {
s = 100;
}
score = score + s;
document.getElementById('score').innerHTML = score;
}
// 消除行
var checkClear = function() {
var line = 0;
for(var i=gameData.length-1; i>=0; i--) {
var clear = true;
for(var j=0; j<gameData[0].length; j++) {
if(gameData[i][j] != 1) {
clear = false;
break;
}
}
if(clear) {
line++;
for(var m=i; m>0; m--) {
for(var n=0; n<gameData[0].length; n++) {
gameData[m][n] = gameData[m-1][n];
}
}
for(var n=0; n<gameData[0].length; n++) {
gameData[0][n] = 0;
}
i++;
}
}
addScore(line);
}
// 检查游戏结束
var checkGameOver = function() {
var gameOver = false;
for(var i=0; i<gameData[0].length; i++) {
if(gameData[0][i] == 1) {
gameOver = true;
break;
}
}
if(gameOver) {
document.onkeydown = null;
clearInterval(timer);
window.alert('game over!');
}
}
// 方块移到底部,给它固定
var fixed = function() {
for(var i=0; i<cur.data.length; i++) {
for(var j=0; j<cur.data[0].length; j++) {
if(check(cur.origin, i, j)) {
if(gameData[cur.origin.x + i][cur.origin.y + j] == 2) {
gameData[cur.origin.x + i][cur.origin.y + j] = 1;
}
}
}
}
checkClear();
checkGameOver();
}
// 生成下一个方块
var randomNext = function() {
next = next.make();
}
// 使用下一个方块
var performNext = function() {
cur = next;
setData();
randomNext();
refreshDivs(next.data, nextDivs);
}
// 绑定按键事件
var bindKeyEvent = function() {
document.onkeydown = function(e) {
if (e.keyCode == 38) { //up
rotate();
} else if (e.keyCode == 39) { //right
right();
} else if (e.keyCode == 40) { //down
move();
} else if (e.keyCode == 37) { //left
left();
} else if (e.keyCode == 32) { //space
while(!move());
}
};
}
// 向下移动
var move = function() {
timeCount = timeCount + 1;
if(timeCount == 5) {
timeCount = 0;
time = time + 1;
document.getElementById('time').innerHTML = time;
}
if(down()) {
return false;
} else {
fixed();
performNext();
refreshDivs(gameData, gameDivs);
return true;
}
}
// 初始化,这里是入口
this.init = function() {
initDivs(document.getElementById('game'), gameData, gameDivs);
initDivs(document.getElementById('next'), next.data, nextDivs);
refreshDivs(gameData, gameDivs);
refreshDivs(next.data, nextDivs);
}
// 开始
this.start = function() {
performNext();
bindKeyEvent();
timer = setInterval(move, INTERVAL);
}
// 结束
this.stop = function() {
clearInterval(timer);
document.onkeydown = null;
}
}
square.js:
var Square = function() {
// 方块数据
this.data = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
// 原点
this.origin = {
x: 0,
y: 0
}
// 旋转方向
this.dir = 0;
}
Square.prototype.make = function() {
var index = Math.ceil(Math.random() * 7) - 1;
var s;
if(index == 0) {
s = new Square1();
} else if(index == 1) {
s = new Square2();
} else if(index == 2) {
s = new Square3();
} else if(index == 3) {
s = new Square4();
} else if(index == 4) {
s = new Square5();
} else if(index == 5) {
s = new Square6();
} else if(index == 6) {
s = new Square7();
}
s.origin.x = 0;
s.origin.y = 3;
var dir = Math.ceil(Math.random() * 4) - 1;
s.rotate(dir);
return s;
}
Square.prototype.canRotate = function(isValid) {
var d = this.dir + 1;
if(d == 4) {
d = 0;
}
var t = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
for(var i=0; i<this.data.length; i++) {
for(var j=0; j<this.data[0].length; j++) {
t[i][j] = this.rotates[d][i][j];
}
}
return isValid(this.origin, t);
}
Square.prototype.rotate = function(num) {
if(!num) num = 1;
this.dir = this.dir + num;
while(this.dir >= 4) {
this.dir = this.dir - 4;
}
for(var i=0; i<this.data.length; i++) {
for(var j=0; j<this.data[0].length; j++) {
this.data[i][j] = this.rotates[this.dir][i][j];
}
}
}
Square.prototype.canDown = function(isValid) {
var test = {};
test.x = this.origin.x + 1;
test.y = this.origin.y;
return isValid(test, this.data);
}
Square.prototype.down = function() {
this.origin.x = this.origin.x + 1;
}
Square.prototype.canLeft = function(isValid) {
var test = {};
test.x = this.origin.x;
test.y = this.origin.y - 1;
return isValid(test, this.data);
}
Square.prototype.left = function() {
this.origin.y = this.origin.y - 1;
}
Square.prototype.canRight = function(isValid) {
var test = {};
test.x = this.origin.x;
test.y = this.origin.y + 1;
return isValid(test, this.data);
}
Square.prototype.right = function() {
this.origin.y = this.origin.y + 1;
}
square1.js:
var Square1 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 0, 0, 0],
[2, 2, 2, 2],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 0, 0, 0],
[2, 2, 2, 2],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
];
}
Square1.prototype = Square.prototype;
square2.js:
var Square2 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[0, 2, 2, 2],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[0, 2, 2, 0],
[0, 0, 2, 0],
[0, 0, 2, 0]
],
[
[0, 0, 0, 0],
[0, 0, 2, 0],
[2, 2, 2, 0],
[0, 0, 0, 0]
]
];
}
Square2.prototype = Square.prototype;
square3.js:
var Square3 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 0, 2, 0],
[0, 0, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[0, 2, 0, 0],
[0, 2, 2, 2],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[0, 2, 2, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 0, 0, 0],
[2, 2, 2, 0],
[0, 0, 2, 0],
[0, 0, 0, 0]
]
];
}
Square3.prototype = Square.prototype;
square4.js:
var Square4 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 2, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
];
}
Square4.prototype = Square.prototype;
square5.js:
var Square5 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 2, 0, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[2, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 2, 0],
[0, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
]
];
}
Square5.prototype = Square.prototype;
square6.js:
var Square6 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[0, 2, 2, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
]
];
}
Square6.prototype = Square.prototype;
square7.js:
var Square7 = function() {
this.square = Square;
this.square();
this.rotates = [
[
[2, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[2, 2, 0, 0],
[2, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[2, 2, 0, 0],
[2, 0, 0, 0],
[0, 0, 0, 0]
]
];
}
Square7.prototype = Square.prototype;
在这里,我们完成了所有逻辑,不过文件也变得多起来了,但是结构却清晰很多。首先,我们把方块的所有操作封装在Square中,一些大家都要用的方法封装在prototype中,而每个Square都要使用的属性封装在this中。然后,我们定义了Square1到Square7,从Square继承下来。注意,这里用this.square = Square;this.square();实现了对象继承,用Square1.prototype = Square.prototype;实现了原型链继承,这样,Square的所有东西都继承下来了。而每一个方块特有的东西就是旋转,所以我们为每一个方块定义了旋转矩阵。然后在Game中,我们实现了计分和计时,这部分比较简单,应该很容易看懂。在实现过程中,有一个比较麻烦的地方就是判断数据是否有效,这个操作应该放在Game中还是Square中呢?其实都有点麻烦,因为这个操作涉及到这两个东西,我们这里采用的方法是,将操作放在Square中,然后将Game中的方法作为参数传递过来。个人觉得这样可以比较好地解决这个问题。
在这里,我们最重要的是使用到了js的继承,下面单独拿出来看看。
html:
<!DOCTYPE html> <head> <meta charset="utf-8" /> <title>js的继承</title> </head> <body> <script src="script.js"></script> </body>check the result
script.js:
function Student(name, age) {
this.name = name;
this.age = age;
this.show = function() {
document.write('我是' + this.name + ', 我' + this.age + '岁<br/>');
}
}
function Pupil(name, age) {
this.stu = Student; //这里是关键!
this.stu(name, age);
this.gotoSchool = function() {
document.write('我是小学生,我去上学了<br/>');
}
}
function Middle(name, age) {
this.stu = Student; //这里是关键!
this.stu(name, age);
this.gotoSchool = function() {
document.write('我是中学生,我去上学了<br/>');
}
}
var p = new Pupil('wanmingniu', 10);
p.show();
p.gotoSchool();
var m = new Middle('xinxin', 15);
m.show();
m.gotoSchool();
js中继承最重要的就是this.stu = Student;this.stu(name, age);了,首先,我们定义一个this.stu,让它等于Student这个function,然后调用它,注意,这时候是以this身份去调用它,然后把它里面的属性全都拿过来了,相当于实现了继承。
这个游戏说实话是有一定难度的,大家可以好好理解。
,不要超过100元哦!